package model

import (
	"database/sql/driver"
	"encoding/json"
	"time"

	"gorm.io/gorm"
)

//go:generate goqueryset -in model.go

var _ = []interface{}{
	//cluster profiles
	&Provider{}, &Region{}, &Zone{},

	// simplify cluster tier
	&InfraProvider{}, &InfraCompute{}, &InfraStorage{}, &Sku{},
	&TierChange{}, &TierUnit{}, &Tier{},

	//tenant
	&Quota{}, &QuotaChangeLog{},
	&APIKey{},

	&ProjectNetwork{},

	//cluster
	&Cluster{}, &Connection{}, &ClusterOperation{}, &ScaleOperationTracking{},
	&ClusterComponent{}, &ClusterSnapshot{},

	//old schema
	&ClusterCidr{}, &GcpVpcPeering{}, &AwsVpcPeering{},

	// integration
	&ProjectMetering{}, &ClusterMetering{}, &BillingReportRecord{},

	// cross account
	&IamRole{}, &CrossAccountDeployment{}, &IamRoleUsage{},

	&CoreEvent{}, &ClusterJob{},

	// Customer-Managed Encryption Keys
	AwsCmek{},

	// Maintenance Window
	MaintenanceWindow{}, MaintenanceTask{}, MaintenanceTaskExecution{},
}

