package config

import (
	"os"
	"time"

	pkgconfig "github.com/tidbcloud/infra-provider/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/constants"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/yaml"
)

type RootConfig struct {
	ControllerConfig `json:",inline"`

	Predicate              metav1.LabelSelector `json:"predicate"`
	PublicHostedZoneDomain string               `json:"publicHostedZoneDomain"`
	PublicHostedZoneID     string               `json:"publicHostedZoneID"`
	// PublicHostedZoneResourceGroup is the resource group of the public hosted zone.
	// Used in Azure
	PublicHostedZoneResourceGroup string                       `json:"publicHostedZoneResourceGroup"`
	ComponentPCAARN               string                       `json:"componentPCAARN"`
	SelfSignedCAConfig            pkgconfig.SelfSignedCAConfig `json:"selfSignedCAConfig"`
	// ResourceLabels will be added into 'metadata.labels' for every resource created by the regionalserver API.
	ResourceLabels                map[string]string `json:"resourceLabels,omitempty"`
	ClusterControlServiceEndpoint string            `json:"clusterControlServiceEndpoint"`
	TiCDCServiceEndpoint          string            `json:"ticdcServiceEndpoint"`
	ACSServiceEndpoint            string            `json:"acsServiceEndpoint"`
	O11yRegionalServiceEndpoint   string            `json:"o11yRegionalServiceEndpoint"`
	// O11yAgentNamespace is where the new Dedicated agent controller stores agent CRs.
	// Empty preserves the AgentControl API default namespace, observability.
	O11yAgentNamespace string `json:"o11yAgentNamespace,omitempty"`
	// O11yBizType overrides the environment-derived biz type for O11y clusters and agents.
	O11yBizType string `json:"o11yBizType,omitempty"`
	// O11yRegionalServerTestConfig is the configuration for the o11y region server dev.
	// This config is used for dev and staging environment.
	O11yRegionalServerTestConfig O11yRegionalServerTestConfig `json:"o11yRegionalServerTestConfig"`
	// O11ySeedClusterID is the cluster id of the o11y seed cluster, used in multi dedicated regionalserver in the same region.
	// At present, this id is send to o11y regional server when to create o11y cluster.
	O11ySeedClusterID string `json:"o11ySeedClusterID"`
	// This config juse for test
	LoadBalancer LoadBalancerConfig `json:"loadBalancer"`
	// DataPlaneAccountNamespace is the namespace of the configmap that stores the data plane account info.
	DataPlaneAccountNamespace string           `json:"dataPlaneAccountNamespace"`
	K8sUpgradeConfig          K8sUpgradeConfig `json:"k8sUpgradeConfig"`

	// FeatureGates is the feature gates configuration.
	// feature gates is used to keep consistent with the cloudnative architecture.
	// deprecated
	FeatureGates map[pkgconfig.FeatureGateName]pkgconfig.FeatureGate `json:"featureGates"`
	CMEKConfig   AzureCMEKConfig                                     `json:"cmekConfig"`
}

func (c *RootConfig) GetTiCDCServiceEndpoint() string {
	if c.TiCDCServiceEndpoint != "" {
		return c.TiCDCServiceEndpoint
	}
	return "http://ticdc-service-server.ticdc-service"
}

func (c *RootConfig) GetACSServiceEndpoint() string {
	if c.ACSServiceEndpoint != "" {
		return c.ACSServiceEndpoint
	}
	return "http://o11y-agent-control-server.regional-server-system:8082"
}

func (c *RootConfig) IsEnableO11yService() bool {
	return c.O11yRegionalServiceEndpoint != "" || c.O11yRegionalServerTestConfig.Enable
}

type K8sUpgradeConfig struct {
	// ConcurrentWorker is the number of concurrent workers for getting k8s infra info.
	ConcurrentWorker int `json:"concurrentWorker"`
}

type LoadBalancerConfig struct {
	ResourceARN string `json:"resourceARN"`
	DNSIdentify string `json:"identify"`
}

