//go:generate $GOBIN/goqueryset -in $PWD/$GOFILE
package db

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

	"github.com/pingcap/errors"
	"gorm.io/gorm"
)

var Models = []any{
	&UploadFile{},
	&UploadFilePart{},
	&NodeAssociation{},
	&JiraIssue{},
	&JiraIssueBindRequest{},
	&LogSymptom{},
	&Organization{},
	&User{},
	&Role{},
	&Cluster{},
	&ResourcePoolO11yRoute{},
	&Credential{},
	&APICredential{},
	&InternalAPICredential{},
	&Kibana{},
	&OperationJob{},
	&Star{},
	&HealthReport{},
	&OrgQuota{},
	&ComparisonReport{},
	&TiDBDefaultConfiguration{},
	&TiKVDefaultConfiguration{},
	&PDDefaultConfiguration{},
	&TiFlashDefaultConfiguration{},
	&PerformanceInsightCluster{},
	&PerformanceInsightTask{},
	&PlanInsightResultStatement{},
	&PlanInsightResultPlan{},
	&IndexAdvisorResult{},
	&HealthCheckStrategy{},
	&HealthCheckResult{},
}

// ResourcePoolO11yRoute caches the O11y VM account that currently owns a
// logical cluster. Resource-pool membership remains sourced from clusters;
// rows in this table are never used to grant access by themselves.
type ResourcePoolO11yRoute struct {
	ID             uint      `gorm:"primaryKey"`
	ResourcePoolID string    `gorm:"type:VARCHAR(255);not null;index:idx_resource_pool_o11y_routes_pool_refreshed,priority:1"`
	ClusterID      string    `gorm:"type:VARCHAR(255);not null;uniqueIndex"`
	VMAccountID    string    `gorm:"type:VARCHAR(64);not null"`
	RefreshedAt    time.Time `gorm:"not null;index:idx_resource_pool_o11y_routes_pool_refreshed,priority:2"`
	CreatedAt      time.Time
	UpdatedAt      time.Time
}

type StatusType string

type RebuildFlagInMeta string

const (
	AssociationEntityJiraIssue  = "JiraIssue"
	AssociationEntityClinicFile = "ClinicFile"

	StatusUnscheduled   StatusType = "unscheduled"
	StatusRunning       StatusType = "running"
	StatusSuccess       StatusType = "success"
	StatusFailed        StatusType = "failed"
	StatusDeleted       StatusType = "deleted"
	StatusPending       StatusType = "pending"
	StatusPartial       StatusType = "partial"
	StatusPartialFailed StatusType = "partial_failed"

	RebuildNow         RebuildFlagInMeta = "true"
	DisableRebuildNow  RebuildFlagInMeta = "false"
	DefaultRebuildFlag RebuildFlagInMeta = ""
)

func HasAssociationEntity(entity string) bool {
	switch entity {
	case AssociationEntityJiraIssue, AssociationEntityClinicFile:
		return true
	default:
		return false
	}
}

// gen:qs
type Report struct {
	gorm.Model

	UUID       string `gorm:"type:VARCHAR(36);not null;uniqueIndex"`
	OrgID      uint   `gorm:"type:uint;not null;DEFAULT:0"`
	ClusterID  string `gorm:"type:VARCHAR(255);not null;index:idx_cluster_id;default:''"`
	CreatorID  uint   `gorm:"type:uint;not null;index"`
	ReportType string `gorm:"type:VARCHAR(255);not null"`
	Status     string `gorm:"type:VARCHAR(255);default:unscheduled;index"`
	Progress   uint   `gorm:"type:uint"`
	Arg        string `gorm:"type:MEDIUMTEXT"` // touch report args in json
	Result     string `gorm:"type:MEDIUMTEXT"` // report result in json
}