// gen:qs
type (
	Provider struct {
		Updatable
		Versioned

		Name    Cloud               `gorm:"uniqueIndex:idx_name;not null;type:ENUM('aws', 'gcp')"`
		Regions []*Region           `gorm:"foreignkey:ProviderID"`
		Status  CloudProviderStatus `gorm:"not null;type:ENUM('active', 'inactive')"`
	}

	Region struct {
		Updatable
		Versioned

		Name              string          `gorm:"type:VARCHAR(255);uniqueIndex:idx_name_provider;not null" valid:"required,lowercase"`
		DisplayName       string          `gorm:"type:VARCHAR(255);not null"`
		ProviderID        ID              `gorm:"index;uniqueIndex:idx_name_provider;not null" valid:"required"`
		Provider          *Provider       `gorm:"foreignkey:ProviderID"`
		Zones             []*Zone         `gorm:"foreignkey:RegionID"`
		CountryCode       string          `gorm:"type:CHAR(4)"` // country code(https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). such as: CN, US
		Area              string          `gorm:"type:VARCHAR(63)"`
		Priority          int32           `gorm:"not null;default:999999"`
		Status            RegionStatus    `gorm:"not null;type:ENUM('active', 'inactive')"`
		IsPublic          *bool           `gorm:"default:true"`
		RegionTenants     []*RegionTenant `gorm:"foreignkey:RegionID"`
		DefaultBackupTime string          `gorm:"type:VARCHAR(255);not null;default:'00:00'"`
	}

	RegionTenant struct {
		RegionID ID `gorm:"not null;"`
		TenantID ID `gorm:"not null;"`
	}

	// TODO(chenfei): we should remove zone entity from platform
	Zone struct {
		Updatable

		Name        string  `gorm:"type:VARCHAR(255);uniqueIndex:idx_name_region;not null"`
		DisplayName string  `gorm:"type:VARCHAR(255);not null"`
		RegionID    ID      `gorm:"index;uniqueIndex:idx_name_region;not null"`
		Region      *Region `gorm:"foreignkey:RegionID"`
	}

	InfraProvider struct {
		Updatable
		Versioned

		// Infra API provider type: aws | gcp | aws-free-tier
		Name       string     `gorm:"type:VARCHAR(255);uniqueIndex:idx_name;not null"`
		DeployType DeployType `gorm:"uniqueIndex:deploy_type_provider,priority:1;not null;type:ENUM('dedicated', 'shared')"`
		ProviderID ID         `gorm:"uniqueIndex:deploy_type_provider,priority:2;not null"`
		Provider   *Provider  `gorm:"foreignkey:ProviderID"`
	}

	InfraCompute struct {
		Updatable
		Versioned

		// r5.xlarge, aws_freetier_10
		InstanceType       string         `gorm:"uniqueIndex:provider_instance,priority:2;type:VARCHAR(255);not null"`
		InfraProvider      *InfraProvider `gorm:"foreignkey:InfraProviderID"`
		InfraProviderID    ID             `gorm:"uniqueIndex:provider_instance,priority:1;not null"`
		CPUNum             uint32         `gorm:"not null"`
		MemorySizeGi       uint32         `gorm:"not null"`
		LocalStorageSizeGi uint32         `gorm:"not null"`
		Regions            []Region       `gorm:"many2many:infra_compute_regions;"`
		CpuArch            ArchType       `gorm:"type:ENUM('amd64', 'arm64', '');default:'amd64'"`
	}

	InfraStorage struct {
		Updatable
		Versioned

		InfraProvider   *InfraProvider `gorm:"foreignkey:InfraProviderID"`
		InfraProviderID ID             `gorm:"uniqueIndex:provider_name,priority:1;not null"`
		ClassName       string         `gorm:"type:VARCHAR(255);uniqueIndex:provider_name,priority:2;not null"` // such as: tikv-gp3-1000-300
		InstanceType    string         `gorm:"type:VARCHAR(255);not null"`                                      // such as: gp2, gp3, pd-ssd, local-storage
		IOPS            uint32         `gorm:"not null"`
		ThroughputMi    uint32         `gorm:"not null"`
	}

	Sku struct {
		Updatable

		VersionID ID     `gorm:"not null;uniqueIndex:uniq_version_name,priority:1"`
		Name      string `gorm:"type:VARCHAR(255);uniqueIndex:uniq_version_name,priority:2;not null"`
		Price     string `gorm:"type:VARCHAR(255);not null"`
		Extra     string `gorm:"type:VARCHAR(4095);default:'{}'"`
	}

	TierUnit struct {
		Updatable
		Versioned

		TierUnitKey      string         `gorm:"type:VARCHAR(255);uniqueIndex:uniq_key;not null"`
		Memo             string         `gorm:"type:text"`
		DefaultNodeNum   int            `gorm:"not null"`
		DeployType       DeployType     `gorm:"type:ENUM('dedicated', 'shared');not null"`
		ComponentType    ComponentName  `gorm:"type:ENUM('pd', 'tikv', 'tidb', 'tiflash', 'ticdc', 'tiproxy');not null" `
		MinStorageGi     int            `gorm:"not null"`
		MaxStorageGi     int            `gorm:"not null"`
		DefaultStorageGi int            `gorm:"not null"`
		ProviderID       ID             `gorm:"not null"`
		Provider         *Provider      `gorm:"foreignkey:ProviderID"`
		InfraProviderID  ID             `gorm:"not null"`
		InfraProvider    *InfraProvider `gorm:"foreignkey:InfraProviderID"`
		InfraComputeID   ID             `gorm:"not null"`
		InfraCompute     *InfraCompute  `gorm:"foreignkey:InfraComputeID"`
		InfraStorageID   ID             `gorm:"not null"`
		InfraStorage     *InfraStorage  `gorm:"foreginkey:InfraStorageID"`
		PD               *PDTier        `gorm:"type:JSON;column:pd"`
		Regions          []*Region      `gorm:"many2many:tier_unit_regions;"`
	}

	TierUnitMappingItem struct {
		ProviderName string   `json:"provider_name"`
		RegionNames  []string `json:"region_names"`
		TenantIDs    []string `json:"tenant_ids"` // since the type of tenant_id is uint64, we MUST use string to store it
		TierUnitKey  string   `json:"tier_unit_key"`
	}

	TierUnitMapping struct {
		Items []*TierUnitMappingItem `json:"items"`
	}

	Tier struct {
		Updatable
		Versioned

		TierKey         string          `gorm:"type:VARCHAR(255);uniqueIndex:uniq_key;not null"`
		Name            string          `gorm:"type:VARCHAR(255);not null"`
		DisplayName     string          `gorm:"type:VARCHAR(255);not null"`
		Memo            string          `gorm:"type:text"`
		DeployType      DeployType      `gorm:"type:ENUM('dedicated', 'shared');not null"`
		ComponentType   ComponentName   `gorm:"type:ENUM('pd', 'tikv', 'tidb', 'tiflash', 'ticdc', 'tiproxy');not null" `
		Default         bool            `gorm:"not null"`
		CPUNum          int             `gorm:"not null"`
		MemorySizeGi    int             `gorm:"not null"`
		TierUnitMapping TierUnitMapping `gorm:"type:JSON"`
	}

	TierChange struct {
		Updatable
		Versioned

		ChangeID string           `gorm:"type:VARCHAR(255);uniqueIndex:uniq_cid;not null"`
		Status   TierChangeStatus `gorm:"not null;type:ENUM('created','applied')"`
	}
)

