package entity

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

	"github.com/pingcap/tiflow/dm/pkg/checker"
	"github.com/tidbcloud/pkgv2/clusterproxy/serverless/dm"
	"github.com/tidbcloud/serverless-service/internal/domain/constant"
	globalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/serverless/v1"
	regionalserverlessv1 "github.com/tidbcloud/tidb-management-service/api/spec/regional/serverless/v1"
)

type DMTask struct {
	ID          string     `gorm:"column:id;type:varchar(255);not null;primaryKey"`
	Name        string     `gorm:"column:name;type:varchar(64);not null;uniqueIndex:idx_dm_task_cluster_name"`
	CreatedAt   time.Time  `gorm:"column:created_at;type:TIMESTAMP DEFAULT CURRENT_TIMESTAMP;not null"` // create and read only
	UpdatedAt   *time.Time `gorm:"column:updated_at;type:TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP"`
	DeletedTime *time.Time `gorm:"column:deleted_time;type:TIMESTAMP NULL DEFAULT NULL"`
	FailedTime  *time.Time `gorm:"column:failed_time;type:TIMESTAMP NULL DEFAULT NULL"`

	Stage  constant.DMTaskStage `gorm:"column:stage;type:enum('sourceCreating','sourceBounding','taskCreating','running','stopped','deleting','deleted','failed');index:idx_dm_task_stage"`
	ErrMsg string               `gorm:"column:err_msg;type:text"`

	Creator       string `gorm:"column:creator;type:VARCHAR(255)"`
	ClusterID     string `gorm:"column:cluster_id;type:varchar(255);not null;index;uniqueIndex:idx_dm_task_cluster_name"`
	TenantID      string `gorm:"column:tenant_id;type:varchar(255);not null;index"`
	CloudProvider string `gorm:"column:cloud_provider;type:VARCHAR(255);not null"`
	Region        string `gorm:"column:region;type:VARCHAR(255);not null"`

	TargetUser              string `gorm:"column:target_user;type:VARCHAR(255)"`               // target user for DM task, used for DM worker to connect to target cluster
	TargetEncryptedPassword string `gorm:"column:target_encrypted_password;type:VARCHAR(255)"` // encrypted password for target user

	Mode            constant.DMTaskMode `gorm:"column:mode;type:enum('full','incremental','all');not null"`
	ShardMode       *dm.TaskShardMode   `gorm:"column:shard_mode;type:VARCHAR(32) NULL DEFAULT NULL"`
	ClusterStatusID uint64              `gorm:"column:cluster_status_id;type:bigint(20) NULL DEFAULT NULL"`

	DMSources []DMSource
}

type DMSource struct {
	ID          string     `gorm:"column:id;type:varchar(255);not null;primaryKey"`
	CreatedAt   time.Time  `gorm:"column:created_at;type:TIMESTAMP DEFAULT CURRENT_TIMESTAMP;not null"` // create and read only
	UpdatedAt   *time.Time `gorm:"column:updated_at;type:TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP"`
	DeletedTime *time.Time `gorm:"column:deleted_time;type:TIMESTAMP NULL DEFAULT NULL"`

	Stage constant.DMSourceStage `gorm:"column:stage;type:enum('creating','bounding','bounded','deleting','deleted')"`

	Creator    string          `gorm:"column:creator;type:VARCHAR(255)"`
	ClusterID  string          `gorm:"column:cluster_id;type:varchar(255);not null"`
	TenantID   string          `gorm:"column:tenant_id;type:varchar(255);not null"`
	SourceType constant.DBType `gorm:"column:source_type;type:enum('mysql','alicloud-rds-mysql');not null;default:mysql"`

	BinlogName string `gorm:"column:binlog_name;type:VARCHAR(256)"`
	BinlogPos  int32  `gorm:"column:binlog_pos;type:INT"`
	BinlogGTID string `gorm:"column:binlog_gtid;type:VARCHAR(255)"`

	ConnProfile     ConnProfile     `gorm:"column:conn_profile;type:JSON"`
	EndpointID      string          `gorm:"column:endpoint_id;type:varchar(255);index"`
	EndpointDomain  string          `gorm:"column:endpoint_domain;type:varchar(255)"`
	BlockAllowRules BlockAllowRules `gorm:"column:block_allow_rules;type:JSON"`
	MigrationRules  MigrationRules  `gorm:"column:migration_rules;type:JSON"`

	SubTaskStatus *SubTaskStatus `gorm:"column:sub_task_status;type:JSON"`

	DMTaskID string `gorm:"column:dm_task_id;type:varchar(255);not null;index"`
}

type SubTaskStatus dm.SubTaskStatus

//nolint:gocritic
func (s SubTaskStatus) Value() (driver.Value, error) {
	b, err := json.Marshal(s)
	return string(b), err
}

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

type ConnProfile struct {
	Type     constant.ConnType
	Target   string
	Port     int32
	User     string
	Password string
	Security *Security
}

//nolint:gocritic
func (t ConnProfile) Value() (driver.Value, error) {
	b, err := json.Marshal(t)
	return string(b), err
}

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

type Security struct {
	CertAllowedCn  []string
	SslCaContent   []byte
	SslCertContent []byte
	SslKeyContent  []byte
}

//nolint:gocritic
func (t Security) Value() (driver.Value, error) {
	b, err := json.Marshal(t)
	return string(b), err
}

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

type BlockAllowRules struct {
	DoDBs    []string
	DoTables []Table
}

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

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

type Table struct {
	Schema string
	Table  string
}

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

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

// MigrationRules represents an array of MigrationRule persisted as JSON
type MigrationRules []MigrationRule

