package entity

import (
	"fmt"
	"strings"
	"time"

	"github.com/samber/lo"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/mysql/schema"
)

type (
	DataPlaneProvider = apiv1.Provider
	ProviderRole      = apiv1.ProviderRole
	ImportRoleConfig  = apiv1.ImportRoleConfig
	ProviderType      = apiv1.ProviderType

	NodeQuota = apiv1.NodeQuota
	NodeSpec  = apiv1.NodeSpec

	ListNodeSpecsResponse                     = apiv1.ListNodeSpecsResponse
	NodeSpecOptions                           = apiv1.NodeSpecOptions
	StorageSizeOptions                        = apiv1.StorageSizeOptions
	StorageMedium                             = apiv1.StorageMedium
	NodeCountOptions                          = apiv1.NodeCountOptions
	NodeCountRule                             = apiv1.NodeCountRule
	NodeCountRuleType                         = apiv1.NodeCountRuleType
	SharedPoolProvisionConfiguration          = apiv1.SharedPoolProvisionConfiguration
	EvaluateSharedPoolNetworkCapacityRequest  = apiv1.EvaluateSharedPoolNetworkCapacityRequest
	EvaluateSharedPoolNetworkCapacityResponse = apiv1.EvaluateSharedPoolNetworkCapacityResponse
	ValidateSharedPoolPauseResponse           = apiv1.ValidateSharedPoolPauseResponse
	SharedPoolPauseValidationFailure          = apiv1.SharedPoolPauseValidationFailure

	Component              = apiv1.Component
	Storage                = apiv1.Component_Storage
	ComponentConfiguration = apiv1.Component_Configuration
	StorageName            = apiv1.Component_Storage_Name
	ComponentType          = apiv1.ComponentType
	ComponentPhase         = apiv1.Component_Phase

	SharedPoolPhase         = apiv1.SharedPool_Phase
	PDDeploymentMode        = apiv1.PDDeploymentMode
	PDDeploymentStatus      = apiv1.PDDeploymentStatus
	PDDeploymentStatusPhase = apiv1.PDDeploymentStatusPhase
	ObjectStorage           = apiv1.SharedPool_ObjectStorage
	InitSharedPoolConfig    = apiv1.CreateSharedPoolRequest_InitSharedPoolConfig
	SharedPoolPCA           = apiv1.SharedPool_PCA

	TiDBCluster                  = apiv1.Cluster
	PlanReplayerFile             = apiv1.PlanReplayerFile
	RequestUnit                  = apiv1.Cluster_RequestUnit
	AuditLogSetting              = apiv1.Cluster_AuditLogSetting
	SetTiProxyExternalDNSRequest = apiv1.SetTiProxyExternalDNSRequest
	ProviderIdentity             = apiv1.Cluster_AuditLogSetting_ProviderIdentity
	ClusterSecurityConfiguration = apiv1.Cluster_SecurityConfiguration

	TiDBGroupPhase = apiv1.TiDBGroup_Phase
	SQLEndpoint    = apiv1.TiDBGroup_SqlEndpoint
	LoadBalancer   = apiv1.TiDBGroup_SqlEndpoint_LoadBalancer

	IpAllowList           = apiv1.TiDBGroup_SqlEndpoint_LoadBalancer_IpAddress
	SecurityConfiguration = apiv1.TiDBGroup_SecurityConfiguration

	PrivateLinkService    = apiv1.PrivateLinkService
	PrivateLinkConnection = apiv1.PrivateLinkService_Connection

	TargetComponent = apiv1.TargetComponent

	RuConfigComponentsMap = apiv1.RuConfig_ComponentsMap
	RuConfigFeatureFlags  = apiv1.RuConfig_FeatureFlags
	RuRange               = apiv1.RuConfig_RuRange
	RuConfigScope         = apiv1.RuConfig_Scope

	AutoscalePolicy                 = apiv1.AutoscalePolicy
	AutoscalePolicyParams           = apiv1.AutoscalePolicyParams
	AutoscalePolicyLimits           = apiv1.AutoscalePolicyParams_Limits
	AutoscalePolicyThresholds       = apiv1.AutoscalePolicyParams_Thresholds
	AutoscalePolicyActions          = apiv1.AutoscalePolicyParams_Actions
	AutoscalePolicyTiming           = apiv1.AutoscalePolicyParams_Timing
	AutoscalePolicyCalculation      = apiv1.AutoscalePolicyParams_Calculation
	AutoscalePolicySubjectType      = apiv1.AutoscalePolicy_SubjectType
	AutoscalePolicyDirection        = apiv1.AutoscalePolicy_Direction
	AutoscalePolicyCapability       = apiv1.AutoscalePolicy_Capability
	AutoscalePolicyTrigger          = apiv1.AutoscalePolicy_Trigger
	AutoscalePolicyState            = apiv1.AutoscalePolicy_State
	AutoscalePolicyMaterializedFrom = apiv1.AutoscalePolicy_MaterializedFrom
	ResolvedAutoscalePolicyBundle   = apiv1.ResolvedAutoscalePolicyBundle
	EvaluateAutoscalePolicyResponse = apiv1.EvaluateAutoscalePolicyResponse
	ListAutoscalePoliciesResponse   = apiv1.ListAutoscalePoliciesResponse
	AutoscaleRuntimeGuard           = apiv1.AutoscaleRuntimeGuard

	// Policy-shaped RPCs (PR-A: feat/apply-autoscale-policy-rpc).
	// See docs/design/tenant-aware-autoscale-policy-redesign.md
	// §Policy Interface Design.
	AutoscalePolicyScope               = apiv1.AutoscalePolicyScope
	AutoscalePolicySpec                = apiv1.AutoscalePolicySpec
	AutoscalePolicySpecBundle          = apiv1.AutoscalePolicySpecBundle
	AutoscalePolicyResponse            = apiv1.AutoscalePolicyResponse
	AutoscalePolicyApplyTarget         = apiv1.AutoscalePolicyApplyTarget
	AutoscalePolicyApplyStatus         = apiv1.AutoscalePolicyApplyStatus
	AutoscalePolicySpecKey             = apiv1.AutoscalePolicySpecKey
	SyncAutoscalePolicyMode            = apiv1.SyncAutoscalePolicyMode
	PreviewOperationType               = apiv1.PreviewOperationType
	AutoscalePolicyPreviewTarget       = apiv1.AutoscalePolicyPreviewTarget
	AutoscalePolicyFieldDiff           = apiv1.AutoscalePolicyFieldDiff
	AutoscaleRuntimeGuardScopeType     = apiv1.AutoscaleRuntimeGuard_ScopeType
	AutoscaleRuntimeGuardType          = apiv1.AutoscaleRuntimeGuard_GuardType
	ListAutoscaleRuntimeGuardsResponse = apiv1.ListAutoscaleRuntimeGuardsResponse

	ScaleOperationStatus      = apiv1.ScaleOperationStatus
	ScaleOperationEvidence    = apiv1.ScaleOperationEvidence
	PauseResumeOperation      = apiv1.PauseResumeOperation
	PauseResumeOperationStage = apiv1.PauseResumeOperation_Stage

	SetVersionConfigScope = apiv1.SetSharedPoolComponentConfigVersionRequest_Scope
	SetVersionConfigMode  = apiv1.SetSharedPoolComponentConfigVersionRequest_Mode

	CheckPodsResponse = apiv1.CheckPodsResponse

	GetSharedPoolScaleConstraintsResponse = apiv1.GetSharedPoolScaleConstraintsResponse

	DMCluster = apiv1.DMCluster
	DMBackend = apiv1.DMCluster_Backend
	Export    = apiv1.Export

	ComponentHPASpec                = apiv1.ComponentHPASpec
	HorizontalPodAutoscalerSpec     = apiv1.HorizontalPodAutoscalerSpec
	MetricSpec                      = apiv1.MetricSpec
	HorizontalPodAutoscalerBehavior = apiv1.HorizontalPodAutoscalerBehavior
	ObjectMetricSource              = apiv1.ObjectMetricSource
	PodsMetricSource                = apiv1.PodsMetricSource
	ResourceMetricSource            = apiv1.ResourceMetricSource
	ContainerResourceMetricSource   = apiv1.ContainerResourceMetricSource
	ExternalMetricSource            = apiv1.ExternalMetricSource
	MetricIdentifier                = apiv1.MetricIdentifier
	HPAScalingPolicy                = apiv1.HPAScalingPolicy
	MetricTarget                    = apiv1.MetricTarget
	HPAScalingRules                 = apiv1.HPAScalingRules

	ScaleOutConfig = apiv1.ScaleOutConfig

	TiKVCapacityReservation = apiv1.TiKVCapacityReservation
	TiKVSpecUsage           = apiv1.TiKVSpecUsage
	PlacementPolicy         = apiv1.PlacementPolicy

	TemplateVersionInfo   = apiv1.TemplateVersionInfo
	TemplateVersionStatus = apiv1.TemplateVersionStatus
)

