package entity

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"strings"
)

type (
	DeployType string
	// Cloud is the cloud DBaaS runs in. The laptop is "local"
	CloudProviderStatus  string
	ProviderStatus       string
	RegionStatus         string
	ProfileStatus        string
	ComponentName        string
	ProfileVersionStatus string

	// ServicePlan represents a TiDB Cloud service plan.
	// Use the typed constants and semantic methods instead of raw string comparison.
	ServicePlan string
)

const (
	DeployTypeDedicated DeployType = "dedicated"
	DeployTypeShared    DeployType = "shared"

	ProviderStatusActive   CloudProviderStatus = "active"
	ProviderStatusInactive CloudProviderStatus = "inactive"
	RegionStatusActive     RegionStatus        = "active"
	RegionStatusInactive   RegionStatus        = "inactive"

	ProfileStatusActive   ProfileStatus = "active"
	ProfileStatusInactive ProfileStatus = "inactive"
	ProfileStatusDeleted  ProfileStatus = "deleted"

	PD      ComponentName = "pd"      // Placement Driver
	TiKV    ComponentName = "tikv"    // TiKV
	TiDB    ComponentName = "tidb"    // TiDB
	TiFlash ComponentName = "tiflash" // TiFlash
	TiCDC   ComponentName = "ticdc"   // TiCDC

	ProfileVersionStatusSyncing  ProfileVersionStatus = "syncing"  // this profile has been created but under syncing
	ProfileVersionStatusUsing    ProfileVersionStatus = "using"    // this profile is in use
	ProfileVersionStatusOutdated ProfileVersionStatus = "outdated" // this profile is outdated

	CloudAdminUser = "cloud_admin" // cloudadmin user for controlplane to access customer tidb cluster

	// cluster have Readable/Writeable status in db record
	// uint64 means we can use this flag to control 64 unrelative status
	// 1 (1 << 0) means cluster is read only (from cluster to db). 2 (1 << 1) means cluster need to force to read only (from db to cluster)
	ForceReadOnlyStatus uint64 = 1 << 1
	ReadOnlyStatus      uint64 = 1 << 0
)

const (
	ServicePlanDedicated   ServicePlan = "Dedicated"
	ServicePlanPremium     ServicePlan = "Premium"
	ServicePlanEssentialV2 ServicePlan = "Essential_V2"
	ServicePlanEssential   ServicePlan = "Essential"
	ServicePlanStarter     ServicePlan = "Starter"
)

// IsNextGen reports whether the plan is a NextGen plan.
// Routing and output hierarchy are shared across NextGen plans; metric isolation
// is selected separately by UsesPremiumMetrics and IsServerless.
func (p ServicePlan) IsNextGen() bool {
	switch ServicePlan(strings.ToLower(strings.TrimSpace(string(p)))) {
	case "premium", "essential_v2", "essential", "starter":
		return true
	}
	return false
}

// HasProjectConcept reports whether the plan has a project hierarchy (project_id/project_name).
// Only Dedicated retains the 3-level tenant/project/cluster hierarchy.
func (p ServicePlan) HasProjectConcept() bool {
	switch ServicePlan(strings.ToLower(strings.TrimSpace(string(p)))) {
	case "dedicated":
		return true
	}
	return false
}

// IsInstanceLevel reports whether the plan uses 2-level product hierarchy (instance/tenant).
// Premium/Essential/Starter all use instance/tenant hierarchy instead of project hierarchy.
func (p ServicePlan) IsInstanceLevel() bool {
	return p.IsNextGen()
}

// IsServerless reports whether the plan is a serverless plan (Essential/Starter).
func (p ServicePlan) IsServerless() bool {
	switch ServicePlan(strings.ToLower(strings.TrimSpace(string(p)))) {
	case "essential", "starter":
		return true
	}
	return false
}

// UsesPremiumMetrics reports whether the plan uses Premium O11y metric
// matchers, aggregation, and metric coverage.
func (p ServicePlan) UsesPremiumMetrics() bool {
	switch ServicePlan(strings.ToLower(strings.TrimSpace(string(p)))) {
	case "premium", "essential_v2":
		return true
	}
	return false
}

// SupportsCDC reports whether the plan exposes CDC metrics.
// Essential and Starter currently have no CDC capability, while Dedicated/Premium do.
func (p ServicePlan) SupportsCDC() bool {
	switch ServicePlan(strings.ToLower(strings.TrimSpace(string(p)))) {
	case "dedicated", "premium", "essential_v2":
		return true
	}
	return false
}

func (p ServicePlan) IsSupported() bool {
	if strings.TrimSpace(string(p)) == "" {
		return false
	}

	_, err := NormalizeServicePlan(string(p))
	return err == nil
}