// gen:qs
type (
	ProjectNetwork struct {
		Updatable
		Versioned

		ProjectID  ID        `gorm:"index:idx_project_id_provider,unique,priority:1;not null"`
		ProviderID ID        `gorm:"index:idx_project_id_provider,unique,priority:2;not null" valid:"required"`
		Provider   *Provider `gorm:"foreignkey:ProviderID"`
		CIDR       string    `gorm:"type:VARCHAR(255);column:cidr"`
	}

	Quota struct {
		Updatable

		TenantID      ID            `gorm:"index;not null"`
		ComponentName ComponentName `gorm:"type:ENUM('pd', 'tikv', 'tidb', 'tispark', 'tiflash', 'ticdc', 'tiproxy');not null"`
		Quota         uint32        `gorm:"not null"`
	}

	QuotaChangeLog struct {
		Base

		QuotaID     ID     `gorm:"index;not null"`
		Quota       Quota  `gorm:"foreignkey:QuotaID"`
		NewQuota    uint64 `gorm:"not null"`
		ChangedByID ID     `gorm:"not null"`
	}

	IamRole struct {
		Updatable
		Versioned

		ProjectID ID              `gorm:"not null;uniqueIndex:uniq_pid_arn,priority:1;"`
		Arn       string          `gorm:"type:VARCHAR(512);not null;uniqueIndex:uniq_pid_arn,priority:2;"`
		Usages    []*IamRoleUsage `gorm:"foreignkey:RoleID"`
	}

	IamRoleUsage struct {
		Updatable
		Versioned

		ProjectID              ID                      `gorm:"index;not null;"`
		RoleID                 ID                      `gorm:"index;not null"`
		UsedByID               ID                      `gorm:"not null"` // only crossAccountDeploymentID now
		CrossAccountDeployment *CrossAccountDeployment `gorm:"foreignkey:UsedByID"`
	}

	CrossAccountDeployment struct {
		Updatable
		Versioned

		TenantID  ID `gorm:"index;not null"`
		ProjectID ID `gorm:"not null;uniqueIndex:unq_pid_rid_cpid,priority:1;"`
		// Cross account deployment belongs to a specific region.
		ProviderID ID                   `gorm:"not null;uniqueIndex:unq_pid_rid_cpid,priority:3;"`
		Provider   *Provider            `gorm:"foreignkey:ProviderID"`
		RegionID   ID                   `gorm:"not null;uniqueIndex:unq_pid_rid_cpid,priority:2;"`
		Region     *Region              `gorm:"foreignkey:RegionID"`
		Status     XAccountDeployStatus `gorm:"type:ENUM('active','deleting');not null;default:'active'"`

		// Network
		VpcID           string    `gorm:"type:VARCHAR(512);not null;"`
		SecurityGroupID string    `gorm:"type:VARCHAR(512);not null;"`
		SubnetIDs       SubnetIDs `gorm:"type:JSON;not null;"`
		// Backup
		BucketName string `gorm:"type:VARCHAR(512);not null;"`
		// Security
		KmsKeyArn               string `gorm:"type:VARCHAR(1024);not null;"`
		PcaArnClusterComponents string `gorm:"type:VARCHAR(1024);not null;"`
		PcaArnClient            string `gorm:"type:VARCHAR(1024);not null;"`
		// IAM Roles
		NodeRoleID        ID       `gorm:"not null"`
		NodeRole          *IamRole `gorm:"foreignkey:NodeRoleID"`
		EKSRoleID         ID       `gorm:"not null"`
		EKSRole           *IamRole `gorm:"foreignkey:EKSRoleID"`
		TidbServiceRoleID ID       `gorm:"not null"`
		TidbServiceRole   *IamRole `gorm:"foreignkey:TidbServiceRoleID"`
		// mTLS x509 certs info
		IsMTLSOn bool   `gorm:"default:false"`
		CACert   string `gorm:"type:TEXT"`
		Cert     string `gorm:"type:TEXT"`
		Key      string `gorm:"type:TEXT"`
	}

	Cidr struct {
		Updatable
		DeletedAt gorm.DeletedAt `gorm:"index"`

		ProjectID  ID      `gorm:"not null;uniqueIndex:project_region,priority:1;"`
		RegionID   ID      `gorm:"not null;uniqueIndex:project_region,priority:2;"`
		Region     *Region `gorm:"foreignkey:RegionID"`
		CIDR       string  `gorm:"type:VARCHAR(255);column:cidr"`
		ProviderID ID      `gorm:"index;not null"`
		TenantID   ID      `gorm:"index"`
	}
)