// TemplateRegistry is the domain entity for a template version record.
// It embeds TemplateVersionInfo (pb) and adds the Content field needed for rendering.
type TemplateRegistry struct {
	TemplateVersionInfo
	Content string
}

type CassandraProxyState struct {
	ClusterID          string
	SharedPoolID       string
	HostID             string
	NodeSpec           string
	Image              string
	Phase              string
	PrivatelinkDNSName string
	NodeQuotaEntity    *NodeQuota
}

// ScaleOperation holds the public ScaleOperation fields plus internal-only
// state. OriginalQuotaJSON is the pre-mutation NodeQuota snapshot (used by
// cancel to restore min/max); Evidence is shortage/diagnostic evidence
// persisted to evidence_json, never exposed on the public proto message.
type ScaleOperation struct {
	apiv1.ScaleOperation
	OriginalQuotaJSON string
	Evidence          []*ScaleOperationEvidence
}

const (
	CassandraProxyPhaseCreating    = "CREATING"
	CassandraProxyPhaseAvailable   = "AVAILABLE"
	CassandraProxyPhaseModifying   = "MODIFYING"
	CassandraProxyPhaseDeleting    = "DELETING"
	CassandraProxyPhaseUnavailable = "UNAVAILABLE"
)

const (
	DMBackendLegacy     = "legacy"
	DMBackendOperatorV2 = "operator-v2"
)

