package config

import (
	"strings"

	"github.com/tidbcloud/metrics-integration/pipeline/central/entity"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
)

var globalConf *CentralConfig = &CentralConfig{}

type CentralConfig struct {
	ServerConfig                   ServerConfig           `mapstructure:"server"`
	O11yGlobalServerConfig         O11yGlobalServerConfig `mapstructure:"o11yGlobalServer"`
	TargetClusters                 TargetClusters         `mapstructure:"targetClusters"`
	PrometheusTokenMappings        []PromTokenMapping     `mapstructure:"prometheusTokenMappings"`
	PrometheusIntegrationNamespace string                 `mapstructure:"prometheusIntegrationNamespace" default:"prometheus-integration"`
}

type ServerConfig struct {
	// Log level: debug, info, warn, error, fatal
	LogLevel string `mapstructure:"logLevel" default:"info" validate:"oneof=debug info warn error fatal"`
	// central server cidr
	GrpcPort int `mapstructure:"grpcPort" default:"10000"`
	// the central metrics port
	MetricsPort uint16 `mapstructure:"metricsPort" default:"9090"`
	// listen addr for metrics-server self
	MetricsListenAddr string `mapstructure:"metricsListenAddr" default:"0.0.0.0:8080"`
}

type O11yGlobalServerConfig struct {
	Url          string `mapstructure:"url" required:"true"`
	DomainSuffix string `mapstructure:"domainSuffix" required:"true"`
	// BizType is the legacy single bizType field from old configs (before TiDB-X support).
	// Kept for backward compatibility: if BizTypeConfigs is empty, this value is used
	// as the default bizType for all service plans (treated as Dedicated).
	BizType string `mapstructure:"bizType"`
	// BizTypeConfigs maps service plan names (lowercase: "dedicated", "premium", "essential_v2", "starter", "essential") to bizType strings.
	BizTypeConfigs map[string]string `mapstructure:"bizTypeConfigs"`

	Auth0Domain       string `mapstructure:"auth0Domain" required:"true"`
	Auth0Audience     string `mapstructure:"auth0Audience" required:"true"`
	Auth0ClientID     string `mapstructure:"auth0ClientID" required:"true"`
	Auth0ClientSecret string `mapstructure:"auth0ClientSecret" required:"true"`

	RegionalDataServerUriPrefix string `mapstructure:"regionalDataServerUriPrefix" required:"true"`
	LocalTestEnabled            bool   `mapstructure:"localTestEnabled"`
	LocalTestGlobalServerURL    string `mapstructure:"localTestGlobalServerUrl"`
	LocalTestRegionalServerURL  string `mapstructure:"localTestRegionalServerUrl"`
	// LocalTestRegionalServerURLMappings maps original regional server hostnames to local test URLs.
	// Used when NextGen clusters have a dedicated regional server hostname (e.g. Premium).
	// Stored as a slice instead of a map to avoid viper treating dotted hostnames as nested keys.
	LocalTestRegionalServerURLMappings []RegionalServerURLMapping `mapstructure:"localTestRegionalServerUrlMappings"`
	AllowList                          []entity.ID                `mapstructure:"allowList" required:"false"`
	BlockList                          []entity.ID                `mapstructure:"blockList" required:"false"`
}

type RegionalServerURLMapping struct {
	Host string `mapstructure:"host"`
	URL  string `mapstructure:"url"`
}

// GetBizTypeByServicePlan returns the bizType for the given service plan name.
// servicePlan is matched case-insensitively against the keys in BizTypeConfigs
// (e.g. "Dedicated" → key "dedicated", "Premium" → key "premium").
//
// Fallback chain for backward compatibility:
//  1. Look up servicePlan in BizTypeConfigs
//  2. If not found, fall back to BizTypeConfigs["dedicated"]
//  3. If BizTypeConfigs is nil/empty, fall back to the legacy BizType field
func (c *O11yGlobalServerConfig) GetBizTypeByServicePlan(servicePlan string) string {
	servicePlan = strings.TrimSpace(servicePlan)
	servicePlanKey := strings.ToLower(servicePlan)
	if len(c.BizTypeConfigs) > 0 {
		if bizType, ok := c.BizTypeConfigs[servicePlanKey]; ok {
			return bizType
		}
	}
	// Essential V2 has no legacy routing behavior. Never silently route it
	// through the Dedicated datasource when its explicit mapping is missing.
	if strings.EqualFold(servicePlan, string(entity.ServicePlanEssentialV2)) {
		return ""
	}
	if len(c.BizTypeConfigs) > 0 {
		// Unknown or empty servicePlan: fall back to "dedicated" entry.
		if bizType, ok := c.BizTypeConfigs["dedicated"]; ok {
			return bizType
		}
	}
	// No BizTypeConfigs at all (old config): use legacy BizType field.
	return c.BizType
}

type TargetCluster struct {
	Id                uint64             `mapstructure:"id" yaml:"id"`
	Name              string             `mapstructure:"name" yaml:"name"`
	CloudProviderName string             `mapstructure:"cloudProviderName" yaml:"cloudProviderName"`
	RegionName        string             `mapstructure:"regionName" yaml:"regionName"`
	TenantId          uint64             `mapstructure:"tenantId" yaml:"tenantId"`
	ProjectId         uint64             `mapstructure:"projectId" yaml:"projectId"`
	ProjectName       string             `mapstructure:"projectName" yaml:"projectName"`
	Components        []ClusterComponent `mapstructure:"components" yaml:"components"`
	ServicePlan       string             `mapstructure:"servicePlan" yaml:"servicePlan"`
}

type TargetClusters []TargetCluster

type ClusterComponent struct {
	ComponentType string `mapstructure:"componentType" yaml:"ComponentType"`
	CpuNum        int64  `mapstructure:"cpuNum" yaml:"CpuNum"`
}

type PromTokenMapping struct {
	ClusterID uint64 `mapstructure:"clusterID"`
	Token     string `mapstructure:"token"`
}

func (c TargetCluster) NodeCPUCapacityComponentMap() map[string]float64 {
	compMap := make(map[string]float64, len(c.Components))
	for _, comp := range c.Components {
		switch comp.ComponentType {
		case "COMPONENT_TYPE_TIDB":
			compMap["tidb"] = float64(comp.CpuNum)
		case "COMPONENT_TYPE_TIKV":
			compMap["tikv"] = float64(comp.CpuNum)
		case "COMPONENT_TYPE_TIFLASH":
			compMap["tiflash"] = float64(comp.CpuNum)
		}
	}
	return compMap
}