type HealthReport struct {
	gorm.Model
	ReportUUID string `gorm:"type:VARCHAR(255);not null"`

	// if the org type is cloud, the FileUUID will become `cluster id`_`start unix`_`end unix`
	FileUUID string `gorm:"type:VARCHAR(255);not null;uniqueIndex"`

	OrgType   string `gorm:"type:VARCHAR(255)"`
	Status    string `gorm:"type:VARCHAR(255);default:unscheduled"`
	StartUnix uint64 `gorm:"type:uint;not null;default:0"`                               // used when searching on time range for org_type is `cloud`
	EndUnix   uint64 `gorm:"type:uint;not null;default:0"`                               // used when searching on time range for org_type is `cloud`
	ClusterID string `gorm:"type:VARCHAR(255);not null;index:idx_cluster_id;default:''"` // redundant metadata for cluster
	OrgID     uint   `gorm:"type:uint;not null;DEFAULT:0"`                               // redundant metadata for org
}

type ComparisonReport struct {
	gorm.Model

	ClusterID string `gorm:"type:VARCHAR(255);not null;uniqueIndex:search"`
	OrgID     uint   `gorm:"not null"`
	OrgType   string `gorm:"type:VARCHAR(255);not null;default:op"`
	Hash      string `gorm:"type:VARCHAR(255);not null;uniqueIndex:search"` // if length fit the md5

	ReportUUID    string         `gorm:"type:VARCHAR(255);not null"`
	ComparisonArg *ComparisonArg `gorm:"type:text"`
	Status        string         `gorm:"type:VARCHAR(255);not null;default:unscheduled"`
	Creator       string         `gorm:"type:VARCHAR(255);not null"`
	Info          string         `gorm:"type:text"`
}

// gen:qs
type Role struct {
	gorm.Model

	RoleName       string `gorm:"type:VARCHAR(255);not null;uniqueIndex:role_no"`
	Description    string `gorm:"type:VARCHAR(255)"`
	OrganizationID uint   `gorm:"not null;uniqueIndex:role_no"`
}

// gen:qs
type Organization struct {
	gorm.Model
	Name        string `gorm:"type:VARCHAR(255);not null;uniqueIndex:org_key"`
	Description string `gorm:"type:VARCHAR(255)"`

	DisableRebuild bool   `gorm:"default:false"`
	DbaasOrgID     uint   `gorm:"index;uniqueIndex:org_key"`
	Plan           string `gorm:"type:VARCHAR(255)"` // sync from dbaas
	SupportPlan    string `gorm:"type:VARCHAR(255)"` // sync from dbaas
	Kind           string `gorm:"type:VARCHAR(255)"` // internal or external
	Country        string `gorm:"type:VARCHAR(255)"`
	EmailDomain    string `gorm:""`
	CreatedBy      uint   `gorm:""`

	OrgType           string `gorm:""`
	isStarredByViewer bool   `gorm:"-"`
}

func (o *Organization) IsStarredByViewer() bool {
	return o.isStarredByViewer
}

func (o *Organization) SetStarredByViewer(isStar bool) {
	o.isStarredByViewer = isStar
}

// remain for legacy row that storage cluster name with tenantID
func (o *Organization) GetRawName() string {
	return strings.TrimSuffix(o.Name, fmt.Sprintf("/%d", o.DbaasOrgID))
}

// gen:qs
// Credential for uploading package
type Credential struct {
	gorm.Model
	OrgID     uint   `gorm:"not null"`
	UserID    uint   `gorm:"not null"`
	HashedKey string `gorm:"type:VARCHAR(255)"`
}

// APICredential for other api calling.
type APICredential struct {
	ID        uint           `gorm:"primarykey"`
	OrgID     uint           `gorm:"not null"` // currently, this field is not used for api credentials.
	UserID    uint           `gorm:"not null"`
	PlainKey  string         `gorm:"column:plain_key;type:VARCHAR(255)"`
	CreatedAt time.Time      `gorm:"column:created_at;<-:false;type:timestamp;not null;DEFAULT:CURRENT_TIMESTAMP"`
	UpdatedAt time.Time      `gorm:"column:updated_at;<-:false;type:timestamp;not null;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"`
	DeletedAt gorm.DeletedAt `gorm:"index"`
}

// InternalAPICredential for internal user (internal=true) API access.
// Internal users don't belong to any organization, so they need a separate credential table.
type InternalAPICredential struct {
	gorm.Model
	UserID     uint       `gorm:"not null;index"`                    // Associated internal user
	Name       string     `gorm:"type:VARCHAR(255)"`                 // Readable name
	HashedKey  string     `gorm:"type:VARCHAR(255);not null"`        // PBKDF2 hashed key
	Status     string     `gorm:"type:VARCHAR(20);default:'active'"` // active/inactive/revoked
	ExpiresAt  *time.Time `gorm:"index"`                             // Optional expiration time
	LastUsedAt *time.Time // Last used time
}