// gen:qs
type (
	ClusterAttributes struct {
		TokenBasedAuthEnabled   *bool `json:"token_based_auth_enabled"`
		WithBuiltinCDCAdminRole *bool `json:"with_builtin_cdc_admin_role"`
	}

	Cluster struct {
		Updatable
		Versioned
		DeletedTime          *time.Time `gorm:"type:TIMESTAMP;deleted_time"`
		CreationFinishedTime *time.Time `gorm:"type:TIMESTAMP;creation_finished_time"`

		Name                        string              `gorm:"type:VARCHAR(255);index:name_tenant;not null"`
		TenantID                    ID                  `gorm:"index:name_tenant;not null"`
		CreatedByID                 ID                  `gorm:"index;not null"`
		Status                      ClusterStatus       `gorm:"type:ENUM('created', 'initializing', 'preparing', 'volume_snapshot_restoring', 'launching', 'normal', 'hibernated', 'pausing', 'paused', 'resuming', 'scaling', 'upgrading','recovering', 'deleted', 'cleared', 'deletecomplete');not null"`
		EncryptedRootPassword       string              `gorm:"type:VARCHAR(1024);not null;default ''"`
		EncryptedCloudAdminPassword string              `gorm:"type:VARCHAR(1024);not null;default ''"`
		PasswordProviderID          string              `gorm:"type:VARCHAR(255);default:''"`
		ComponentsV2                []*ClusterComponent `gorm:"foreignkey:ClusterID"`
		Connections                 []Connection        `gorm:"foreignkey:ClusterID"`
		ClusterJobs                 []*ClusterJob       `gorm:"foreignkey:ClusterID"`
		Replicas                    uint                `gorm:"not null"`
		ProviderID                  ID                  `gorm:"not null"`
		Provider                    *Provider           `gorm:"foreignkey:ProviderID"`
		RegionID                    ID                  `gorm:""` // TODO(chenfei): get cluster region by this field to instead get component's region
		Region                      *Region             `gorm:"foreignkey:RegionID"`
		DeployType                  DeployType          `gorm:"type:ENUM('dedicated', 'shared')"`
		FinalBackupBeforeDelete     bool                `gorm:"column:poc_deleted;default:false"` // indicate the worker which clean deleted cluster have to make a final-backup before delete TODO(chenfei): rename column name
		Free                        bool                `gorm:"default:false"`
		ProjectID                   ID                  `gorm:"index;not null"`
		HasSetPwd                   *bool               `gorm:"default:true"`
		FirstClusterInProjectRegion *bool               `gorm:""`

		// Not null if it's importing.
		// we set this as the importing id when user create an import
		// and set as null when user exit the importing mode
		ImportID *uint64 `gorm:"index"`

		// Deprecated: no longer to use. TODO(chenfei): remove it later
		// Not null if replication is connection
		// set it when the replication is creating
		// delete it when the replication is detached
		ReplicationID *uint64 `gorm:"index"`

		// the status of the readonly
		// 1 means read only status
		// 2 means force set cluster read only in db side
		// for the readonly status is sync from cluster to db, so the change from `bool` to `uint64` can compatible with exist clusters
		ReadOnly uint64 `gorm:"default:0"`

		// Add per cluster salt for security related works
		Salt []byte `gorm:"type:BINARY(8);default:null"`

		// Not null if this cluster is a cross account deployment.
		CrossAccountDeploymentID ID                      `gorm:"index"`
		CrossAccountDeployment   *CrossAccountDeployment `gorm:"foreignkey:CrossAccountDeploymentID"`

		// not null if this cluster is restored from a backup
		RestoreID *uint64 `gorm:"index"`

		// RecoveryMode means cluster should enter recover mode for volume-snapshot or not.
		// if we're using volume-snapshot to create cluster this should be true.
		RecoveryMode bool `gorm:"default:false"`

		// whether the cluster is created in the new arch (EKS/GKE enabled).
		// as the feature gate config in "oneconfig" may be changed when migrating network from gardener to eks/gke,
		// we need to record the cluster's real arch in meta DB.
		OnNewArch bool `gorm:"default:false"`

		// InitialVersion specifies cluster version when create cluster
		InitialVersion string            `gorm:"type:VARCHAR(16);default:''"`
		Attributes     ClusterAttributes `gorm:"type:JSON;not null"`
	}

	ClusterConfig struct {
		Updatable

		ClusterID ID       `gorm:"unique;not null"`
		Cluster   *Cluster `gorm:"foreignkey:ClusterID"`

		// the status of the audit log: on or off
		IsAuditLogOn   bool   `gorm:"default:false"`
		AuditLogConfig string `gorm:"type:JSON;not null"`
	}

	// cluster connections
	Connection struct {
		Updatable

		ClusterID     ID                    `gorm:"index;not null"`
		Cluster       *Cluster              `gorm:"foreignkey:ClusterID"`
		Type          ClusterConnectionType `gorm:"type:ENUM('private','public');not null"`
		DSN           string                `gorm:"type:VARCHAR(255);not null"` // should rename to host
		Port          int32                 `gorm:"default:4000;not null"`
		CreateSuccess *bool
	}

	ClusterOperation struct {
		Updatable

		ClusterID   ID                     `gorm:"index;not null"`
		Cluster     *Cluster               `gorm:"foreignkey:ClusterID"`
		OperatorID  ID                     `gorm:"not null; default: 0"`
		Type        ClusterOperationType   `gorm:"type:ENUM('scale', 'pause', 'resume', 'final-backup');not null"`
		CompletedAt *time.Time             `gorm:"type:TIMESTAMP"`
		Status      ClusterOperationStatus `gorm:"type:ENUM('created','running','done');not null"`
		Content     string                 `gorm:"type:TEXT;not null"`

		// snapshot before the operation, only used for scaling now
		SnapshotID ID   `gorm:"index;not null"`
		Force      bool `gorm:"default:false"` // force scale operation
	}

	ScaleOperationTracking struct {
		Updatable

		ClusterOperationID ID                           `gorm:"index;not null"`
		ClusterOperation   *ClusterOperation            `gorm:"foreignkey:ClusterOperationID"`
		Status             ScaleOperationTrackingStatus `gorm:"type:ENUM('scale-init', 'scale-pod','scale-node','done');not null"`
		Replicas           uint32
		StorageChanged     uint32            `gorm:"column:storage_changed"`
		ClusterComponentID ID                `gorm:"index;not null"`
		ClusterComponent   *ClusterComponent `gorm:"foreignkey:ClusterComponentID"`
	}

	TrafficFilters struct {
		Cidrs []TrafficFilter `json:"cidrs"`
	}

	TrafficFilter struct {
		Cidr        string `gorm:"type:VARCHAR(255)" json:"cidr"`
		Description string `gorm:"type:VARCHAR(255)" json:"description"`
	}

	ClusterCidr struct {
		Updatable

		ClusterID       ID                  `gorm:"uniqueIndex:uniq_cid;not null"`
		DisablePublicLB *bool               `gorm:"NOT NULL;default:false"`
		Cidr            TrafficFilters      `gorm:"type:JSON"`
		Status          TrafficFilterStatus `gorm:"not null;type:ENUM('synced', 'unsynced');default:'synced'"`
	}

	// Gcp Vpc Peering
	GcpVpcPeering struct {
		Updatable
		Versioned

		Region            string `gorm:"type:VARCHAR(255);index:project_region,priority:2"`
		TenantProjectID   string `gorm:"type:VARCHAR(255);not null"`
		TenantNetworkName string `gorm:"type:VARCHAR(255);not null"`
		TenantVpcRegion   string `gorm:"type:VARCHAR(255);not null"`
		TenantVpcCIDR     string `gorm:"type:VARCHAR(255);not null"`
		NetworkName       string `gorm:"type:VARCHAR(255);not null"`
		Status            string `gorm:"type:VARCHAR(255);not null"`
		// according peeriing type, decide to accept the vpc peering request by dbaas worker or customer
		ProjectID   ID          `gorm:"index:project_region,priority:1"`
		PeeringType PeeringType `gorm:"type:ENUM('dbaas','customer','customer_readonly');not null;default:'customer'"`
	}

	// Aws Vpc Peering
	AwsVpcPeering struct {
		Updatable
		Versioned

		Region          string `gorm:"type:VARCHAR(255);index:project_region,priority:2"`
		TenantAccountID string `gorm:"type:VARCHAR(255);not null"`
		TenantRegion    string `gorm:"type:VARCHAR(255);not null"`
		TenantVpcID     string `gorm:"type:VARCHAR(255);not null"`
		TenantVpcCIDR   string `gorm:"type:VARCHAR(255);not null"`
		VpcPeeringID    string `gorm:"type:VARCHAR(255);not null"`
		PrivateHostZone string `gorm:"type:VARCHAR(255);not null"`
		Status          string `gorm:"type:VARCHAR(255);not null"`
		ProjectID       ID     `gorm:"index:project_region,priority:1"`
		// according peering type, decide to accept the vpc peering request by dbaas worker or customer
		PeeringType PeeringType `gorm:"type:ENUM('dbaas','customer','customer_readonly');not null;default:'customer'"`
	}

	// Including both AWS and GCP
	AwsPrivateLinkEndpoint struct {
		Updatable
		Versioned

		TenantID      ID                      `gorm:"not null"`
		ProjectID     ID                      `gorm:"not null;index"`
		ClusterID     ID                      `gorm:"not null;index;uniqueIndex:uniq_cid_epid,priority:1"`
		ServiceName   string                  `gorm:"not null;type:VARCHAR(255)"`
		EndpointID    string                  `gorm:"not null;type:VARCHAR(255);uniqueIndex:uniq_cid_epid,priority:2"`
		EndpointType  PrivateLinkEndpointType `gorm:"type:ENUM('dbaas','customer');not null;default:'customer'"`
		CloudProvider string                  `gorm:"type:ENUM('aws','gcp');not null;default:'aws'"`
		WorkPoolID    ID                      `gorm:"not null;index;default:0"`
	}

	AwsCmek struct {
		Updatable
		Versioned

		ProjectID ID      `gorm:"not null;uniqueIndex:project_region,priority:1;"`
		RegionID  ID      `gorm:"not null;uniqueIndex:project_region,priority:2;"`
		Region    *Region `gorm:"foreignkey:RegionID"`
		KmsArn    string  `gorm:"type:VARCHAR(255);not null"`
	}
)

