package appregistry

import (
	"context"

	"github.com/grafana/grafana/pkg/registry/apps/quotas"
	"github.com/open-feature/go-sdk/openfeature"
	"k8s.io/client-go/rest"

	"github.com/grafana/grafana-app-sdk/app"
	appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"

	"github.com/grafana/grafana/pkg/infra/log"
	"github.com/grafana/grafana/pkg/registry"
	"github.com/grafana/grafana/pkg/registry/apps/advisor"
	"github.com/grafana/grafana/pkg/registry/apps/alerting/historian"
	"github.com/grafana/grafana/pkg/registry/apps/alerting/notifications"
	"github.com/grafana/grafana/pkg/registry/apps/alerting/rules"
	"github.com/grafana/grafana/pkg/registry/apps/annotation"
	"github.com/grafana/grafana/pkg/registry/apps/correlations"
	"github.com/grafana/grafana/pkg/registry/apps/example"
	"github.com/grafana/grafana/pkg/registry/apps/investigations"
	"github.com/grafana/grafana/pkg/registry/apps/logsdrilldown"
	"github.com/grafana/grafana/pkg/registry/apps/playlist"
	"github.com/grafana/grafana/pkg/registry/apps/plugins"
	"github.com/grafana/grafana/pkg/registry/apps/shorturl"
	"github.com/grafana/grafana/pkg/services/apiserver"
	"github.com/grafana/grafana/pkg/services/apiserver/builder"
	"github.com/grafana/grafana/pkg/services/apiserver/builder/runner"
	"github.com/grafana/grafana/pkg/services/featuremgmt"
	"github.com/grafana/grafana/pkg/setting"
)

// ProvideAppInstallers returns a list of app installers that can be used to install apps.
// This is the pattern that should be used to provide app installers in the app registry.
func ProvideAppInstallers(
	features featuremgmt.FeatureToggles,
	playlistAppInstaller *playlist.PlaylistAppInstaller,
	pluginsApplInstaller *plugins.AppInstaller,
	shorturlAppInstaller *shorturl.ShortURLAppInstaller,
	rulesAppInstaller *rules.AlertingRulesAppInstaller,
	correlationsAppInstaller *correlations.AppInstaller,
	alertingNotificationAppInstaller *notifications.AlertingNotificationsAppInstaller,
	logsdrilldownAppInstaller *logsdrilldown.LogsDrilldownAppInstaller,
	annotationAppInstaller *annotation.AnnotationAppInstaller,
	exampleAppInstaller *example.ExampleAppInstaller,
	advisorAppInstaller *advisor.AdvisorAppInstaller,
	alertingHistorianAppInstaller *historian.AlertingHistorianAppInstaller,
	quotasAppInstaller *quotas.QuotasAppInstaller,
) []appsdkapiserver.AppInstaller {
	featureClient := openfeature.NewDefaultClient()
	installers := []appsdkapiserver.AppInstaller{
		playlistAppInstaller,
		pluginsApplInstaller,
		exampleAppInstaller,
	}
	if featureClient.Boolean(context.Background(), featuremgmt.FlagKubernetesUnifiedStorageQuotas, false, openfeature.TransactionContext(context.Background())) {
		installers = append(installers, quotasAppInstaller)
	}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if features.IsEnabledGlobally(featuremgmt.FlagKubernetesShortURLs) {
		installers = append(installers, shorturlAppInstaller)
	}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if features.IsEnabledGlobally(featuremgmt.FlagKubernetesAlertingRules) && rulesAppInstaller != nil {
		installers = append(installers, rulesAppInstaller)
	}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if features.IsEnabledGlobally(featuremgmt.FlagKubernetesCorrelations) {
		installers = append(installers, correlationsAppInstaller)
	}
	if alertingNotificationAppInstaller != nil {
		installers = append(installers, alertingNotificationAppInstaller)
	}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if features.IsEnabledGlobally(featuremgmt.FlagKubernetesLogsDrilldown) {
		installers = append(installers, logsdrilldownAppInstaller)
	}
	//nolint:staticcheck
	if features.IsEnabledGlobally(featuremgmt.FlagKubernetesAnnotations) {
		installers = append(installers, annotationAppInstaller)
	}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if features.IsEnabledGlobally(featuremgmt.FlagGrafanaAdvisor) {
		installers = append(installers, advisorAppInstaller)
	}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if features.IsEnabledGlobally(featuremgmt.FlagKubernetesAlertingHistorian) && alertingHistorianAppInstaller != nil {
		installers = append(installers, alertingHistorianAppInstaller)
	}

	return installers
}

var (
	_ registry.BackgroundService = (*Service)(nil)
)

type Service struct {
	runner *runner.APIGroupRunner
	log    log.Logger
}

// ProvideBuilderRunners adapts apps to the APIGroupBuilder interface.
// deprecated: Use ProvideAppInstallers instead.
func ProvideBuilderRunners(
	registrar builder.APIRegistrar,
	restConfigProvider apiserver.RestConfigProvider,
	features featuremgmt.FeatureToggles,
	investigationAppProvider *investigations.InvestigationsAppProvider,
	grafanaCfg *setting.Cfg,
) (*Service, error) {
	cfgWrapper := func(ctx context.Context) (*rest.Config, error) {
		cfg, err := restConfigProvider.GetRestConfig(ctx)
		if err != nil {
			return nil, err
		}
		cfg.APIPath = "/apis"
		return cfg, nil
	}

	cfg := runner.RunnerConfig{
		RestConfigGetter: cfgWrapper,
		APIRegistrar:     registrar,
	}
	logger := log.New("app-registry")
	var apiGroupRunner *runner.APIGroupRunner
	var err error
	providers := []app.Provider{}
	//nolint:staticcheck // not yet migrated to OpenFeature
	if features.IsEnabledGlobally(featuremgmt.FlagInvestigationsBackend) {
		logger.Debug("Investigations backend is enabled")
		providers = append(providers, investigationAppProvider)
	}
	apiGroupRunner, err = runner.NewAPIGroupRunner(cfg, providers...)

	if err != nil {
		return nil, err
	}
	return &Service{runner: apiGroupRunner, log: logger}, nil
}

func (s *Service) Run(ctx context.Context) error {
	s.log.Debug("initializing app registry")
	if err := s.runner.Init(ctx); err != nil {
		return err
	}
	s.log.Info("app registry initialized")
	return s.runner.Run(ctx)
}