// gen:qs
type Kibana struct {
	gorm.Model
	OrgID    uint   `gorm:"not null;uniqueIndex:idx_org"`
	User     string `gorm:"not null;"`
	Password string `gorm:"not null;"`
}

// gen:qs
type UploadFile struct {
	gorm.Model
	// ID               uint64    `gorm:"primary_key;not null;AUTO_INCREMENT"`
	FileUUID     string `gorm:"type:VARCHAR(255);not null;uniqueIndex:idx_file;"`
	ClusterName  string `gorm:"type:VARCHAR(255)"`
	BeginTime    *time.Time
	EndTime      *time.Time
	Alias        string `gorm:"type:VARCHAR(255)"`
	ClusterID    string `gorm:"type:VARCHAR(255);index"` // foreign key of Cluster
	UploadID     string `gorm:"type:VARCHAR(255);not null;"`
	FileLen      int64  `gorm:"not null"`
	DirSize      int    `gorm:"type:int"`
	UploadStatus string `gorm:"type:VARCHAR(15);default:'running'"`
	StorageType  string `gorm:"type:VARCHAR(15);not null"`
	// CreateAt         time.Time `gorm:"type:TIMESTAMP;DEFAULT: CURRENT_TIMESTAMP;"`
	// UpdateAt         time.Time `gorm:"type:TIMESTAMP;DEFAULT: CURRENT_TIMESTAMP;"`
	DownloadURL                string      `gorm:"type:VARCHAR(255)"`
	FileOriginalName           string      `gorm:"type:VARCHAR(255);not null;"`
	Collectors                 StringSlice `gorm:"type:TEXT"`
	RebuildMetricStatus        string      `gorm:"type:VARCHAR(255);default:'unscheduled'" rebuild:"rebuildMetric"`
	RebuildLogStatus           string      `gorm:"type:VARCHAR(255);default:'unscheduled'" rebuild:"rebuildLog"`
	RebuildConfigurationStatus string      `gorm:"type:VARCHAR(255);default:'unscheduled'" rebuild:"rebuildConfiguration"`
	ReportStatus               string      `gorm:"type:VARCHAR(255);default:'unscheduled'"`
	// FetchConfStatus            string `gorm:"type:VARCHAR(255);default:'unscheduled'"`
	Creator         uint   `gorm:"type:uint"` // foreign key of User
	OrgID           uint   `gorm:"index"`     // foreign key of Org
	Rebuild         string `gorm:"type:VARCHAR(15)"`
	ClusterType     string `gorm:"type:VARCHAR(15)"`
	SubmitedRebuild bool   `gorm:"default:false"`
}

// ClusterOrgIDCreatedAtIndex is managed by migration 94. It is declared here
// instead of as a GORM tag because CreatedAt belongs to the embedded gorm.Model.
const ClusterOrgIDCreatedAtIndex = "idx_clusters_org_id_created_at"

// ClusterDeployTypeV2CreatedAtIndex is managed by migration 91. It is declared
// here because CreatedAt belongs to the embedded gorm.Model.
const ClusterDeployTypeV2CreatedAtIndex = "idx_clusters_deploy_type_v2_created_at"

// ClusterNameCreatedAtIndex is managed by migration 95. The name prefix
// matches the longest value currently stored in production and keeps the
// secondary index bounded while Name remains LONGTEXT.
const ClusterNameCreatedAtIndex = "idx_clusters_name_created_at"