func NormalizeServicePlan(servicePlan string) (ServicePlan, error) {
	normalized := strings.TrimSpace(servicePlan)
	if normalized == "" {
		return ServicePlanDedicated, nil
	}

	switch strings.ToLower(normalized) {
	case "dedicated":
		return ServicePlanDedicated, nil
	case "premium":
		return ServicePlanPremium, nil
	case "essential_v2":
		return ServicePlanEssentialV2, nil
	case "essential":
		return ServicePlanEssential, nil
	case "starter":
		return ServicePlanStarter, nil
	default:
		return "", fmt.Errorf("unsupported service plan %q", servicePlan)
	}
}

func StringToComponentName(in string) (ComponentName, error) {
	var cn ComponentName
	switch strings.ToLower(in) {
	case "pd":
		cn = PD
	case "tikv", "kv":
		cn = TiKV
	case "tidb", "mysql":
		cn = TiDB
	case "tiflash":
		cn = TiFlash
	case "ticdc":
		cn = TiCDC
	default:
		return cn, errors.New("could not parse as ComponentName: " + in)
	}
	return cn, nil
}

type (
	// enum
	TenantStatus          string
	ProjectStatus         string
	TenantPlan            string
	NotificationScene     string
	TenantKind            string
	ClusterStatus         string
	UserStatus            string
	UserRole              string
	RestoreStatus         string
	ImportStatus          string
	ClusterConnectionType string
	BackupStatus          string
	BackupType            string
	BackupMethod          string
	CDCTaskStatus         string
	CDCDownStreamType     string
	PeeringType           string
	ReplicationType       string
	ReplicationStatus     string
	CDCStatus             string
	DetachType            string
	TrialType             string
	DevTierIdleStage      string // the idle stage of dev tier, such as: warning, shutdown
	DevTierTrialStage     string // the ending stage of dev tier, such as: one-month before end
	DataFormat            uint32
	DataSource            uint32
	PeeringStatus         string // the status of aws/gcp vpc peering
	CDCMeteringStatus     string

	ClusterOperationType         string
	ClusterOperationStatus       string
	ScaleOperationTrackingStatus string

	DiscountType      uint32
	KafkaProtocolType string
	// Cloud is the cloud DBaaS runs in. The laptop is "local"
	Cloud string

	// Integration types
	IntegrationType    string
	IntegrationStatus  string
	IntegrationPayload string
	DatadogSite        string
	NewrelicSite       string
	UserAlertType      string
)

func (s ImportStatus) String() string {
	return string(s)
}

