package prometheusregion

import (
	"fmt"
	"log"

	"github.com/go-viper/mapstructure/v2"
	"github.com/tidbcloud/metrics-integration/global/internal/domain/entity"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/config"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/helm"
	pipetype "github.com/tidbcloud/metrics-integration/global/pkg/helm/pipeline"
	"helm.sh/helm/v3/pkg/chart"
	"helm.sh/helm/v3/pkg/chart/loader"
	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
)

type PrometheusConfig struct {
	PrometheusID           string                    `json:"prometheusID,omitempty"`
	TargetClusters         []*pipetype.TargetCluster `json:"targetClusters,omitempty"`
	PrometheusMetaData     []*entity.MetricsKey      `json:"prometheusMetaData,omitempty"`
	ChartPath              string                    `json:"chartPath,omitempty"`
	PrometheusImageRepo    string                    `json:"prometheusImageRepo,omitempty"`
	PrometheusImageTag     string                    `json:"promethuesImageTag,omitempty"`
	O11yGlobalServerConfig map[string]interface{}    `json:"o11yGlobalServerConfig,omitempty"`
	// ExcludeImageUpdate controls whether to exclude image configuration from helm values
	// Set to true for target cluster only updates to avoid image upgrades
	ExcludeImageUpdate bool `json:"excludeImageUpdate,omitempty"`
}

var _ helm.DryRunablePackage = &PrometheusConfig{}

func NewPrometheusConfig(prometheusID string, targetClusters []*pipetype.TargetCluster, PrometheusMetaData []*entity.MetricsKey, excludeImageUpdate bool) *PrometheusConfig {
	// Get config values once and store them in the struct
	prometheusChart := config.Global().HelmChartConfigs.GetChart(config.PrometheusRegionChartName)

	// Process the o11y global server config
	var o11yGlobalServerConfig map[string]interface{}
	if err := mapstructure.Decode(config.Global().O11yGlobalServerConfig, &o11yGlobalServerConfig); err != nil {
		log.Fatalf("failed to decode o11y global server config: %v", err)
	}

	return &PrometheusConfig{
		PrometheusID:           prometheusID,
		TargetClusters:         targetClusters,
		PrometheusMetaData:     PrometheusMetaData,
		ChartPath:              prometheusChart.ChartPath,
		PrometheusImageRepo:    prometheusChart.ImageRepo,
		PrometheusImageTag:     prometheusChart.ImageTag,
		O11yGlobalServerConfig: o11yGlobalServerConfig,
		ExcludeImageUpdate:     excludeImageUpdate,
	}
}

func (c *PrometheusConfig) GetNamespace() string {
	return fmt.Sprintf("prometheus-%s", c.PrometheusID)
}

func (c *PrometheusConfig) GetName() string {
	return fmt.Sprintf("prometheus-%s", c.PrometheusID)
}

func (c *PrometheusConfig) DryRun() bool {
	return false
}

func (c *PrometheusConfig) ToHelmChart() (result *chart.Chart, err error) {
	result, err = loader.Load(c.ChartPath)
	if err != nil {
		return nil, fmt.Errorf("failed to load helm chart %q: %w", c.ChartPath, err)
	}
	return
}

func (c *PrometheusConfig) ToHelmValues() map[string]interface{} {
	// Process the target clusters config

	tokenMappings := make(map[string]string)
	for _, meta := range c.PrometheusMetaData {
		tokenMappings[fmt.Sprintf("%d", meta.ClusterID)] = meta.KeyHash
	}

	values := map[string]interface{}{
		"service": map[string]interface{}{
			"type": "ClusterIP",
			"port": 8080,
		},
		"prometheus": map[string]interface{}{
			"serverConfig": map[string]interface{}{
				"server": map[string]interface{}{
					"logLevel":          "info",
					"grpcPort":          10000,
					"metricsPort":       9090,
					"metricsListenAddr": "0.0.0.0:8080",
				},
				"o11yGlobalServer": c.O11yGlobalServerConfig,
				// fix targetCluster Keys
				"targetClusters":          c.TargetClusters,
				"prometheusTokenMappings": tokenMappings,
			},
		},
		"disableDedicatedNodepool": config.Global().DebugConfig.DisableDedicatedNodepool,
	}

	// Only include image configuration if not excluded
	// When ExcludeImageUpdate is true, we skip setting image values to use defaults from values.yaml
	// TODO: fix exclude image update
	// if !c.ExcludeImageUpdate {

	values["image"] = map[string]interface{}{
		"repository": c.PrometheusImageRepo,
		"pullPolicy": "IfNotPresent",
		"tag":        c.PrometheusImageTag,
	}

	return values
}

func (c *PrometheusConfig) GetCRDs() ([]*apiextensionsv1.CustomResourceDefinition, error) {
	return nil, nil
}