const (
	DMBackendUnspecifiedProto = apiv1.DMCluster_BACKEND_UNSPECIFIED
	DMBackendLegacyProto      = apiv1.DMCluster_LEGACY
	DMBackendOperatorV2Proto  = apiv1.DMCluster_OPERATOR_V2
)

const (
	// DMGroupNamePrefix is the operator v2 group name prefix; operator appends -dm-master/-dm-worker.
	DMGroupNamePrefix = "dbcluster"

	dmK8sNameMaxLength = 63
)

func GetDMV2Namespace(sharedPoolId string) string {
	return GetDataPlaneNamespace(sharedPoolId)
}

// DMGroupNameV2 returns the TiDB Operator v2 DMGroup/DMWorkerGroup name for a DM cluster.
// DMGroup and DMWorkerGroup are different CRDs and may share the same metadata.name in a namespace;
// operator derives service/cert names as <groupName>-dm-master/-dm-worker[-cluster-secret].
func DMGroupNameV2(clusterID string) (string, error) {
	fullName := fmt.Sprintf("%s-%s", DMGroupNamePrefix, clusterID)
	if len(fullName) <= dmK8sNameMaxLength {
		return fullName, nil
	}
	return "", fmt.Errorf("dm group name %q exceeds max length %d", fullName, dmK8sNameMaxLength)
}

func ComponentPhaseToCassandraProxyPhase(phase ComponentPhase) string {
	switch phase {
	case apiv1.Component_CREATING:
		return CassandraProxyPhaseCreating
	case apiv1.Component_AVAILABLE:
		return CassandraProxyPhaseAvailable
	case apiv1.Component_MODIFYING:
		return CassandraProxyPhaseModifying
	case apiv1.Component_DELETING:
		return CassandraProxyPhaseDeleting
	case apiv1.Component_UNAVAILABLE:
		return CassandraProxyPhaseUnavailable
	default:
		return apiv1.Component_PHASE_UNSPECIFIED.String()
	}
}

