package config

import (
	"time"

	"github.com/tidbcloud/pkgv2/gormutil"

	"github.com/tidbcloud/tidb-management-service/utilities/dynamicconfig"
	"github.com/tidbcloud/tidb-management-service/utilities/featuregate"
)

type Config struct {
	MysqlConfig          gormutil.Config           `mapstructure:"mysql"`
	ProxyConfig          ProxyConfig               `mapstructure:"proxy"`
	ServerConfig         ServerConfig              `mapstructure:"server"`
	AwsConfig            AwsConfig                 `mapstructure:"aws"`
	Eventbus             Eventbus                  `mapstructure:"eventbus"`
	RegionalServerConfig RegionalServerConfig      `mapstructure:"regionalserver"      validate:"required"`
	BizConfig            BizConfig                 `mapstructure:"biz"`
	DynamicConfig        DynamicConfig             `mapstructure:"dynamicConfig"`
	ResourcePoolNetwork  ResourcePoolNetworkConfig `mapstructure:"resourcePoolNetwork"`

	// FIXME For data migration only, please remove this field once all existing regions have been supplemented.
	SupplementProviderIdentity SupplementProviderIdentity `mapstructure:"supplementProviderIdentity"`
}

type ResourcePoolNetworkConfig struct {
	ReservedCIDRs                      []string `mapstructure:"reservedCidrs"`
	AWSMinPrefixLength                 int      `mapstructure:"awsMinPrefixLength"                 default:"16"`
	AWSMaxPrefixLength                 int      `mapstructure:"awsMaxPrefixLength"                 default:"22"`
	AWSRecommendationStartPrefixLength int      `mapstructure:"awsRecommendationStartPrefixLength" default:"16"`
}

// AWSPrefixLengthRange returns the configured AWS resource pool CIDR prefix bounds,
// substituting the production defaults for omitted values.
func (c ResourcePoolNetworkConfig) AWSPrefixLengthRange() (awsMinPrefixLength, awsMaxPrefixLength int) {
	awsMinPrefixLength = c.AWSMinPrefixLength
	if awsMinPrefixLength == 0 {
		awsMinPrefixLength = 16
	}
	awsMaxPrefixLength = c.AWSMaxPrefixLength
	if awsMaxPrefixLength == 0 {
		awsMaxPrefixLength = 22
	}
	return awsMinPrefixLength, awsMaxPrefixLength
}

type SupplementProviderIdentity struct {
	// Identities is a map of logic region name to provider identity.
	// For example:
	// {
	//   "us-east-1-ng": "7838321435",
	//   "us-west-2-byoc-1011": "227896186585",
	//   "ap-southeast-1-ng": "5753857577068077"
	// }
	Identities map[string]string `json:"identities"`
}

type ServerConfig struct {
	SecretStore           *SecretConfig                      `mapstructure:"secret"`
	DependentSvcAddresses DependentSvcAddresses              `mapstructure:"dependentServiceAddresses"`
	FeatureGates          map[string]featuregate.FeatureGate `mapstructure:"featureGates"`
	Env                   string                             `mapstructure:"env"`
}

type DependentSvcAddresses struct {
	Billing2 string `mapstructure:"billing2" default:"billing2:18888" validate:"required"`

	// AccountProviderSvcAddr string     `mapstructure:"accountProviderSvcAddr" default:"account-provider-grpc:10000"`
	// WaitTimeoutInMilliSec  int        `mapstructure:"waitTimeoutInMilliSec"  default:"5000"`

	O11yConfig O11yConfig `mapstructure:"o11yConfig"`

	// ConfigCenterAddr       string     `mapstructure:"configCenterAddr"`
	DataFlow DataFlowService `mapstructure:"dataflow"`
}

type O11yConfig struct {
	GlobalServerDomain        string `mapstructure:"globalServerDomain"`
	GlobalServerDomainForByoc string `mapstructure:"globalServerDomainForByoc"`
	Auth0Domain               string `mapstructure:"auth0Domain"`
	Auth0ClientID             string `mapstructure:"auth0ClientID"`
	Auth0Audience             string `mapstructure:"auth0Audience"`
	Auth0Secret               string `mapstructure:"auth0Secret"`
}

type DataFlowService struct {
	Url                   string `mapstructure:"url"`
	WaitTimeoutInMilliSec int    `mapstructure:"wait-timeout-in-milli-sec"`
}

type AwsConfig struct {
	// AWS private certificate authority (CA) ARN.
	PCAArn string `mapstructure:"pcaArn" validate:"required"`
	// AWS KMS customer master key (CMK) ARN.
	CMKArn string `mapstructure:"cmkArn" validate:"required"`
	// PremiumReservedMetaS3URI is the target S3 URI (s3://bucket/prefix)
	// for premium_reserved metering metadata files.
	PremiumReservedMetaS3URI string `mapstructure:"premiumReservedMetaS3Uri"`
}

type Eventbus struct {
	ProxyAddress    string `mapstructure:"proxyAddress"`
	RetryTimes      uint   `mapstructure:"retryTimes"      default:"15"`
	NumOfGoroutines uint   `mapstructure:"numOfGoroutines" default:"5"`
}

