package schema

import (
	"time"

	"github.com/tidbcloud/cluster-service-ng/pkg/nodegroup"
	"gorm.io/gorm"
)

// NodeInstanceGroupState constants for the shortage handling state machine.
// State transitions: Normal -> ShortageDetected -> FallbackCreating -> FallbackReady
//
//	-> Recovering -> DrainingFallback -> DeletingFallback -> Normal
const (
	NodeInstanceGroupStateNormal           = nodegroup.NodeInstanceGroupStateNormal
	NodeInstanceGroupStateShortageDetected = nodegroup.NodeInstanceGroupStateShortageDetected
	NodeInstanceGroupStateFallbackCreating = nodegroup.NodeInstanceGroupStateFallbackCreating
	NodeInstanceGroupStateFallbackReady    = nodegroup.NodeInstanceGroupStateFallbackReady
	NodeInstanceGroupStateRecovering       = nodegroup.NodeInstanceGroupStateRecovering
	NodeInstanceGroupStateDrainingFallback = nodegroup.NodeInstanceGroupStateDrainingFallback
	NodeInstanceGroupStateDeletingFallback = nodegroup.NodeInstanceGroupStateDeletingFallback
	NodeInstanceGroupStateDeprecated       = nodegroup.NodeInstanceGroupStateDeprecated
)

// NodeInstanceGroup phase constants for region-level node instance group management.
const (
	NodeInstanceGroupStatusActive     = nodegroup.NodeInstanceGroupStatusActive
	NodeInstanceGroupStatusPending    = nodegroup.NodeInstanceGroupStatusPending
	NodeInstanceGroupStatusDeprecated = nodegroup.NodeInstanceGroupStatusDeprecated
	NodeInstanceGroupStatusRetiring   = nodegroup.NodeInstanceGroupStatusRetiring
	NodeInstanceGroupStatusRetired    = nodegroup.NodeInstanceGroupStatusRetired
)

// NodeInstanceGroup stores the node instance group configuration for a node spec at region level.
// When the primary instance type encounters capacity issues (shortage),
// the system can automatically create fallback node instance groups using the configured fallback instance type.
type NodeInstanceGroup struct {
	ID              string `gorm:"type:varchar(128);primaryKey"`
	NodeSpec        string `gorm:"type:varchar(64);not null;uniqueIndex:idx_nig_spec_primary,priority:1"` // e.g., "8C16G"
	PrimaryInstance string `gorm:"type:varchar(64);not null;uniqueIndex:idx_nig_spec_primary,priority:2"` // e.g., "m5.2xlarge"

	CPU               uint32 `gorm:"not null;default:0"`
	MemoryGi          uint32 `gorm:"not null;default:0"`
	AvailableCPUCore  uint32 `gorm:"not null;default:0"`
	AvailableMemoryMi uint32 `gorm:"not null;default:0"`
	WithLocalDisk     bool   `gorm:"not null;default:false"`
	LocalDiskSizeGi   uint32 `gorm:"not null;default:0"`
	Arch              string `gorm:"type:varchar(16);not null;default:'arm64'"` // CPU architecture: "arm64" or "amd64"
	Priority          uint32 `gorm:"not null;default:1000"`                     // scheduling priority (>= 1000 for primary)

	FallbackInstances     string `gorm:"type:text"` // protojson of fallback instances
	DisabledInstanceTypes string `gorm:"type:text"` // protojson of instance types
	Phase                 string `gorm:"type:varchar(32);not null;default:'active';index"`
	CreatedAt             time.Time
	UpdatedAt             time.Time
	DeletedAt             gorm.DeletedAt `gorm:"index"`
}

func (NodeInstanceGroup) TableName() string { return "node_instance_groups" }

// DataplaneNodeInstanceGroupState tracks the current state of a node instance group within the shortage handling
// state machine. Each (DataplaneName, NodeSpec, PrimaryInstance) has its own state.
type DataplaneNodeInstanceGroupState struct {
	ID                 string     `gorm:"type:varchar(128);primaryKey"`
	DataplaneName      string     `gorm:"type:varchar(128);not null;uniqueIndex:idx_state_dp_spec,priority:1"`
	NodeSpec           string     `gorm:"type:varchar(64);not null;uniqueIndex:idx_state_dp_spec,priority:2"` // e.g., "8C16G"
	PrimaryInstance    string     `gorm:"type:varchar(64);not null;uniqueIndex:idx_state_dp_spec,priority:3"`
	State              string     `gorm:"type:varchar(32);not null;default:'normal';index"`
	StateEnteredAt     time.Time  // when the current state was entered (for timeout calculations)
	FencingToken       int64      `gorm:"not null;default:0"` // monotonic counter for distributed lock validation
	LastShortageReason string     `gorm:"type:varchar(512)"`  // reason for the last shortage detection
	CooldownUntil      *time.Time // if set, don't process this state until this time
	CreatedAt          time.Time
	UpdatedAt          time.Time
	DeletedAt          gorm.DeletedAt `gorm:"index"`
}

func (DataplaneNodeInstanceGroupState) TableName() string {
	return "dataplane_node_instance_group_states"
}

// DataplaneNodeInstanceGroupLock implements distributed locking for per-dataplane operations.
// Uses a table-based lease mechanism with fencing tokens for stale lock prevention.
// This table does NOT use soft delete (no DeletedAt) because:
// - (DataplaneName, NodeSpec, PrimaryInstance) is the composite primary key, so soft-deleted rows would block re-creation
// - Lock rows are transient and should be fully removed on release
type DataplaneNodeInstanceGroupLock struct {
	DataplaneName   string    `gorm:"type:varchar(128);primaryKey"` // dataplane identifier
	NodeSpec        string    `gorm:"type:varchar(64);primaryKey"`  // node spec, e.g. "8c16g"
	PrimaryInstance string    `gorm:"type:varchar(64);primaryKey"`  // primary instance type, e.g. "m5.2xlarge"
	OwnerID         string    `gorm:"type:varchar(128);not null"`   // instance ID holding the lock
	FencingToken    int64     `gorm:"not null"`                     // monotonic counter, incremented on each acquisition
	ExpiresAt       time.Time // lease expiration time
	CreatedAt       time.Time
	UpdatedAt       time.Time
}

func (DataplaneNodeInstanceGroupLock) TableName() string {
	return "dataplane_node_instance_group_locks"
}

// DataplaneNodeInstanceGroup stores per-dataplane overrides for node instance groups.
type DataplaneNodeInstanceGroup struct {
	ID              string `gorm:"type:varchar(128);primaryKey"`
	DataplaneName   string `gorm:"type:varchar(128);not null;uniqueIndex:idx_dataplane_spec,priority:1"`
	NodeSpec        string `gorm:"type:varchar(64);not null;uniqueIndex:idx_dataplane_spec,priority:2"`
	PrimaryInstance string `gorm:"type:varchar(64);not null;uniqueIndex:idx_dataplane_spec,priority:3"`
	Status          string `gorm:"type:varchar(32);not null;default:'active';index"`
	CreatedAt       time.Time
	UpdatedAt       time.Time
	DeletedAt       gorm.DeletedAt `gorm:"index"`
}

func (DataplaneNodeInstanceGroup) TableName() string { return "dataplane_node_instance_groups" }
