package config

import (
	"fmt"
	"strings"
	"time"

	"github.com/tidbcloud/metrics-integration/global/internal/domain/constant"
	"github.com/tidbcloud/metrics-integration/global/internal/domain/entity"
)

type GlobalConfig struct {
	ServerConfig     ServerConfig           `mapstructure:"server"`
	MysqlConfig      MysqlConfig            `mapstructure:"mysql"`
	HelmChartConfigs HelmChartConfigs       `mapstructure:"helmChart"`
	WorkflowConfig   WorkflowConfig         `mapstructure:"workflow"`
	GrpcConfig       GrpcConfig             `mapstructure:"grpc"`
	RBACConfig       RBACConfig             `mapstructure:"rbacConfig"`
	DataplaneConfig  []DataplaneCluster     `mapstructure:"dataplane"`
	DebugConfig      DebugConfig            `mapstructure:"debugConfig"`
	PipelineDefaults PipelineDefaultsConfig `mapstructure:"pipelineDefaults"`
	// used for pipeline
	O11yGlobalServerConfig O11yGlobalServerConfig `mapstructure:"o11yGlobalServer"`
	RegionalExecutorConfig RegionalExecutorConfig `mapstructure:"regionalExecutor"`
}

type ServerConfig struct {
	CMKArn                 string                                   `mapstructure:"cmkArn" default:""`
	EnableEventBusConsumer bool                                     `mapstructure:"enableEventBusConsumer" default:"true"`
	FeatureGates           map[constant.FeatureGateName]FeatureGate `mapstructure:"featureGates"`
}

type MysqlConfig struct {
	User              string `yaml:"user" default:"root" valid:"required"`
	Host              string `yaml:"host" default:"host" valid:"required"`
	Database          string `yaml:"database" default:"dbaas" valid:"required"`
	Password          string `yaml:"password" required:"true"`
	Port              uint16 `yaml:"port" default:"4000"`
	EnableDetailedLog bool   `yaml:"enableDBQueryLog" default:"false"`
	LogLevel          string `yaml:"logLevel" default:"info"`
	CACert            string
	ClientCert        string
	ClientKey         string
	TLSConfigKey      string
	ConnMaxLifetime   time.Duration
	MaxIdleConns      int
	MaxOpenConns      int
}

// FeatureGate ... https://pingcap.feishu.cn/docx/Nj1gdJxBnoWiO2xIHCdcWN7unqf
type FeatureGate struct {
	// If `All` is true, this feature gate will be enabled for all except DisabledTenants and DisabledProjects.
	// If `All` is false, this feature gate will be disabled for all except EnabledTenants and EnabledProjects.
	All bool `mapstructure:"all" default:"false"`
	// Enable the feature for the specified tenants.
	EnabledTenants []uint64 `mapstructure:"enabledTenants"`
	// Enable the feature for the specified projects.
	EnabledProjects []uint64 `mapstructure:"enabledProjects"`
	EnabledRegions  []string `mapstructure:"enabledRegions"`

	// Disable the feature for the specified tenants.
	DisabledTenants []uint64 `mapstructure:"disabledTenants"`
	// Disable the feature for the specified projects.
	DisabledProjects []uint64 `mapstructure:"disabledProjects"`
	DisabledRegions  []string `mapstructure:"disabledRegions"`

	// Required the feature in these regions
	RequiredRegions []string `mapstructure:"requiredRegions"`

	// Required if the organization is created after the specified time.
	AffectOrgCreatedAfter string `mapstructure:"affectOrgCreatedAfter" default:"2099-01-01T00:00:00Z"`
}

type WorkflowConfig struct {
	// use the same database cluster as server
	Database string `mapstructure:"database" default:"workflow"`
	DAGDir   string `mapstructure:"dag_dir"`
}

// DataplaneConfig example:
//   - name: us-west-2
//     provider: aws
//     resource_id: arn:aws:eks:us-west-2:123456789012:cluster/o11y-us-west-2
//   - name: us-west1
//     provider: gke
//     resource_id: /projects/my-project/locations/us-west1/clusters/o11y-us-west1
//   - name: local
//     provider: local
//     resource_id: "/root/.kube/config"

type DataplaneCluster struct {
	Name       string `mapstructure:"name" yaml:"name"`
	Provider   string `mapstructure:"provider" yaml:"provider"`
	ResourceID string `mapstructure:"resource_id" yaml:"resource_id"`
}

type GrpcConfig struct {
	MgmtSvcAddr         string `mapstructure:"mgmtSvcAddr" default:"tidb-mgmt-svc-grpc:10001"`
	AccountProviderAddr string `mapstructure:"accountProviderSvcAddr" default:"account-provider-grpc:10000"`
	AccountAclSvcAddr   string `mapstructure:"accountAclSvcAddr" default:"account-acl-grpc:10000"`
}