// tool tables
type (
	// CoreEvent events for core-svc
	CoreEvent struct {
		Updatable
		Versioned

		Topic       string      `gorm:"type:VARCHAR(255);not null"`
		UUID        string      `gorm:"type:VARCHAR(255);not null"`
		Payload     string      `gorm:"not null"`
		TimestampMs int64       `gorm:"not null"`
		Status      EventStatus `gorm:"type:ENUM('created','sent');not null;default:'created'"`
	}
)

// Value marshal json
func (c ClusterAttributes) Value() (driver.Value, error) {
	b, err := json.Marshal(c)
	return string(b), err
}

// Scan unmarshal json
func (c *ClusterAttributes) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), c)
}

func (m TierUnitMapping) Value() (driver.Value, error) {
	b, err := json.Marshal(m)
	return string(b), err
}

func (m *TierUnitMapping) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), m)
}

// Value marshal json
func (c TrafficFilters) Value() (driver.Value, error) {
	b, err := json.Marshal(c)
	return string(b), err
}

// Scan unmarshal json
func (c *TrafficFilters) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), c)
}

// gen:qs
type APIKey struct {
	Updatable

	// The tenant that this API can be used for.
	TenantID    ID
	CreatedByID ID     `gorm:"not null" valid:"required"`
	Name        string `gorm:"not null;size:50" valid:"required"`
	KeyHash     string `gorm:"not null;size:128;unique" valid:"required"` // The SHA256 of the API key.
}