type SecretConfig struct {
	SecretKey  string `mapstructure:"secretKey"`
	Region     string `mapstructure:"region"`
	Profile    string `mapstructure:"profile"`
	RetryCount int    `mapstructure:"retryCount" default:"5"`
}

type ProxyConfig struct {
	Auth0Domain       string `mapstructure:"auth0Domain"       validate:"required"`
	Auth0ClientID     string `mapstructure:"auth0ClientID"     validate:"required"`
	Auth0ClientSecret string `mapstructure:"auth0ClientSecret"`
	Auth0Audience     string `mapstructure:"auth0Audience"     validate:"required"`
}

type RegionalServerConfig struct {
	RequestTimeoutInSec   int `mapstructure:"requestTimeoutInSec"   validate:"required"`
	RetryCount            int `mapstructure:"retryCount"            validate:"required"`
	RetryWaitTimeoutInSec int `mapstructure:"retryWaitTimeoutInSec" validate:"required"`

	// SetPasswordTimeoutInSec overrides RequestTimeoutInSec for the cluster-agent
	// SetPassword / SetPasswordViaSharedPool calls, which can take longer than a
	// typical regional RPC because the V1 fallback path activates the privileged
	// TiDB (mutex + ~30s activate/deactivate cycle) when the TiDB HTTP API is
	// unavailable. Default: 60s when unset or non-positive.
	SetPasswordTimeoutInSec int `mapstructure:"setPasswordTimeoutInSec"`
}

type DynamicConfig struct {
	Enable         bool          `mapstructure:"enable"         default:"false"`
	ConfigPath     string        `mapstructure:"configPath"     default:"dynamic-config/config.yaml"`
	ReloadInterval time.Duration `mapstructure:"reloadInterval" default:"5s"`
	// Loader is not unmarshalled from the config, it will be injected after loader is initialized
	Loader dynamicconfig.Loader `mapstructure:"_"`
}

type BizConfig struct {
	AwsPrivateLinkEndpointLimitPerGroup      int `mapstructure:"maxAwsPrivateLinkEndpointLimit"      default:"15"`
	GcpPrivateLinkEndpointLimitPerGroup      int `mapstructure:"maxGcpPrivateLinkEndpointLimit"      default:"10"`
	AzurePrivateLinkEndpointLimitPerGroup    int `mapstructure:"maxAzurePrivateLinkEndpointLimit"    default:"15"`
	AliCloudPrivateLinkEndpointLimitPerGroup int `mapstructure:"maxAliCloudPrivateLinkEndpointLimit" default:"15"`

	MaxRCUQuotaPerCluster uint `mapstructure:"maxRCUQuotaPerCluster" default:"100000"`

	TotalMaxRCU uint `mapstructure:"totalMaxRCU" default:"200000"`

	MinimalRcuBaseline uint32 `mapstructure:"minimalRcuBaseline" default:"5000"`

	MinimalRcuCoefficient float32 `mapstructure:"minimalRcuCoefficient" default:"0.25"`

	BurstyRatio float32 `mapstructure:"burstyRatio" default:"1.0"`

	InstanceLimitPerResourcePool uint `mapstructure:"instanceLimitPerResourcePool" default:"50"`

	VcpusLimitPerResourcePool uint `mapstructure:"vcpusLimitPerResourcePool" default:"0"`

	IdleResourcePoolTtlInHours uint `mapstructure:"idleResourcePoolTtlInHours" default:"4"`

	// ScaleDownCooldownMinutes is the default cooldown period (in minutes) after a capacity change
	// during which scale-down of max RCU is not permitted. Defaults to 720 (12 hours).
	// Individual tenants can override this via the RcuQuota config.
	ScaleDownCooldownMinutes uint `mapstructure:"scaleDownCooldownMinutes" default:"720"`

	// RcuQuotaConfigs overrides default RCU quota values by service plan.
	// Keys must use global service plan enum names, for example: Premium, BYOC, Essential_V2.
	RcuQuotaConfigs map[string]RcuQuotaConfig `mapstructure:"rcuQuotaConfigs"`

	EnableCrossRegionVPCPeering   bool `mapstructure:"enableCrossRegionVPCPeering"   default:"false"`
	EnableAuditLogViaClusterAgent bool `mapstructure:"enableAuditLogViaClusterAgent" default:"false"`

	// BackupConfig holds the configuration for backup related features.

	BackupConfig BackupConfig `mapstructure:"backup"`

	EncryptionChangeConfig EncryptionChangeConfig `mapstructure:"encryptionChange"`

	// deprecated
	ClusterCertsBucket string             `mapstructure:"clusterCertsBucket"`
	ClusterCertsConfig ClusterCertsConfig `mapstructure:"clusterCertsConfig"`
}

