package webhook

import (
	"crypto/hmac"
	"errors"
	"net/http"
	"strings"

	kapi "k8s.io/kubernetes/pkg/api"
	kerrors "k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/api/unversioned"

	buildapi "github.com/openshift/origin/pkg/build/api"
)

const (
	refPrefix        = "refs/heads/"
	DefaultConfigRef = "master"
)

var (
	ErrSecretMismatch  = errors.New("the provided secret does not match")
	ErrHookNotEnabled  = errors.New("the specified hook is not enabled")
	MethodNotSupported = errors.New("unsupported HTTP method")
)

// Plugin for Webhook verification is dependent on the sending side, it can be
// eg. github, bitbucket or else, so there must be a separate Plugin
// instance for each webhook provider.
type Plugin interface {
	// Method extracts build information and returns:
	// - newly created build object or nil if default is to be created
	// - information whether to trigger the build itself
	// - eventual error.
	Extract(buildCfg *buildapi.BuildConfig, secret, path string, req *http.Request) (*buildapi.SourceRevision, []kapi.EnvVar, bool, error)
}

// GitRefMatches determines if the ref from a webhook event matches a build
// configuration
func GitRefMatches(eventRef, configRef string, buildSource *buildapi.BuildSource) bool {
	if buildSource.Git != nil && len(buildSource.Git.Ref) != 0 {
		configRef = buildSource.Git.Ref
	}

	eventRef = strings.TrimPrefix(eventRef, refPrefix)
	configRef = strings.TrimPrefix(configRef, refPrefix)
	return configRef == eventRef
}

// FindTriggerPolicy retrieves the BuildTrigger of a given type from a build
// configuration
func FindTriggerPolicy(triggerType buildapi.BuildTriggerType, config *buildapi.BuildConfig) (buildTriggers []buildapi.BuildTriggerPolicy, err error) {
	buildTriggers = buildapi.FindTriggerPolicy(triggerType, config)
	if len(buildTriggers) == 0 {
		err = ErrHookNotEnabled
	}
	return buildTriggers, err
}

// ValidateWebHookSecret validates the provided secret against all currently
// defined webhook secrets and if it is valid, returns its information.
func ValidateWebHookSecret(webHookTriggers []buildapi.BuildTriggerPolicy, secret string) (*buildapi.WebHookTrigger, error) {
	for _, trigger := range webHookTriggers {
		if trigger.Type == buildapi.GenericWebHookBuildTriggerType {
			if !hmac.Equal([]byte(trigger.GenericWebHook.Secret), []byte(secret)) {
				continue
			}
			return trigger.GenericWebHook, nil
		}
		if trigger.Type == buildapi.GitHubWebHookBuildTriggerType {
			if !hmac.Equal([]byte(trigger.GitHubWebHook.Secret), []byte(secret)) {
				continue
			}
			return trigger.GitHubWebHook, nil
		}
	}
	return nil, ErrSecretMismatch
}

// NewWarning returns an StatusError object with a http.StatusOK (200) code.
func NewWarning(message string) *kerrors.StatusError {
	return &kerrors.StatusError{ErrStatus: unversioned.Status{
		Status:  unversioned.StatusSuccess,
		Code:    http.StatusOK,
		Message: message,
	}}
}