func (id ID) Uint64() uint64 {
	return uint64(id)
}

func (c ComponentStates) Value() (driver.Value, error) {
	b, err := json.Marshal(c)
	return string(b), err
}

func (c *ComponentStates) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), c)
}

type ComponentStates struct {
	Port       int32            `json:"port"`
	Components []ComponentState `json:"components"`
}

type ComponentState struct {
	ComponentType     ComponentName `json:"component_type"`
	CloudProviderName string        `json:"cloud_provider_name"`
	RegionName        string        `json:"region_name"`
	TierKey           string        `json:"tier_key"`
	TierUnitKey       string        `json:"tier_unit_key"`
	Replicas          uint32        `json:"replicas"`
	StorageSizeGi     uint32        `json:"storage_size_gi"`
	StorageIOPS       uint32        `json:"storage_iops"`
}

type WorkPoolState struct {
	WorkPoolID       uint64 `json:"work_pool_id"`
	TidbNodeQuantity uint32 `json:"tidb_node_quantity"`
}

type ClusterMeteringStates struct {
	ClusterStatus     ClusterStatus     `json:"cluster_status"`
	ClusterComponents []*ComponentState `json:"cluster_components"`
	WorkPoolStates    []*WorkPoolState  `json:"work_pool_states"`
}

func (m ClusterMeteringStates) Value() (driver.Value, error) {
	b, err := json.Marshal(m)
	return string(b), err
}