// enums
const (
	ClusterConnectionTypePrivate ClusterConnectionType = "private"
	ClusterConnectionTypePublic  ClusterConnectionType = "public"

	// ClusterStatusInitializing initialize the infra.
	ClusterStatusInitializing ClusterStatus = "initializing"
	// ClusterStatusPreparing provisioning nodes
	ClusterStatusPreparing ClusterStatus = "preparing"
	// ClusterStatusLaunching set up the tidb cluster
	ClusterStatusLaunching ClusterStatus = "launching"
	ClusterStatusNormal    ClusterStatus = "normal"
	ClusterStatusScaling   ClusterStatus = "scaling"
	// ClusterStatusUpgrading  ClusterStatus = "upgrading"
	ClusterStatusRecovering ClusterStatus = "recovering"
	// ClusterStatusDeleted indicate cluster is deleting, cluster
	// worker will list cluster in deleted status, then delete all resources
	ClusterStatusDeleted ClusterStatus = "deleted"
	// ClusterStatusCleared indicate all reosurce already clean up
	ClusterStatusCleared ClusterStatus = "cleared"
	// ClusterStatusDeletecomplete indicate cluster resources already deleted, but backup cleanup worker still in the process
	ClusterStatusDeletecomplete ClusterStatus = "deletecomplete"

	CDCMeteringStatusInit   CDCMeteringStatus = "init"
	CDCMeteringStatusDone   CDCMeteringStatus = "done"
	CDCMeteringStatusFailed CDCMeteringStatus = "failed"

	TenantStatusPending  TenantStatus = "pending"
	TenantStatusActive   TenantStatus = "active"
	TenantStatusInactive TenantStatus = "inactive"
	TenantStatusDeleted  TenantStatus = "deleted"
	TenantStatusCleared  TenantStatus = "cleared"

	TenantPlanNewUser   TenantPlan = "new_user"
	TenantPlanFreeTrial TenantPlan = "free_trial"
	TenantPlanPOC       TenantPlan = "poc"
	TenantPlanOnDemand  TenantPlan = "on_demand"

	ProjectStatusActive  ProjectStatus = "active"
	ProjectStatusDeleted ProjectStatus = "deleted"
	ProjectStatusCleared ProjectStatus = "cleared"

	DevTierTrialStageNormal           DevTierTrialStage = "normal" // farthest to end
	DevTierTrialStageCloseToEndStage1 DevTierTrialStage = "close_to_end_stage1"
	DevTierTrialStageCloseToEndStage2 DevTierTrialStage = "close_to_end_stage2"
	DevTierTrialStageEnd              DevTierTrialStage = "end"

	DevTierIdleStageActive    DevTierIdleStage = "active"
	DevTierIdleStageEncourage DevTierIdleStage = "encourage"
	DevTierIdleStageWarning   DevTierIdleStage = "warning"
	DevTierIdleStageShutdown  DevTierIdleStage = "shutdown"

	NotificationSceneOptional                 NotificationScene = "optional"
	NotificationSceneOptional2                NotificationScene = "optional2" // secondary welcome email
	NotificationScenePocWillEnd               NotificationScene = "poc_will_end"
	NotificationScenePocClusterWillEndIn3Days NotificationScene = "poc_cluster_will_end_in_3_days"
	NotificationScenePocClusterEnd            NotificationScene = "poc_cluster_end"
	NotificationScenePocEndBackupWillDelete   NotificationScene = "poc_end_backup"
	NotificationSceneClusterNetworkThreshold  NotificationScene = "cluster_network_threshold"
	NotificationSceneDevTierTrial             NotificationScene = "dev_tier_trial"
	NotificationSceneDevTierIdle              NotificationScene = "dev_tier_idle"
	TenantKindInternal                        TenantKind        = "internal"
	TenantKindExternal                        TenantKind        = "external"

	UserStatusActive   UserStatus = "active"
	UserStatusPending  UserStatus = "pending"
	UserStatusInactive UserStatus = "inactive"

	UserRoleOwner        UserRole = "owner"
	UserRoleMember       UserRole = "member"
	UserRoleBillingAdmin UserRole = "billing_admin"
	UserRoleAuditAdmin   UserRole = "audit_admin"

	ImportStatusUnknown   ImportStatus = "unknown"
	ImportStatusPreparing ImportStatus = "preparing"
	ImportStatusImporting ImportStatus = "importing"
	ImportStatusCompleted ImportStatus = "completed"
	ImportStatusFailed    ImportStatus = "failed"
	ImportStatusCanceling ImportStatus = "canceling"
	ImportStatusCanceled  ImportStatus = "canceled"

	DataSourceTypeUnknown DataSource = 0
	DataSourceTypeS3      DataSource = 1
	DataSourceTypeGCS     DataSource = 2

	DataFormatUnknown    DataFormat = 0
	DataFormatSQL        DataFormat = 1
	DataFormatParquet    DataFormat = 2
	DataFormatCustomCSV  DataFormat = 3
	DataFormatRawParquet DataFormat = 4

	PingCAPTenantID ID = 1

	// Backup Method
	BackupMethod_Full        BackupMethod = "full"
	BackupMethod_Incremental BackupMethod = "incremental"

	// Backup Type
	BackupType_All    BackupType = "all"
	BackupType_Auto   BackupType = "auto"
	BackupType_Manual BackupType = "manual"

	// Backup Status
	BackupStatus_PENDING BackupStatus = "pending"
	BackupStatus_RUNNING BackupStatus = "running"
	BackupStatus_FAILED  BackupStatus = "failed"
	BackupStatus_SUCCESS BackupStatus = "success"

	// Restore Status
	RestoreStatus_PENDING RestoreStatus = "pending"
	RestoreStatus_RUNNING RestoreStatus = "running"
	RestoreStatus_FAILED  RestoreStatus = "failed"
	RestoreStatus_SUCCESS RestoreStatus = "success"

	ClusterOperationTypeScale       ClusterOperationType         = "scale"
	ClusterOperationStatusCreated   ClusterOperationStatus       = "created"
	ClusterOperationStatusDone      ClusterOperationStatus       = "done"
	ScaleOperationTrackingScaleInit ScaleOperationTrackingStatus = "scale-init"
	ScaleOperationTrackingScalePod  ScaleOperationTrackingStatus = "scale-pod"
	ScaleOperationTrackingScaleNode ScaleOperationTrackingStatus = "scale-node"
	ScaleOperationTrackingDone      ScaleOperationTrackingStatus = "done"

	Normal DiscountType = 1

	// CDC task Status
	CDCTaskStatusNormal   CDCTaskStatus = "normal"
	CDCTaskStatusStopped  CDCTaskStatus = "stopped"
	CDCTaskStatusRemoved  CDCTaskStatus = "removed"
	CDCTaskStatusFinished CDCTaskStatus = "finished"

	// CDC DownStream Type
	CDCDownStreamTypeMysql CDCDownStreamType = "mysql"
	CDCDownStreamTypeTiDB  CDCDownStreamType = "tidb"
	CDCDownStreamTypeKafka CDCDownStreamType = "kafka"
	CDCDownStreamTypeAll   CDCDownStreamType = "all"

	// Replication Type
	ReplicationTypeCdc = "cdc"

	// Replication Status
	ReplicationStatusCreating    ReplicationStatus = "creating"
	ReplicationStatusReplicating ReplicationStatus = "replicating"
	ReplicationStatusDetaching   ReplicationStatus = "detaching"
	ReplicationStatusDetached    ReplicationStatus = "detached"
	ReplicationStatusFailed      ReplicationStatus = "failed"
	ReplicationStatusPause       ReplicationStatus = "pause"

	// CDC Status
	CDCStatusCreating CDCStatus = "creating"
	CDCStatusNormal   CDCStatus = "normal"
	CDCStatusDeleting CDCStatus = "deleting"
	CDCStatusDeleted  CDCStatus = "deleted"
	CDCStatusFailed   CDCStatus = "failed"
	CDCStatusPause    CDCStatus = "pause"

	//Peering Type
	PeeringTypeDBaaS    PeeringType = "dbaas"
	PeeringTypeCustomer PeeringType = "customer"

	// Detach Type
	DetachTypePlanned DetachType = "planned"
	DetachTypeForce   DetachType = "force"
	DetachTypeNone    DetachType = "none"

	// Trial Type
	TrialTypeDevTier TrialType = "dev_tier"
	TrialTypePOC     TrialType = "poc"

	KafkaProtocolTypeDefault   = "default"
	KafkaProtocolTypeAvro      = "avro"
	KafkaProtocolTypeMaxwell   = "maxwell"
	KafkaProtocolTypeCanalJson = "canal-json"

	// Peering Status
	PeeringStatusActive PeeringStatus = "Active"

	// DatadogIntegration enum values
	IntegrationTypeUnspecified IntegrationType = "UNSPECIFIED"
	IntegrationTypeDatadog     IntegrationType = "DATADOG"
	IntegrationTypePrometheus  IntegrationType = "PROMETHEUS"
	IntegrationTypeNewrelic    IntegrationType = "NEWRELIC"

	IntegrationStatusUnspecified IntegrationStatus = "UNSPECIFIED"
	IntegrationStatusNone        IntegrationStatus = "NONE"
	IntegrationStatusActive      IntegrationStatus = "ACTIVE"
	IntegrationStatusError       IntegrationStatus = "ERROR"

	IntegrationPayloadUnspecified IntegrationPayload = "UNSPECIFIED"
	IntegrationPayloadMetrics     IntegrationPayload = "METRICS"
	IntegrationPayloadLogs        IntegrationPayload = "LOGS"

	DatadogSiteUnspecified DatadogSite = "UNSPECIFIED"
	DatadogSiteUS1         DatadogSite = "US1"
	DatadogSiteUS3         DatadogSite = "US3"
	DatadogSiteUS5         DatadogSite = "US5"
	DatadogSiteEU1         DatadogSite = "EU1"
	DatadogSiteUS1FED      DatadogSite = "US1_FED"
	DatadogSiteAP1         DatadogSite = "AP1"

	NewrelicSiteUnspecified NewrelicSite = "UNSPECIFIED"
	NewrelicSiteUSOTLP      NewrelicSite = "US_OTLP"
	NewrelicSiteEUOTLP      NewrelicSite = "EU_OTLP"

	UserAlertTypeUnspecified UserAlertType = "UNSPECIFIED"
	UserAlertTypeEmail       UserAlertType = "EMAIL"
	UserAlertTypeWebhook     UserAlertType = "WEBHOOK"
)

const (
	// Gcp is the GCP cloud
	Gcp = Cloud("gcp")

	// Aws is the AWS cloud
	Aws = Cloud("aws")
)

// Scan is for gorm
func (u *Cloud) Scan(value interface{}) error {
	*u = Cloud(value.([]byte))
	return nil
}

// Value is for gorm
func (u Cloud) Value() (driver.Value, error) { return string(u), nil }

func (u Cloud) String() string {
	return string(u)
}

func IsValidProvider(provider Cloud) bool {
	switch provider {
	case Aws, Gcp:
		return true
	default:
		return false
	}
}