// MigrationRule describes how to route from a source pattern to a target table
type MigrationRule struct {
	Source MigrationRuleSource `json:"source"`
	Target MigrationRuleTarget `json:"target"`
}

// MigrationRuleSource defines the pattern of source schema/table
type MigrationRuleSource struct {
	SchemaPattern string `json:"schema_pattern"`
	TablePattern  string `json:"table_pattern"`
}

// MigrationRuleTarget defines the concrete target schema/table
type MigrationRuleTarget struct {
	Schema string `json:"schema"`
	Table  string `json:"table"`
}

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

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

type DMTaskFilter struct {
	ClusterID     string
	TenantID      string
	Stages        []constant.DMTaskStage
	Offset        *int
	Limit         *int
	OrderBy       string
	FailedTimeGTE *time.Time
}

type DMPrecheck struct {
	ID           string     `gorm:"column:id;type:varchar(255);not null;primaryKey"`
	CreatedAt    time.Time  `gorm:"column:created_at;type:TIMESTAMP DEFAULT CURRENT_TIMESTAMP;not null"` // create and read only
	UpdatedAt    *time.Time `gorm:"column:updated_at;type:TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP"`
	CanceledTime *time.Time `gorm:"column:canceled_time;type:TIMESTAMP NULL DEFAULT NULL"`

	Creator   string `gorm:"column:creator;type:VARCHAR(255)"`
	ClusterID string `gorm:"column:cluster_id;type:varchar(255);not null;index"`
	TenantID  string `gorm:"column:tenant_id;type:varchar(255);not null;index"`

	Request *PrecheckRequest `gorm:"column:request;type:JSON"`

	Stage constant.DMPrecheckStage `gorm:"column:stage;type:enum('pending','running','finished','failed','canceled');index"`

	TotalItems      int32 `gorm:"column:total_items;type:INT"`
	DMPrecheckItems []DMPrecheckItem
}

type DMPrecheckItem struct {
	ID           string                                       `gorm:"column:id;type:varchar(255);not null;primaryKey"`
	CreatedAt    time.Time                                    `gorm:"column:created_at;type:TIMESTAMP DEFAULT CURRENT_TIMESTAMP;not null"` // create and read only
	UpdatedAt    *time.Time                                   `gorm:"column:updated_at;type:TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP"`
	CanceledTime *time.Time                                   `gorm:"column:canceled_time;type:TIMESTAMP NULL DEFAULT NULL"`
	Stage        constant.DMPrecheckStage                     `gorm:"column:stage;type:enum('pending','running','finished','failed','canceled');index:idx_dm_precheck_item_stage"`
	Description  string                                       `gorm:"column:description;type:varchar(1024);not null"`
	Type         regionalserverlessv1.DMPrecheck_PrecheckItem `gorm:"column:type;type:INT;not null"`
	Results      Results                                      `gorm:"column:results;type:JSON"`

	DMPrecheckID string `gorm:"column:dm_precheck_id;type:varchar(255);not null;index:idx_dm_precheck_item_precheck"`
}

type PrecheckRequest globalserverlessv1.ServerlessDMServicePrecheckRequest

func (t *PrecheckRequest) Value() (driver.Value, error) {
	b, err := json.Marshal(t)
	return string(b), err
}

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

type Results checker.Results

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

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

type DMPrecheckFilter struct {
	ClusterID string
	TenantID  string
	Stages    []constant.DMPrecheckStage
	Offset    *int
	Limit     *int
	OrderBy   string
}

// DMVpcEndpoint represents a persisted VPC endpoint created for a cluster/service
type DMVpcEndpoint struct {
	ID          string     `gorm:"column:id;type:varchar(255);not null;primaryKey"`
	ClusterID   string     `gorm:"column:cluster_id;type:varchar(255);not null;index"`
	CreatedAt   time.Time  `gorm:"column:created_at;type:TIMESTAMP DEFAULT CURRENT_TIMESTAMP;not null"` // create and read only
	UpdatedAt   *time.Time `gorm:"column:updated_at;type:TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP"`
	DeletedTime *time.Time `gorm:"column:deleted_time;type:TIMESTAMP NULL DEFAULT NULL"`
	Creator     string     `gorm:"column:creator;type:VARCHAR(255)"`

	ServiceName  string                      `gorm:"column:service_name;type:varchar(255);not null;index"`
	EndpointId   string                      `gorm:"column:endpoint_id;type:varchar(255)"`
	State        constant.DMVpcEndpointState `gorm:"column:state;type:enum('pending','pendingAcceptance','available','deleting','failed','deleted')"`
	VpcId        string                      `gorm:"column:vpc_id;type:varchar(255)"`
	CreationTime string                      `gorm:"column:creation_time_str;type:varchar(128)"`
	Domain       string                      `gorm:"column:domain;type:varchar(255)"`

	DMTaskID *string `gorm:"column:dm_task_id;type:varchar(255);index;default:null"`
}

type VpcEndpointFilter struct {
	ClusterID string
	States    []constant.DMVpcEndpointState
	Offset    *int
	Limit     *int
	OrderBy   string
	// CreatedBefore filters endpoints whose created_at is earlier than this time
	CreatedBefore *time.Time
	// EmptyTaskID filters endpoints that have no associated DMTaskID (empty string)
	EmptyTaskID bool
	// TaskStageIn selects endpoints whose associated DMTask is in any of these stages
	// This requires a join with dm_tasks on dm_vpc_endpoints.dm_task_id = dm_tasks.id
	TaskStageIn []constant.DMTaskStage
}
