package advisor

import (
	"context"

	"github.com/grafana/grafana-app-sdk/k8s"
	"github.com/grafana/grafana-app-sdk/resource"
	advisorv0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
	"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
	"github.com/grafana/grafana/apps/advisor/pkg/app/checks/datasourcecheck"
	"github.com/grafana/grafana/apps/advisor/pkg/app/checks/plugincheck"
	"github.com/grafana/grafana/pkg/services/apiserver"
	apiserverrequest "github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
	"github.com/grafana/grafana/pkg/setting"
)

type AdvisorStats interface {
	ReportSummary(ctx context.Context) (*ReportInfo, error)
}

type Service struct {
	cfg             *setting.Cfg
	namespace       string
	clientGenerator func(ctx context.Context) (resource.Client, error)
}

func ProvideService(
	cfg *setting.Cfg,
	restConfigProvider apiserver.RestConfigProvider,
) (*Service, error) {
	namespace := "default"
	if cfg.StackID != "" {
		namespace = apiserverrequest.GetNamespaceMapper(cfg)(1)
	}

	return &Service{
		cfg:       cfg,
		namespace: namespace,
		clientGenerator: func(ctx context.Context) (resource.Client, error) {
			kubeConfig, err := restConfigProvider.GetRestConfig(ctx)
			if err != nil {
				return nil, err
			}
			clientGenerator := k8s.NewClientRegistry(*kubeConfig, k8s.ClientConfig{})
			return clientGenerator.ClientFor(advisorv0alpha1.CheckKind())
		},
	}, nil
}

type ReportInfo struct {
	PluginsOutdated      int
	PluginsDeprecated    int
	DatasourcesUnhealthy int
}

func isMoreRecent(check1 resource.Object, check2 resource.Object) bool {
	return check1.GetCommonMetadata().CreationTimestamp.After(check2.GetCommonMetadata().CreationTimestamp)
}

// findLatestCheck returns the most recent check of the specified type from the list
func findLatestCheck(checkList []resource.Object, checkType string) *advisorv0alpha1.Check {
	var latestCheck *advisorv0alpha1.Check
	for _, check := range checkList {
		currentCheckType := check.GetLabels()[checks.TypeLabel]
		if currentCheckType != checkType {
			continue
		}
		if latestCheck == nil || isMoreRecent(check, latestCheck) {
			latestCheck = check.(*advisorv0alpha1.Check)
		}
	}
	return latestCheck
}

func (s *Service) ReportSummary(ctx context.Context) (*ReportInfo, error) {
	client, err := s.clientGenerator(ctx)
	if err != nil {
		return nil, err
	}
	checkList, err := client.List(ctx, s.namespace, resource.ListOptions{})
	if err != nil {
		return nil, err
	}

	latestPluginCheck := findLatestCheck(checkList.GetItems(), plugincheck.CheckID)
	latestDatasourceCheck := findLatestCheck(checkList.GetItems(), datasourcecheck.CheckID)
	reportInfo := &ReportInfo{}
	if latestPluginCheck != nil {
		for _, failure := range latestPluginCheck.Status.Report.Failures {
			switch failure.StepID {
			case plugincheck.UpdateStepID:
				reportInfo.PluginsOutdated++
			case plugincheck.DeprecationStepID:
				reportInfo.PluginsDeprecated++
			}
		}
	}
	if latestDatasourceCheck != nil {
		for _, failure := range latestDatasourceCheck.Status.Report.Failures {
			if failure.StepID == datasourcecheck.HealthCheckStepID {
				reportInfo.DatasourcesUnhealthy++
			}
		}
	}

	return reportInfo, nil
}