type DebugConfig struct {
	Enable                   bool   `mapstructure:"enable"  default:"false"`
	DebugEventBusInLocal     bool   `mapstructure:"debugEventBusInLocal" default:"false"`
	DisableKafka             bool   `mapstructure:"disableKafka" default:"true"`
	DisableServerWorkflowCli bool   `mapstructure:"disableServerWorkflowCli" default:"false"`
	KafkaProxyAddress        string `mapstructure:"kafkaProxyAddress" default:"localhost:9092"`
	DisableDedicatedNodepool bool   `mapstructure:"disableDedicatedNodepool" default:"true"`
}

type PipelineDefaultsConfig struct {
	EnableBackfillBatchMode bool `mapstructure:"enableBackfillBatchMode" default:"true"`
}

type HelmChartConfigs []HelmChartConfig

type HelmChartConfig struct {
	Name      string `mapstructure:"name" yaml:"name"`
	ChartPath string `mapstructure:"chartPath" yaml:"chartPath"`
	ImageRepo string `mapstructure:"imageRepo" yaml:"imageRepo"`
	ImageTag  string `mapstructure:"imageTag" yaml:"imageTag"`
}

type O11yGlobalServerConfig struct {
	Url          string `mapstructure:"url" yaml:"url" required:"true"`
	DomainSuffix string `mapstructure:"domainSuffix" yaml:"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" yaml:"bizType"`
	// BizTypeConfigs maps service plan names (lowercase: "dedicated", "premium", "essential_v2", "starter", "essential") to bizType strings.
	BizTypeConfigs map[string]string `mapstructure:"bizTypeConfigs" yaml:"bizTypeConfigs"`

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

	RegionalDataServerUriPrefix string      `mapstructure:"regionalDataServerUriPrefix" yaml:"regionalDataServerUriPrefix" required:"true"`
	AllowList                   []entity.ID `mapstructure:"allowList" yaml:"allowList" required:"false"`
	BlockList                   []entity.ID `mapstructure:"blockList" yaml:"blockList" required:"false"`
}

// GetBizTypeByServicePlan returns the appropriate biztype for the given service plan.
// 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)
	if len(c.BizTypeConfigs) > 0 {
		if bizType, ok := c.BizTypeConfigs[strings.ToLower(servicePlan)]; ok {
			return bizType
		}
	}
	// Essential V2 has no safe legacy/Dedicated route.
	if strings.EqualFold(servicePlan, "Essential_V2") {
		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 RegionalExecutorConfig struct {
	Servers                  map[string]string `mapstructure:"servers"`
	PipelineImageOverride    map[string]string `mapstructure:"pipelineImageOverride"`
	GlobalManagedPipeline    []string          `mapstructure:"globalManagedPipeline"`
	PrometheusQueryEndpoints map[string]string `mapstructure:"prometheusQueryEndpoints"`
}

func normalizeCloudProvider(provider string) string {
	switch strings.ToLower(strings.TrimSpace(provider)) {
	case "aws", "amazon", "amazonwebservices":
		return "aws"
	case "gcp", "gke", "google", "googlecloud":
		return "gcp"
	case "azure":
		return "azure"
	case "alicloud", "aliyun", "alibaba", "alibabacloud":
		return "alicloud"
	case "local", "incluster":
		return "local"
	default:
		return strings.ToLower(strings.TrimSpace(provider))
	}
}

func providerRegionLookupKeys(provider, region string) []string {
	normalizedProvider := normalizeCloudProvider(provider)
	switch {
	case normalizedProvider != "" && region != "":
		return []string{fmt.Sprintf("%s-%s", normalizedProvider, region), region}
	case region != "":
		return []string{region}
	default:
		return []string{region}
	}
}

func lookupRegionalConfigValue(values map[string]string, provider, region string) string {
	for _, key := range providerRegionLookupKeys(provider, region) {
		if value, ok := values[key]; ok {
			return value
		}
	}
	return ""
}

func (c *RegionalExecutorConfig) GetServerAddr(provider, region string) string {
	return lookupRegionalConfigValue(c.Servers, provider, region)
}

func (c *RegionalExecutorConfig) GetPipelineImageOverride(provider, region string) string {
	return lookupRegionalConfigValue(c.PipelineImageOverride, provider, region)
}

func (c *RegionalExecutorConfig) GetPrometheusQueryEndpoint(provider, region string) string {
	return lookupRegionalConfigValue(c.PrometheusQueryEndpoints, provider, region)
}

func (c *RegionalExecutorConfig) GetPipelineDataplaneServerAddr(pipelineId uint64, provider, region string) string {
	// 1. If pipeline id is in globalManagedPipeline, it means this pipeline is globally managed by manager, return empty string directly
	// 2. Otherwise, find the corresponding server addr from Servers based on region and return it
	// 3. If the corresponding region is not found, return empty string
	// 4. Note: If globalManagedPipeline configures a certain pipeline id, this pipeline will be globally managed by manager regardless of its region

	for _, id := range c.GlobalManagedPipeline {
		if id == fmt.Sprintf("%d", pipelineId) {
			return ""
		}
	}

	return c.GetServerAddr(provider, region)
}