type ControllerConfig struct {
	Provider string `json:"provider"`
	Env      string `json:"env"`
	// ProviderConfig is the manager account's configuration
	// Used in GCP and Azure
	ProviderConfig ProviderConfig `json:"providerConfig"`
	RegionConfig   RegionConfig   `json:"regionConfig"`
	APPBoxConfig   APPBoxConfig   `json:"appboxConfig"`
	DefaultVPCCIDR string         `json:"defaultVPCCIDR"`
	GCConfig       GCConfig       `json:"gcConfig"`
	// For global resources such as dns, pca, use GlobalCrossplaneProviderConfig to provision
	GlobalCrossplaneProviderConfig string `json:"globalCrossplaneProviderConfig"`
	// ClusterControllerAssumeRoleName is the name of the role that the cluster controller assumes to access eks.
	ClusterControllerAssumeRoleName string `json:"clusterControllerAssumeRoleName"`
	// TiCDCControllerAssumeRoleName is the name of the role that the ticdc service controller assumes to access eks.
	TiCDCControllerAssumeRoleName string `json:"ticdcControllerAssumeRoleName"`
	// ReadonlyClusterControllerAssumeRoleName is the name of the readonly role that the readonly cluster controller assumes to access eks in readonly.
	ReadonlyClusterControllerAssumeRoleName string `json:"readonlyClusterControllerAssumeRoleName"`
	// DPControllerAssumeRoleName is the name of the role that the data plane controller assumes to access eks.
	DPControllerAssumeRoleName string `json:"dpControllerAssumeRoleName"`
	// ACRResourceGroup is the resource group of the Azure Container Registry.
	ACRResourceGroup string `json:"acrResourceGroup"`
}

type K8sVersionConfig struct {
	Tenants  map[string]string `json:"tenants"`
	Projects map[string]string `json:"projects"`
}

type APPBoxConfig struct {
	ServiceRoleName                   string                            `json:"serviceRoleName"`
	NodeConfig                        NodeConfig                        `json:"nodeConfig"`
	EnableK8sPublicAccess             bool                              `json:"enableK8sPublicAccess"`
	ApiserverPublicAccessCidrs        []string                          `json:"apiserverPublicAccessCidrs"`
	K8sVersion                        string                            `json:"k8sVersion"`
	K8sVersionConfig                  K8sVersionConfig                  `json:"k8sVersionConfig"`
	GKEMaintenanceExclusionTimeWindow GKEMaintenanceExclusionTimeWindow `json:"gkeMaintenanceExclusionTimeWindow,omitempty"`
	// GKEMaintenanceExclusionRequeueAfter is the requeue after duration of the maintenance exclusion.
	GKEMaintenanceExclusionRequeueAfter metav1.Duration     `json:"gkeMaintenanceExclusionRequeueAfter,omitempty"`
	EnableArtifactRegistry              bool                `json:"enableArtifactRegistry"`
	AuthorizedSubjects                  []string            `json:"authorizedSubjects"`
	AdminNodeRoleConfig                 AdminNodeRoleConfig `json:"adminNodeRoleConfig"`
	TiCDCNodeRoleConfig                 TiCDCNodeRoleConfig `json:"tiCDCNodeRoleConfig"`
	// AdminGroupObjectIDs is the object ids of the admin group in the Azure AD.
	AdminGroupObjectIDs []string `json:"adminGroupObjectIDs"`
}

func (cfg *APPBoxConfig) GetK8sVersion(tenant, project string) string {
	v, ok := cfg.K8sVersionConfig.Projects[project]
	if ok {
		return v
	}
	v, ok = cfg.K8sVersionConfig.Tenants[tenant]
	if ok {
		return v
	}
	return cfg.K8sVersion
}

type AdminNodeRoleConfig struct {
	// TrustPolicyFile is the path of the trust policy file. This policy is the relationship policy document.
	TrustPolicyFile string `json:"trustPolicyFile"`
	// PolicyTemplateFile is the path of the policy template file. This policy is the permission policy document.
	// The policy template file is a json file that contains the policy template.
	// Supported template variables: {{.Project}}
	PolicyTemplateFile string `json:"policyTemplateFile"`
	// ManagedPolicies is the list of managed policies that will be attached to the role.
	ManagedPolicies []string `json:"managedPolicies"`
	// AWSLBControllerPolicyFile is the path of the policy file for the AWS Load Balancer Controller.
	AWSLBControllerPolicyFile string `json:"awsLBControllerPolicyFile"`
}

