package schema

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

	"gorm.io/gorm"
)

type Cluster struct {
	ID               uint64 `gorm:"primarykey"`
	CreatedAt        time.Time
	UpdatedAt        time.Time
	DeletedAt        gorm.DeletedAt `gorm:"index"`
	CreateFinishedAt sql.NullTime

	TenantId                       uint64 `gorm:"index"`
	ProjectId                      uint64 `gorm:"index"`
	ProviderId                     uint64 `gorm:"index"`
	RegionId                       uint64 `gorm:"index"`
	Port                           uint32 `gorm:"default:4000"`
	State                          string `gorm:"index"`
	OnNewArch                      bool   `gorm:"default:false"` // will deprecate
	CustomerManagedEncryptionKeyId *uint64
	EncryptedRootPassword          string
	EncryptedCloudAdminPassword    string
	HasRootPassword                bool
	ClusterVersion                 string
	ClusterIdentifierHash          string `gorm:"index:,unique"`

	Region                       Region                        // A cluster belongs to a region.
	Provider                     Provider                      // A cluster belongs to a provider.
	CustomerManagedEncryptionKey *CustomerManagedEncryptionKey // A cluster belongs to a customer managed encryption key.
	ClusterComponents            []ClusterComponent            // A cluster has many cluster components.
}

type ClusterComponent struct {
	gorm.Model

	// For non-tidb components, the WorkPoolId is the default tidb group id.
	WorkPoolId           uint64 `gorm:"index:comp_group,unique"`
	ComponentType        string `gorm:"index:comp_group,unique"`
	ClusterId            uint64 `gorm:"index:comp_group,unique"`
	ComponentTierKey     string `gorm:"index"`
	ComponentTierUnitKey string `gorm:"index"`
	Count                uint32

	ComponentTierUnit ComponentTierUnit          `gorm:"foreignKey:ComponentTierUnitKey;references:Key"` // A component belongs to a tier unit.
	WorkPool          WorkPool                   // A component belongs to a work pool.
	Storages          []*ClusterComponentStorage // A component has many storages.
}

type ClusterComponentStorage struct {
	gorm.Model

	ClusterComponentId uint64 `gorm:"index:comp_storage_type,unique"`
	// This storage type should be disk type, not storage type. Keep it for compatibility.
	StorageType string `gorm:"index:comp_storage_type,unique;type:ENUM('data','raftlog');not null; default:'data'"`
	// Current effective size.
	SizeGi uint32
	// Current effective volume type.
	VolumeType *string `gorm:"type:ENUM('gp3','io2','pd-ssd','local-storage','gp2','premiumv2','ultrassd','hyperdisk-balanced','hyperdisk-extreme')"`
	// Current effective iops.
	Iops *uint32
	// Current effective throughput.
	ThroughPutMibps *uint32
	// Current additional iops.
	AdditionalIops uint32
}

type WorkPool struct {
	gorm.Model

	DisplayName string `gorm:"type:VARCHAR(191);not null"`
	Default     bool   `gorm:"not null"`
	ClusterId   uint64 `gorm:"index"`

	Cluster Cluster // A work pool belongs to a cluster.
}

type ClusterOperation struct {
	gorm.Model

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

type ClusterSnapshot struct {
	gorm.Model

	TenantID        uint64          `gorm:"index;not null"`
	ProjectID       uint64          `gorm:"index;not null"`
	ClusterID       uint64          `gorm:"index;not null"`
	ComponentStates ComponentStates `gorm:"type:JSON"`

	Cluster *Cluster `gorm:"foreignkey:ClusterID"` // A snapshot belongs to a cluster.
}

type ClusterPausePlan struct {
	gorm.Model

	ClusterID uint64 `gorm:"index;not null"`
	PauseAt   time.Time
	Plan      string `gorm:"type:ENUM('normal','long')"`
}

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

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

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

type ComponentState struct {
	ComponentType string `json:"component_type"`
	SpuKey        string `json:"tier_key"`
	SkuKey        string `json:"tier_unit_key"`
	Replicas      uint32 `json:"replicas"`

	StorageSizeGi          uint32 `json:"storage_size_gi"`
	StorageIOPS            uint32 `json:"storage_iops"`
	StorageThroughputMibps uint32 `json:"storage_throughput_mibps"`
	StorageVolumeType      string `json:"storage_volume_type"`
	StorageAdditionalIops  uint32 `json:"storage_additional_iops"`
	// This storage type should be disk type, not storage type. Keep it for compatibility.
	StorageDiskType string `json:"storage_type" default:"data"`
}