func CassandraProxyPhaseToComponentPhase(phase string) ComponentPhase {
	switch phase {
	case CassandraProxyPhaseCreating:
		return apiv1.Component_CREATING
	case CassandraProxyPhaseAvailable:
		return apiv1.Component_AVAILABLE
	case CassandraProxyPhaseModifying:
		return apiv1.Component_MODIFYING
	case CassandraProxyPhaseDeleting:
		return apiv1.Component_DELETING
	case CassandraProxyPhaseUnavailable:
		return apiv1.Component_UNAVAILABLE
	default:
		return apiv1.Component_PHASE_UNSPECIFIED
	}
}

// storage class
const (
	LocalSSD = "local-ssd"
	CloudSSD = "cloud-ssd"
)

// storage mount paths
const (
	// DefaultRaftlogMountPath is the default mount path for raftlog storage
	DefaultRaftlogMountPath = "/var/lib/raft"
)

// storage types
const (
	// volume type for aws
	GP3 = "gp3"
	IO2 = "io2"
	// volume type for azure
	PremiumSSDv2 = "premiumssdv2"
	// volume type for alicloud
	// CloudESSD = "cloud-essd"
	CloudAuto = "cloud-auto"
)

// Minimum storage size constants
const (
	MinTiKVDataStorageSize    uint32 = 50
	MinTiKVRaftlogStorageSize uint32 = 20
	MinTiFlashCNStorageSize   uint32 = 100
	MinTiFlashWNStorageSize   uint32 = 100
)

// DM component types
const (
	DMMasterComponent = "dm-master"
	DMWorkerComponent = "dm-worker"
)

var EnableHAPComponent = []apiv1.ComponentType{
	apiv1.ComponentType_TIDB,
	apiv1.ComponentType_TIDB_WORKER,
	apiv1.ComponentType_TIKV_WORKER,
	apiv1.ComponentType_COPROCESSOR_WORKER,
	apiv1.ComponentType_TIPROXY,
}

type SharedPool struct {
	apiv1.SharedPool
	TemplateVersion int64
	TenantId        string
	DeployType      string
	TiflashMode     string
	// RequestedTiflashMode is request-only. TiflashMode stores the effective persisted architecture marker.
	RequestedTiflashMode apiv1.TiflashMode
	// ConfigCenterMetaProfileId is empty when no Config Center meta profile is used.
	ConfigCenterMetaProfileId string
	CreatedAt                 time.Time
	DeletedAt                 *time.Time
	PauseResumeState          PauseResumeState
	SuspendComputeDesired     bool
	EnableCapacityPlanner     bool
}

type PauseResumeState string

const (
	PauseResumeStateNone     PauseResumeState = ""
	PauseResumeStatePausing  PauseResumeState = "pausing"
	PauseResumeStatePaused   PauseResumeState = "paused"
	PauseResumeStateResuming PauseResumeState = "resuming"
)

func ValidatePauseResumeCombination(sharedPool *SharedPool) error {
	switch sharedPool.PauseResumeState {
	case PauseResumeStateNone:
		if sharedPool.SuspendComputeDesired {
			return fmt.Errorf("invalid pause/resume desired-state combination")
		}
	case PauseResumeStatePausing:
	case PauseResumeStatePaused:
		if !sharedPool.SuspendComputeDesired {
			return fmt.Errorf("invalid pause/resume desired-state combination")
		}
	case PauseResumeStateResuming:
	default:
		return fmt.Errorf("unknown pause/resume state %q", sharedPool.PauseResumeState)
	}
	return nil
}

type TiDBGroup struct {
	apiv1.TiDBGroup
	TemplateVersion int64
	IsDeleted       bool // used in send metering metadata action to check if is deleted
	TopologySpec    *apiv1.TopologySpec
}