// TiCDCNodeRoleConfig holds configuration for the TiCDC node IAM role.
type TiCDCNodeRoleConfig struct {
	// TrustPolicyTemplateFile is the path of the trust policy file. This policy is the relationship policy document.
	// The policy template file is a json file that contains the policy template.
	// Supported template variables: {{.RoleARN}}
	TrustPolicyTemplateFile string `json:"trustPolicyTemplateFile"`
	// PolicyTemplate is the path of the policy template file. This policy is the permission policy document.
	// The policy template file is a json file that contains the policy template.
	// Supported template variables: {{.Project}}
	PolicyTemplateFile string `json:"policyTemplateFile"`
	// ManagedPolicies is the list of managed policies that will be attached to the role.
	ManagedPolicies []string `json:"managedPolicies"`
}

type NodeConfig struct {
	CapacityType      *string `json:"capacityType,omitempty"` // optional: ON_DEMAND;SPOT
	AdminNodesAMIType string  `json:"adminNodesAMIType"`
	// SystemVolumeSize is the GiB size of volume used for storing container images and logs.
	SystemVolumeSize int64 `json:"systemVolumeSize"`
	MaxPodsPerNode   int64 `json:"maxPodsPerNode"`
}

type GKEMaintenanceExclusionTimeWindow struct {
	// AutoUpdate is the flag to enable auto update the exclusion time window.
	AutoUpdate bool       `json:"autoUpdate"`
	StartTime  *time.Time `json:"startTime"`
	EndTime    *time.Time `json:"endTime"`
}

type GCConfig struct {
	DryRun bool `json:"dryRun"`
	// NodePoolGC controls periodic cleanup of idle workload nodepools.
	NodePoolGC NodePoolGCConfig `json:"nodePoolGC,omitempty"`
	// CheckIdleInterval is the interval of checking idle resources
	CheckIdleInterval metav1.Duration `json:"checkIdleInterval"`
	// IdleTimeThreshold is the threshold of idle time.
	// If the idle time of a resource is greater than this threshold, it will be considered as idle and set phase to ReadyForDelete
	IdleTimeThreshold metav1.Duration `json:"idleTimeThreshold"`
	// IdleTimeRetention is the retention of idle resources
	// If the resources are ReadyForDelete for more than this retention, they will be deleted.
	IdleTimeRetention metav1.Duration `json:"idleTimeRetention"`
}

type NodePoolGCConfig struct {
	Enabled bool `json:"enabled"`
	// DryRun is populated from the parent GC configuration at runtime.
	DryRun              bool            `json:"-"`
	AutoApproveDeletion bool            `json:"autoApproveDeletion"`
	ScanInterval        metav1.Duration `json:"scanInterval"`
	ExpireAfter         metav1.Duration `json:"expireAfter"`
}

type RegionConfig struct {
	Name  string   `json:"name"`
	Zones []string `json:"zones"`
}

type ProviderConfig struct {
	// manager account id, resources shared by other accounts are created in this account
	// e.g. gcr bucket for storing container images.
	// gcp: project id
	// azure: subscription id
	AccountID string `json:"accountID"`
	// crossplane provider config name of the manager account.
	CrossplaneProvider string `json:"crossplaneProvider"`
}

type AzureCMEKConfig struct {
	AppRegistrationId   string `json:"appRegistrationId"`
	AppRegistrationUmi  string `json:"appRegistrationUmi"`
	AppRegistrationName string `json:"appRegistrationName"`
}

func LoadRootConfig(path string) (RootConfig, error) {
	data, err := os.ReadFile(path)
	if err != nil {
		return RootConfig{}, err
	}
	var config RootConfig
	err = yaml.Unmarshal(data, &config)
	if err != nil {
		return RootConfig{}, err
	}

	return config, nil
}

// SetGlobalConfig sets the global configuration.
func SetGlobalConfig(conf RootConfig) {
	if conf.DataPlaneAccountNamespace != "" {
		constants.DataPlaneAccountNamespace = conf.DataPlaneAccountNamespace
	}
}