type RcuQuotaConfig struct {
	MaxRCUQuotaPerCluster      *uint    `mapstructure:"maxRCUQuotaPerCluster"`
	TotalMaxRCU                *uint    `mapstructure:"totalMaxRCU"`
	MinimalRcuBaseline         *uint32  `mapstructure:"minimalRcuBaseline"`
	MinimalRcuCoefficient      *float32 `mapstructure:"minimalRcuCoefficient"`
	BurstyRatio                *float32 `mapstructure:"burstyRatio"`
	VcpusLimitPerResourcePool  *uint    `mapstructure:"vcpusLimitPerResourcePool"`
	IdleResourcePoolTtlInHours *uint    `mapstructure:"idleResourcePoolTtlInHours"`
	ScaleDownCooldownMinutes   *uint    `mapstructure:"scaleDownCooldownMinutes"`
}

type ClusterCertsConfig struct {
	Bucket     string `mapstructure:"bucket"`
	DefaultKey string `mapstructure:"defaultKey"`

	// ResourcePoolCertKeyMappings is an optional mapping from resource pool to cert key.
	// If a resource pool is not in the mapping, the DefaultKey will be used.
	ResourcePoolCertKeyMappings map[string]string `mapstructure:"resourcePoolCertKeyMappings"`
	// If a PCA ARN is specified when creating the resource pool, the corresponding certificate key will be used.
	// Otherwise, an error is returned.
	PcaArnCertKeyMappings map[string]string `mapstructure:"pcaArnCertKeyMappings"`
}

type BackupConfig struct {
	ScheduleConfig              ScheduleConfig              `mapstructure:"schedule"`
	SyncConfig                  SyncBackupsConfig           `mapstructure:"sync"`
	ExpireConfig                ExpireBackupsConfig         `mapstructure:"expire"`
	SyncMissingConfig           SyncMissingBackupsConfig    `mapstructure:"syncMissing"`
	UpdateBackupGcSettingConfig UpdateBackupGcSettingConfig `mapstructure:"updateBackupGcSetting"`
}

type ScheduleConfig struct {
	PitrRPO                      time.Duration `mapstructure:"pitrRPO"                      default:"10m"`
	MinRetentionDays             int           `mapstructure:"minRetentionDays"             default:"1"`
	MaxRetentionDays             int           `mapstructure:"maxRetentionDays"             default:"33"`
	DefaultRetentionDays         int           `mapstructure:"defaultRetentionDays"         default:"15"`
	DefaultScheduleMinutePerDay  int           `mapstructure:"defaultScheduleMinutePerDay"  default:"0"`
	DefaultScheduleMinutePerHour int           `mapstructure:"defaultScheduleMinutePerHour" default:"0"`
}

type SyncBackupsConfig struct {
	Enable                      bool          `mapstructure:"enable"                      default:"true"`
	WorkerInterval              time.Duration `mapstructure:"workerInterval"              default:"5m"`
	LoadScheduleChangeInterval  time.Duration `mapstructure:"loadScheduleChangeInterval"  default:"5m"`
	StartRebuildMinuteAtADay    int           `mapstructure:"startRebuildMinuteAtADay"    default:"0"`    // 0 means midnight
	RebuildSlotsIntervalMinutes int           `mapstructure:"rebuildSlotsIntervalMinutes" default:"1440"` // 24 hours
	// Quickly sync backup which created during process restart, this will provide better user experience for users, most of the backup records will be synced in 1 hour.
	// The value here should be smaller than the SyncMissingConfig.BackTraceEndHoursAgo, otherwise they will be overlap, may produce duplicate backup records.
	TraceBackHours int `mapstructure:"traceBackHours" default:"1"`
}

type ExpireBackupsConfig struct {
	Enable         bool          `mapstructure:"enable"         default:"true"`
	WorkerInterval time.Duration `mapstructure:"workerInterval" default:"2h"`
}

type SyncMissingBackupsConfig struct {
	Enable                       bool          `mapstructure:"enable"                       default:"true"`
	WorkerInterval               time.Duration `mapstructure:"workerInterval"               default:"2h"`
	BatchSize                    int           `mapstructure:"batchSize"                    default:"10"`  // the number of clusters to check in one batch
	BatchInterval                time.Duration `mapstructure:"batchInterval"                default:"1s"`  // the interval between two batches
	BackTraceEndHoursAgo         int           `mapstructure:"backTraceEndHoursAgo"         default:"2"`   // the end of the back trace hours, avoid overlap with normal sync
	BackTraceStartPerDayDays     int           `mapstructure:"backTraceStartPerDayDays"     default:"14"`  // 14 days
	BackTraceStartPerHourHours   int           `mapstructure:"backTraceStartPerHourHours"   default:"168"` // 7 days
	BackTraceStartPerMinuteHours int           `mapstructure:"backTraceStartPerMinuteHours" default:"12"`  // 12 hours
}

type UpdateBackupGcSettingConfig struct {
	Enable         bool          `mapstructure:"enable"         default:"true"`
	WorkerInterval time.Duration `mapstructure:"workerInterval" default:"12h"`
}

type EncryptionChangeConfig struct {
	CseCtlTimeoutSec int `mapstructure:"cseCtlTimeoutSec" default:"600"`
}