// gen:qs
type Cluster struct {
	gorm.Model
	ClusterID    string       `gorm:"type:VARCHAR(255);primarykey;uniqueIndex"`
	Version      string       `gorm:""`
	Name         string       `gorm:""`
	OrgID        uint         `gorm:"type:uint;not null;index:idx_clusters_org_id"`
	Organization Organization `gorm:"foreignKey:OrgID"` // Cluster belongs to Organization
	Desc         string       `gorm:"type:VARCHAR(255);"`
	StartTime    uint64       `gorm:"type:uint;"`
	EndTime      uint64       `gorm:"type:uint;"`
	Topology     *Topology    `gorm:"type:text"` // todo decode cluster topology
	ClusterType  string       `gorm:"type:VARCHAR(15);"`
	Status       string       `gorm:"type:VARCHAR(25);default:active"`
	DeployType   string       `gorm:"type:VARCHAR(64);"`
	DeployTypeV2 string       `gorm:"type:VARCHAR(64);"`

	// only for cloud cluster
	TenantID           uint64            `gorm:"type:uint;not null;index"`
	ShootName          string            `gorm:"type:VARCHAR(255);"`
	DataPlaneName      string            `gorm:"type:VARCHAR(255);"`
	K8sNamespace       string            `gorm:"type:VARCHAR(255);"`
	NetworkName        string            `gorm:"type:VARCHAR(255);"`
	Region             string            `gorm:"type:VARCHAR(255);index"`
	Provider           string            `gorm:"type:VARCHAR(255);index"`
	ProjectID          string            `gorm:"type:VARCHAR(255);index"`
	CreationFinishedAt *time.Time        `gorm:"index"`
	Branches           StringSlice       `gorm:"type:TEXT;"`
	ControlPlaneArch   string            `gorm:"type:VARCHAR(255);"`
	ClusterComponent   ClusterComponents `gorm:"type:text"`
	ParentID           string            `gorm:"type:VARCHAR(255);index"` // nextgen physic cluster id
	MinRCU             int64             `gorm:"-"`                       // min RCU for nextgen cluster, used for serverless cluster
	MaxRCU             int64             `gorm:"-"`                       // max RCU for nextgen cluster, used for serverless cluster
	SpendLimit         int64             `gorm:"-"`                       // spend limit for serverless cluster
	KeyspaceID         uint32            `gorm:"-"`                       // keyspace id for serverless cluster
	KeyspaceName       string            `gorm:"-"`                       // keyspace name for serverless cluster
	RuVersion          int32             `gorm:"-"`                       // resource unit version from mgmt (Essential/Premium)
}

type TidbGroup struct {
	Id          uint64     `json:"id"`
	DisplayName string     `json:"displayName"`
	IsDefault   bool       `json:"isDefault"`
	CreatedAt   *time.Time `json:"createdAt"`
	DeletedAt   *time.Time `json:"deletedAt"`
	Replicas    int32      `json:"replicas"`
	CpuNum      int32      `json:"cpuNum"`
	MemoryGi    int64      `json:"memoryGi"`
	TierName    string     `json:"tierName"`
}

// TODO: replace toplogy
type ClusterComponent struct {
	Replicas             int32                        `json:"replicas"`
	CpuNum               int32                        `json:"cpuNum"`
	MemoryGi             int64                        `json:"memoryGi"`
	StorageSizeGi        int64                        `json:"storageSizeGi"`
	StorageInstanceType  string                       `json:"storageInstanceType"`
	TidbGroupId          uint64                       `json:"TidbGroupId"`
	TidbGroupDisplayName string                       `json:"tidbGroupDisplayName"`
	TierName             string                       `json:"tierName"`
	Storages             map[string]*ComponentStorage `json:"storages"`
	// StorageType only used for tikv and tiflash
	StorageType string      `json:"storageType"`
	TidbGroups  []TidbGroup `json:"tidbGroups"`
}

type ClusterComponents map[string]ClusterComponent

func (c *ClusterComponents) Scan(value any) error {
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
	}
	return json.Unmarshal(bytes, c)
}

func (c ClusterComponents) Value() (driver.Value, error) {
	return json.Marshal(c)
}

func (c ClusterComponents) GormDataType() string {
	return "cluster_component"
}

func (c ClusterComponents) GormDBDataType(db *gorm.DB) string {
	return "cluster_component"
}

func (o *Cluster) GetRawName() string {
	return strings.TrimSuffix(o.Name, fmt.Sprintf("/%s", o.ClusterID))
}

