package github

import (
	"context"
	"fmt"

	"github.com/grafana/grafana-app-sdk/logging"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/validation/field"

	provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
	"github.com/grafana/grafana/apps/provisioning/pkg/repository"
	"github.com/grafana/grafana/apps/provisioning/pkg/repository/git"
	"github.com/grafana/grafana/apps/provisioning/pkg/util"
)

//go:generate mockery --name WebhookURLBuilder --structname MockWebhookURLBuilder --inpackage --filename webhook_builder_mock.go --with-expecter
type WebhookURLBuilder interface {
	WebhookURL(ctx context.Context, r *provisioning.Repository) string
}

type extra struct {
	factory               *Factory
	decrypter             repository.Decrypter
	webhookBuilder        WebhookURLBuilder
	folderMetadataEnabled bool
}

func Extra(decrypter repository.Decrypter, factory *Factory, webhookBuilder WebhookURLBuilder, folderMetadataEnabled bool) repository.Extra {
	return &extra{
		decrypter:             decrypter,
		factory:               factory,
		webhookBuilder:        webhookBuilder,
		folderMetadataEnabled: folderMetadataEnabled,
	}
}

func (e *extra) Type() provisioning.RepositoryType {
	return provisioning.GitHubRepositoryType
}

func (e *extra) Build(ctx context.Context, r *provisioning.Repository) (repository.Repository, error) {
	if r == nil || r.Spec.GitHub == nil {
		return nil, fmt.Errorf("github configuration is required")
	}
	logger := logging.FromContext(ctx).With("url", r.Spec.GitHub.URL, "branch", r.Spec.GitHub.Branch, "path", r.Spec.GitHub.Path)
	logger.Info("Instantiating Github repository")

	secure := e.decrypter(r)
	token, err := secure.Token(ctx)
	if err != nil {
		return nil, fmt.Errorf("unable to decrypt token: %w", err)
	}

	gitRepo, err := git.NewRepository(ctx, r, git.RepositoryConfig{
		URL:    r.Spec.GitHub.URL,
		Branch: r.Spec.GitHub.Branch,
		Path:   r.Spec.GitHub.Path,
		Token:  token,
	})
	if err != nil {
		return nil, fmt.Errorf("error creating git repository: %w", err)
	}

	ghRepo, err := NewRepository(ctx, r, gitRepo, e.factory, token)
	if err != nil {
		return nil, fmt.Errorf("error creating github repository: %w", err)
	}

	if util.IsInterfaceNil(e.webhookBuilder) {
		return ghRepo, nil
	}

	webhookURL := e.webhookBuilder.WebhookURL(ctx, r)
	if len(webhookURL) == 0 {
		logger.Debug("Skipping webhook setup as no webhooks are not configured")
		return ghRepo, nil
	}

	webhookSecret, err := secure.WebhookSecret(ctx)
	if err != nil {
		return nil, fmt.Errorf("decrypt webhookSecret: %w", err)
	}

	return NewGithubWebhookRepository(ghRepo, webhookURL, webhookSecret, e.folderMetadataEnabled, e.factory.replayCache), nil
}

func (e *extra) Mutate(ctx context.Context, obj runtime.Object) error {
	return Mutate(ctx, obj)
}

func (e *extra) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
	return Validate(ctx, obj)
}