func (m *ClusterMeteringStates) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), m)
}

type NetworkDetailState struct {
	Unit  string `json:"unit"`
	Cost  int64  `json:"cost"`
	Usage int64  `json:"usage"`
}

type NetworkDetailStates []NetworkDetailState

type ProjectMeteringStates struct {
	NetworkDetailStates NetworkDetailStates `json:"network_detail_states"`
}

func (m ProjectMeteringStates) Value() (driver.Value, error) {
	b, err := json.Marshal(m)
	return string(b), err
}

func (m *ProjectMeteringStates) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), m)
}

type ProjectMetering struct {
	Updatable

	TenantID  ID        `gorm:"index;not null"`
	ProjectID ID        `gorm:"not null;uniqueIndex:unq_pid_sts,priority:1"`
	StartTS   time.Time `gorm:"timestamp;index;uniqueIndex:unq_pid_sts,priority:2"`
	EndTS     time.Time `gorm:"timestamp;index"`
	Latest    bool      `gorm:"not null;default true"`

	ProjectMeteringStates ProjectMeteringStates `gorm:"type:JSON"`
}

type ClusterMetering struct {
	Updatable

	TenantID  ID        `gorm:"index;not null"`
	ProjectID ID        `gorm:"index;not null"`
	ClusterID ID        `gorm:"not null;uniqueIndex:unq_cid_sts,priority:1"`
	StartTS   time.Time `gorm:"timestamp;uniqueIndex:unq_cid_sts,priority:2"`
	EndTS     time.Time `gorm:"timestamp"`
	Latest    bool      `gorm:"not null;default true"`

	ClusterMeteringStates ClusterMeteringStates `gorm:"type:JSON"`
}

func (m MeteringReportData) Value() (driver.Value, error) {
	b, err := json.Marshal(m)
	return string(b), err
}

func (m *MeteringReportData) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), m)
}

type MeteringReportDataItem struct {
	CostID        string            `json:"cost_id"`
	SKU           string            `json:"sku"`
	CostTimeStamp int64             `json:"cost_time_stamp"`
	Cost          int64             `json:"cost"`
	Labels        map[string]string `json:"labels"`
	Memo          string            `json:"memo"`
}

type MeteringReportData struct {
	Items []*MeteringReportDataItem `json:"items"`
}

// gen:qs
type BillingReportRecord struct {
	Updatable

	Data              MeteringReportData `gorm:"type:JSON"`
	Status            string             `gorm:"not null;type:ENUM('synced', 'unsynced')"`
	ProjectMeteringID ID                 `gorm:"not null;default '0';index"`
	ClusterMeteringID ID                 `gorm:"not null;default '0';index"`
	TenantID          ID                 `gorm:"not null;index"`
}