type UploadFilePart struct {
	gorm.Model
	// ID           uint64      `gorm:"primary_key;not null;AUTO_INCREMENT"`
	FileUUID     string      `gorm:"type:VARCHAR(255);not null;uniqueIndex:idx_file_no;"`
	SerialNumber int         `gorm:"not null;uniqueIndex:idx_file_no;"`
	Etag         string      `gorm:"type:VARCHAR(255)"`
	UploadFile   *UploadFile `gorm:"foreignkey:UploadFileID"`
	UploadFileID uint
	// CreateAt     time.Time `gorm:"type:TIMESTAMP;DEFAULT: CURRENT_TIMESTAMP"`
	// UpdateAt     time.Time `gorm:"type:TIMESTAMP;DEFAULT: CURRENT_TIMESTAMP"`
	PartLen uint64 `gorm:"not null"`
}

type NodeAssociation struct {
	SourceNodeID     uint   `gorm:"primaryKey"`
	SourceNodeEntity string `gorm:"primaryKey"`
	SinkNodeID       uint   `gorm:"primaryKey"`
	SinkNodeEntity   string `gorm:"primaryKey"`
	CreatedAt        time.Time
}

type JiraIssue struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	IssueNumber uint   `gorm:"type:VARCHAR(255)" json:"issue_number"`
	Project     string `gorm:"type:VARCHAR(255)" json:"project"`
	Summary     string `gorm:"type:VARCHAR(255)" json:"summary"`
}

type JiraIssueBindRequest struct {
	FileUUID    string `gorm:"primaryKey" json:"uuid"`
	IssueNumber uint   `gorm:"primaryKey" json:"issue_number"`
	Project     string `gorm:"primaryKey" json:"project"`
	CreatedAt   time.Time
}

type LogSymptom struct {
	FileUUID string `gorm:"primaryKey;column:file_uuid"`
	Symptom  string `gorm:"primaryKey;column:symptom"`
	Kibana   string `gorm:"column:kibana"`
	KB       string `gorm:"column:kb"`   // KB is a string array split by comma
	Jira     string `gorm:"column:jira"` // Jira is a string array split by comma
}

// gen:qs
type User struct {
	gorm.Model

	UserName string `gorm:"type:VARCHAR(255);not null;uniqueIndex:idx_username;"`
	Email    string `gorm:"type:VARCHAR(255)"`

	ExternalIdentifier string `gorm:"type:VARCHAR(255);not null;uniqueIndex:idx_external_identifier;"`
	Internal           bool   `gorm:"default:false"`
}

type Topology struct {
	TiDB     int `json:"tidb"`
	TiKV     int `json:"tikv"`
	PD       int `json:"pd"`
	DMMaster int `json:"dmMaster"`
	DMWorker int `json:"dmWorker"`
	TiFlash  int `json:"tiflash"`
}

func (t *Topology) Scan(value any) error {
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
	}
	return json.Unmarshal(bytes, t)
}

func (t Topology) Value() (driver.Value, error) {
	return json.Marshal(t)
}

func (t Topology) GormDataType() string {
	return "topology"
}

func (t Topology) GormDBDataType(db *gorm.DB) string {
	return "topology"
}

type ComponentStorage struct {
	DiskType        string  `json:"disk_type"`
	SizeGi          uint32  `json:"size_gi"`
	Iops            uint32  `json:"iops"`
	ThroughputMibps uint32  `json:"throughput_mibps"`
	VolumeType      string  `json:"volume_type"`
	AdditionalIops  *uint32 `json:"additional_iops"`
}

func (c *ComponentStorage) Scan(value any) error {
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
	}
	return json.Unmarshal(bytes, c)
}

func (c *ComponentStorage) Value() (driver.Value, error) {
	return json.Marshal(c)
}

func (c *ComponentStorage) GormDataType() string {
	return "component_storage"
}

func (c *ComponentStorage) GormDBDataType(db *gorm.DB) string {
	return "component_storage"
}

type ComparisonArg struct {
	PackageID1 []string `json:"packageID1"`
	StartTime1 uint     `json:"startTime1"`
	EndTime1   uint     `json:"endTime1"`

	PackageID2 []string `json:"packageID2"`
	StartTime2 uint     `json:"startTime2"`
	EndTime2   uint     `json:"endTime2"`
}

func (c *ComparisonArg) Scan(value any) error {
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
	}
	return json.Unmarshal(bytes, c)
}

func (c ComparisonArg) Value() (driver.Value, error) {
	return json.Marshal(c)
}