type Lock struct {
	Key         string
	TimeoutSecs int32 // get lock timeout
}

type AuditLogConfig struct {
	LogDir    string                `yaml:"logDir,omitempty"`
	BucketURL string                `yaml:"bucketURL,omitempty"`
	Region    string                `yaml:"region,omitempty"`
	AWS       *AuditLogAwsConfig    `yaml:"aws,omitempty"`      // AWS config for audit log
	AliCloud  *AliCloudRoleIdentity `yaml:"alicloud,omitempty"` // AliCloud config for audit log
	Azure     *AuditLogAzureConfig  `yaml:"azure,omitempty"`    // Azure config for audit log
	// TODO: add gcp
	GracefulStopWaitSeconds int32 `yaml:"gracefulStopWaitSeconds,omitempty"`
}

type AuditLogAwsConfig struct {
	RoleARN    string `yaml:"roleARN,omitempty"`
	ExternalID string `yaml:"externalID,omitempty"`
}

type AliCloudRoleIdentity struct {
	RoleARN         string `yaml:"roleARN,omitempty"`
	ExternalID      string `yaml:"externalID,omitempty"`
	RoleSessionName string `yaml:"roleSessionName,omitempty"`
}

type AuditLogAzureConfig struct {
	SASToken string `yaml:"sasToken,omitempty"`
}

type RuConfig struct {
	apiv1.RuConfig
}

const (
	RestoreFailureSourceKernel       = "kernel"
	RestoreFailureSourceControlPlane = "control_plane"
)

type RestoreFailure struct {
	Source  string
	Code    string
	Message string
}

const (
	defaultNamespace = "cluster-ng-service"

	AuditAndSlowLogPath = "/var/log/auditlog"
)

type LightweightRestore struct {
	schema.LightweightRestore
}

func (r *LightweightRestore) GetTargetSharedPoolId() string {
	return r.TargetSharedPoolID
}

func (r *LightweightRestore) GetSourceSharedPoolId() string {
	return r.SourceSharedPoolID
}

func (r *LightweightRestore) GetLastAction() string {
	if r.ExecutedActions == "" {
		return ""
	}
	actions := strings.Split(r.ExecutedActions, ",")
	return actions[len(actions)-1]
}

type ClassicRestore struct {
	schema.ClassicRestore
}

func (r *ClassicRestore) GetLastAction() string {
	if r.ExecutedActions == "" {
		return ""
	}
	actions := strings.Split(r.ExecutedActions, ",")
	return actions[len(actions)-1]
}

type ManualBackup struct {
	schema.ManualBackup
}

func (b *ManualBackup) GetLastAction() string {
	if b.ExecutedActions == "" {
		return ""
	}
	actions := strings.Split(b.ExecutedActions, ",")
	return actions[len(actions)-1]
}

type ManualBackupRestore struct {
	schema.ManualBackupRestore
}

func (r *ManualBackupRestore) GetLastAction() string {
	if r.ExecutedActions == "" {
		return ""
	}
	actions := strings.Split(r.ExecutedActions, ",")
	return actions[len(actions)-1]
}

type SharedPoolObjectStore struct {
	schema.SharedPoolObjectStore
}

type BackupGcSetting struct {
	schema.BackupGcSetting
}

func (b *BackupGcSetting) GetMaxAutoBackupDataRetentionDays() uint32 {
	nums := []uint32{0}
	if b.MaxT1MetaLifetimeDays != nil {
		nums = append(nums, *b.MaxT1MetaLifetimeDays)
	}
	if b.MaxT1WalLifetimeDays != nil {
		nums = append(nums, *b.MaxT1WalLifetimeDays)
	}
	if b.MaxT1PackedLifetimeDays != nil {
		nums = append(nums, *b.MaxT1PackedLifetimeDays)
	}
	if b.MaxT2SstLifetimeDays != nil {
		nums = append(nums, *b.MaxT2SstLifetimeDays)
	}
	if b.MaxT3DataLifetimeDays != nil {
		nums = append(nums, *b.MaxT3DataLifetimeDays)
	}
	return lo.Max(nums)
}