// gen:qs
type ClusterComponent struct {
	Updatable

	TenantID      ID            `gorm:"not null;index"`
	ProjectID     ID            `gorm:"not null;index"`
	ClusterID     ID            `gorm:"not null;uniqueIndex:unq_cid_ctype_rid,priority:1"`
	Cluster       *Cluster      `gorm:"foreignkey:ClusterID"`
	ComponentType ComponentName `gorm:"not null;uniqueIndex:unq_cid_ctype_rid,priority:2;type:ENUM('pd', 'tikv', 'tidb', 'tiflash', 'ticdc')"`
	RegionID      ID            `gorm:"index;uniqueIndex:unq_cid_ctype_rid,priority:3;not null"`
	Region        *Region       `gorm:"foreignkey:RegionID"`
	StorageSizeGi uint32        `gorm:"not null"`
	StorageIOPS   uint32        `gorm:"not null;default: 0"`
	Replicas      uint32        `gorm:"not null"`
	TierKey       string        `gorm:"type:VARCHAR(255);not null; default ''"`
	TierUnitKey   string        `gorm:"type:VARCHAR(255);not null; default ''"`
}

// Used to backup and restore the cluster with the same configuration as the original cluster
// gen:qs
type ClusterSnapshot struct {
	Updatable
	Versioned

	TenantID        ID              `gorm:"index;not null"`
	ProjectID       ID              `gorm:"index;not null"`
	ClusterID       ID              `gorm:"index;not null"`
	Cluster         *Cluster        `gorm:"foreignkey:ClusterID"`
	ComponentStates ComponentStates `gorm:"type:JSON"`
}

type PDTier struct {
	TierKey     string `json:"tier_key"`
	TierUnitKey string `json:"tier_unit_key"`
	NodeNum     int    `json:"node_num"`
}

// Value marshal json
func (args PDTier) Value() (driver.Value, error) {
	b, err := json.Marshal(args)
	return string(b), err
}

// Scan unmarshal json
func (args *PDTier) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), args)
}

type SubnetIDs struct {
	SubnetIDs []string `json:"subnet_ids"`
}

func (s SubnetIDs) Value() (driver.Value, error) {
	b, err := json.Marshal(s)
	return string(b), err
}

func (s *SubnetIDs) Scan(input interface{}) error {
	return json.Unmarshal(input.([]byte), s)
}

// gen:qs
type ClusterJob struct {
	Updatable

	ClusterID ID       `gorm:"uniqueIndex:uniq_cluster_type_name,priority:1;not null"`
	Cluster   *Cluster `gorm:"foreignkey:ClusterID"`
	JobType   string   `gorm:"type:VARCHAR(256);uniqueIndex:uniq_cluster_type_name,priority:2;not null"`
	Name      string   `gorm:"type:VARCHAR(256);uniqueIndex:uniq_cluster_type_name,priority:3;not null"`
}

type MaintenanceWindow struct {
	Updatable
	Versioned

	ProjectID    ID    `gorm:"not null;index"`
	DayOfWeek    int32 `gorm:"not null"`
	HourOfDay    int32 `gorm:"not null"`
	MinuteOfHour int32 `gorm:"not null"`
}

type MaintenanceTask struct {
	Updatable
	Versioned

	ProjectID          ID                               `gorm:"not null;index"`
	Description        string                           `gorm:"type:text"`
	Status             MaintenanceTaskStatus            `gorm:"type:ENUM('pending','running','done');not null"`
	ScheduledApplyTime time.Time                        `gorm:"index;type:TIMESTAMP"`
	Deadline           *time.Time                       `gorm:"type:TIMESTAMP"`
	NotificationPhase  MaintenanceTaskNotificationPhase `gorm:"type:VARCHAR(256);default:''"`
}

type MaintenanceTaskExecution struct {
	Updatable
	Versioned

	ClusterID          ID                                         `gorm:"uniqueIndex:uniq_cluster_task,priority:1;not null"`
	MaintenanceTaskID  ID                                         `gorm:"uniqueIndex:uniq_cluster_task,priority:2;not null"`
	Status             MaintenanceTaskExecutionStatus             `gorm:"type:ENUM('pending','running','done','canceled');not null"`
	CancellationReason MaintenanceTaskExecutionCancellationReason `gorm:"type:VARCHAR(256);default:''"`
}

type WorkPool struct {
	Updatable
	DeletedAt gorm.DeletedAt `gorm:"index"`

	DisplayName      string `gorm:"type:VARCHAR(255);not null"`
	ClusterID        ID     `gorm:"index;not null"`
	TidbNodeQuantity uint32 `gorm:"not null"`
}