func (t ComparisonArg) GormDataType() string {
	return "comparison"
}

func (t ComparisonArg) GormDBDataType(db *gorm.DB) string {
	return "comparison"
}

type StringSlice []string

func (s *StringSlice) Scan(value any) error {
	bytes, ok := value.([]byte)
	if !ok {
		return errors.New(fmt.Sprint("Failed to unmarshal JSON value to string list:", value))
	}
	return json.Unmarshal(bytes, s)
}

func (s StringSlice) Value() (driver.Value, error) {
	return json.Marshal(s)
}

// gen:qs
type OperationJob struct {
	gorm.Model
	JobID       string `gorm:"primaryKey;not null"`
	Name        string `gorm:"type:VARCHAR(255)"`
	ClusterID   string `gorm:"type:VARCHAR(255)"`
	ClusterName string `gorm:"type:VARCHAR(255)"`
	Type        string `gorm:"type:VARCHAR(50)"`
	Email       string `gorm:"type:VARCHAR(50)"`
	Reason      string `gorm:"type:VARCHAR(255)"`
	Status      string `gorm:"type:VARCHAR(50)"`
	StartTime   uint64 `gorm:"type:uint;"`
	EndTime     uint64 `gorm:"type:uint;"`
	Approver    string `gorm:"type:VARCHAR(50)"`
	ApproveTime uint64 `gorm:"type:uint;"`
	Comment     string `gorm:"type:VARCHAR(255)"`
}

// gen:qs
type Approver struct {
	gorm.Model
	Email string `gorm:"primaryKey;not null"`
}

type Star struct {
	ID     uint64 `gorm:"column:id;type:uint;AUTOINCREMENT;PRIMARY_KEY"`                                   // bigint unsigned in mysql
	UserID uint64 `gorm:"column:user_id;type:uint;not null;uniqueIndex:idx_user_org,priority:1;DEFAULT:0"` // bigint unsigned in mysql
	OrgID  uint64 `gorm:"column:org_id;type:uint;not null;uniqueIndex:idx_user_org,priority:2;DEFAULT:0"`  // bigint unsigned in mysql
}

func (s *Star) TableName() string {
	return "star"
}

type OrgQuota struct {
	ID                          uint64    `gorm:"column:id;type:uint;AUTOINCREMENT;PRIMARY_KEY"`                             // bigint unsigned in mysql
	OrgID                       uint64    `gorm:"column:org_id;type:uint;not null;uniqueIndex:idx_org;DEFAULT:0"`            // bigint unsigned in mysql
	DataSizeBytesPerCluster     uint64    `gorm:"column:data_size_bytes_per_cluster;type:uint;not null;DEFAULT:53687091200"` // max data size in bytes per cluster, default 50GB
	RebuildDataTTL              uint64    `gorm:"column:rebuild_data_ttl;type:uint;not null;DEFAULT:259200"`                 // max live time for rebuild data in seconds, default 3 days
	DownloadCntPerClusterPerDay uint64    `gorm:"column:download_cnt_per_cluster_per_day;type:uint;not null;DEFAULT:10"`     // max download counts per cluster per day, default 10
	PackageTTL                  uint64    `gorm:"column:package_ttl;type:uint;not null;DEFAULT:11232000"`                    // max live time for a package in seconds, default 180 day
	FileLenBytes                uint64    `gorm:"column:file_len_bytes;type:uint;not null;DEFAULT:2147483648"`               // max file length for a package in bytes, default 2GB
	UploadCntPerClusterPerDay   uint64    `gorm:"column:upload_cnt_per_cluster_per_day;type:uint;not null;DEFAULT:10"`       // max upload count per cluster per day, default 10
	ClusterCnt                  uint64    `gorm:"column:cluster_cnt;type:uint;not null;DEFAULT:10"`                          // max cluster cnt, default 10
	ReportCntPerClusterPerDay   uint64    `gorm:"column:report_cnt_per_cluster_per_day;type:uint;not null;DEFAULT:10"`       // max report cnt per cluster per day, default 10
	RebuildConcurrency          uint64    `gorm:"column:rebuild_concurrency;type:uint;not null;DEFAULT:1"`                   // max rebuild task concurrency, default 1
	CreatedAt                   time.Time `gorm:"column:created_at;<-:false;type:timestamp;not null;DEFAULT:CURRENT_TIMESTAMP"`
	UpdatedAt                   time.Time `gorm:"column:updated_at;<-:false;type:timestamp;not null;DEFAULT:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"`
}

func (o *OrgQuota) TableName() string {
	return "org_quota"
}

// gen:qs
type Slogan struct {
	gorm.Model
	ClusterID string `gorm:"type:VARCHAR(255)"`
	Text      string `gorm:"type:MEDIUMTEXT"` // report result in json
	OrgID     uint   `gorm:"type:uint"`       // foreign key of Org
}

// gen:qs
type TiDBDefaultConfiguration struct {
	DefaultConfiguration
}

func (o *TiDBDefaultConfiguration) TableName() string {
	return "tbl_tidb"
}

// gen:qs
type PDDefaultConfiguration struct {
	DefaultConfiguration
}

func (o *PDDefaultConfiguration) TableName() string {
	return "tbl_pd"
}

// gen:qs
type TiKVDefaultConfiguration struct {
	DefaultConfiguration
}

func (o *TiKVDefaultConfiguration) TableName() string {
	return "tbl_tikv"
}

// gen:qs
type TiFlashDefaultConfiguration struct {
	DefaultConfiguration
}

func (o *TiFlashDefaultConfiguration) TableName() string {
	return "tbl_tiflash"
}

type DefaultConfiguration struct {
	ItemName     string `gorm:"column:item_name;type:VARCHAR(255);uniqueIndex:idx_item_ver" json:"itemName"`
	ValueType    string `gorm:"column:value_type;type:VARCHAR(255);" json:"valueType"`
	DefaultValue string `gorm:"column:default_value;type:VARCHAR(1000);" json:"defaultValue"`
	Version      string `gorm:"column:version;type:VARCHAR(20);uniqueIndex:idx_item_ver" json:"-"`
}

// gen:qs
type (
	PerformanceInsightCluster struct {
		gorm.Model
		ClusterID               uint
		Cluster                 Cluster                  // PerformanceInsightCluster belongs to Cluster
		PerformanceInsightTasks []PerformanceInsightTask // PerformanceInsightCluster has many PerformanceInsightTasks

		ConnectAccountName string
		// For working with aws secret manager.
		ConnectPasswordSecretID string
		ConnectHost             string
		ConnectPort             uint
		CreatorID               uint
	}

	PerformanceInsightTask struct {
		gorm.Model
		PerformanceInsightClusterID uint
		PerformanceInsightCluster   PerformanceInsightCluster // PerformanceInsightTask belongs to PerformanceInsightCluster
		Status                      string
		Type                        string
		CreatorID                   uint
		ExecutionCount              uint
		OffsetHours                 uint
		LastFailedMessage           string
		MaterialCount               uint
	}
)

// gen:qs
type (
	PlanInsightResultStatement struct {
		gorm.Model
		PerformanceInsightTaskID uint
		PerformanceInsightTask   PerformanceInsightTask // PlanInsightResultStatement belongs to PerformanceInsightTask
		DbaasOrgID               uint64
		DbaasClusterID           uint64

		// Fields from the statements_summary table.
		// Refer to https://docs.pingcap.com/tidb/dev/statement-summary-tables#fields-description.
		StmtType        string
		SchemaName      string
		Digest          string
		DigestText      string
		QuerySampleText string
		TableNames      string
		IndexNames      string
		SampleUser      string

		// Intermediate result fields.
		PlanCount                uint64
		MinOfAvgLatenciesOfPlans uint64
		MaxOfAvgLatenciesOfPlans uint64
		M                        float64
		N                        float64

		// Final result fields.
		HasSuggestedPlan bool
		Impact           string

		PlanInsightResultPlans []PlanInsightResultPlan // PlanInsightResultStatement has many PlanInsightResultPlans
	}

	PlanInsightResultPlan struct {
		gorm.Model
		PlanInsightResultStatementID uint

		// Fields from the statements_summary table.
		// Refer to https://docs.pingcap.com/tidb/dev/statement-summary-tables#fields-description.
		PlanDigest string
		ExecCount  uint64
		AvgLatency uint64
		MinLatency uint64
		MaxLatency uint64
		AvgMemByte uint64
		MaxMemByte uint64

		// Final result fields.
		IsSuggested bool
	}
)

// gen:qs
type (
	IndexAdvisorResult struct {
		gorm.Model
		PerformanceInsightTaskID uint
		DbaasOrgID               uint64
		DbaasClusterID           uint64

		Result string // TODO: split
	}
)

type Task struct {
	gorm.Model
	TaskID   string `gorm:"type:VARCHAR(255);not null;uniqueIndex:task_id"`
	TaskType string `gorm:"type:VARCHAR(255);not null"` // unused
	Status   string `gorm:"type:VARCHAR(255);not null"` // unused
	Progress int    `gorm:"type:int;not null"`
}

// HealthCheckStrategy stores the health check strategy for clusters
// A strategy contains general configuration and multiple check items stored in JSON
// gen:qs
type HealthCheckStrategy struct {
	gorm.Model

	UUID       string `gorm:"type:VARCHAR(36);not null;uniqueIndex"`
	ClusterID  string `gorm:"type:VARCHAR(255);not null;index:idx_cluster_id"`
	DbaasOrgID string `gorm:"type:VARCHAR(255);not null;index"` // dbaas org id from URL
	CreatorID  uint   `gorm:"type:uint;not null"`

	// Schedule configuration
	ScheduleType string `gorm:"type:VARCHAR(50);not null;default:'manual'"` // manual, cron
	ScheduleExpr string `gorm:"type:VARCHAR(255)"`                          // cron expression or empty for manual

	// Strategy name and description
	Name        string `gorm:"type:VARCHAR(255);not null"`
	Description string `gorm:"type:VARCHAR(1000)"`

	// Default time range for all check items (legacy, for backward compatibility)
	// Deprecated: Use TimeRangeAStartExpr, TimeRangeAEndExpr, etc. instead
	TimeRangeAStart *time.Time
	TimeRangeAEnd   *time.Time
	TimeRangeBStart *time.Time
	TimeRangeBEnd   *time.Time

	// Time range expressions for relative time support (e.g., "now", "-3600", RFC3339)
	// These take precedence over the legacy TimeRange fields if set
	TimeRangeAStartExpr string `gorm:"type:VARCHAR(255)"`
	TimeRangeAEndExpr   string `gorm:"type:VARCHAR(255)"`
	TimeRangeBStartExpr string `gorm:"type:VARCHAR(255)"`
	TimeRangeBEndExpr   string `gorm:"type:VARCHAR(255)"`

	// Check items stored as JSON array
	Items string `gorm:"type:TEXT"` // JSON format: [{"name":"slow query","checkType":"sql","enabled":true,...}]

	// Status: active, paused, deleted
	Status string `gorm:"type:VARCHAR(50);not null;default:'active'"`

	// Last run time
	LastRunAt *time.Time

	// Next scheduled run time (for cron jobs)
	NextRunAt *time.Time
}

// HealthCheckResult stores the results of health check jobs
// gen:qs
type HealthCheckResult struct {
	gorm.Model

	UUID         string `gorm:"type:VARCHAR(36);not null;uniqueIndex"`
	StrategyUUID string `gorm:"type:VARCHAR(36);not null;index:idx_strategy_uuid"`
	ClusterID    string `gorm:"type:VARCHAR(255);not null;index:idx_cluster_id"`
	DbaasOrgID   string `gorm:"type:VARCHAR(255);not null;index"` // dbaas org id from URL

	// Items to run (JSON array of item names, empty means all enabled items)
	ItemsToRun string `gorm:"type:TEXT"`

	// Job status: pending, running, success, failed
	Status string `gorm:"type:VARCHAR(50);not null;default:'pending'"`

	// Error message if failed
	ErrorMsg string `gorm:"type:TEXT"`

	// Time range used for this check
	TimeRangeAStart *time.Time
	TimeRangeAEnd   *time.Time
	TimeRangeBStart *time.Time
	TimeRangeBEnd   *time.Time

	// Cluster URLs resolved at trigger time (JSON format)
	ClusterURLs string `gorm:"type:TEXT"`

	// Additional metadata
	Metadata string `gorm:"type:TEXT"`
}
