package migration

import (
	"github.com/go-gormigrate/gormigrate/v2"
	"gorm.io/gorm"

	"github.com/tidbcloud/tidb-management-service/tidb-mgmt-service/infra/repository/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/clusterevent"
	"github.com/tidbcloud/tidb-management-service/utilities/lease"
)

var Records = []*gormigrate.Migration{
	// Add your migration record here.
	{
		ID: "2026090301",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.FsRegion{})
		},
	},
	{
		ID: "2026090201",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.FsRegion{})
		},
	},
	{
		ID: "2026083101",
		Migrate: func(g *gorm.DB) error {
			if !g.Migrator().HasTable(&schema.DedicatedToPremiumPrecheckItem{}) {
				return nil
			}

			return g.Exec(
				"UPDATE `dedicated_to_premium_precheck_items` SET `category` = ? WHERE `category` = ?",
				"MIGRATION_READINESS",
				"BR_TICDC_COMPATIBILITY",
			).Error
		},
	},
	{
		ID: "2026082401",
		Migrate: func(g *gorm.DB) error {
			indexDDLs := []struct {
				table string
				name  string
				sql   string
			}{
				{
					table: "events",
					name:  "idx_events_topic_status_deleted_ts",
					sql: "CREATE INDEX idx_events_topic_status_deleted_ts " +
						"ON events (topic(191), status, deleted_at, `timestamp`, id)",
				},
				{
					table: "events",
					name:  "idx_events_status_created",
					sql: "CREATE INDEX idx_events_status_created " +
						"ON events (status, created_at)",
				},
				{
					table: "clusters",
					name:  "idx_clusters_deleted_created",
					sql: "CREATE INDEX idx_clusters_deleted_created " +
						"ON clusters (deleted_at, created_at DESC)",
				},
				{
					table: "clusters",
					name:  "idx_clusters_deploy_plan_deleted_created",
					sql: "CREATE INDEX idx_clusters_deploy_plan_deleted_created " +
						"ON clusters (deploy_type, service_plan, deleted_at, created_at DESC)",
				},
				{
					table: "clusters",
					name:  "idx_clusters_org_deploy_plan_deleted",
					sql: "CREATE INDEX idx_clusters_org_deploy_plan_deleted " +
						"ON clusters (organization_id, deploy_type, service_plan, deleted_at)",
				},
				{
					table: "clusters",
					name:  "idx_clusters_shadowpool_state_deleted",
					sql: "CREATE INDEX idx_clusters_shadowpool_state_deleted " +
						"ON clusters (shadow_pool_id, shadow_state, deleted_at)",
				},
			}

			for _, item := range indexDDLs {
				if !g.Migrator().HasTable(item.table) {
					continue
				}
				if g.Migrator().HasIndex(item.table, item.name) {
					continue
				}
				if err := g.Exec(item.sql).Error; err != nil {
					return err
				}
			}

			return nil
		},
	},
	{
		ID: "20260820",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.DedicatedToPremiumPrecheck{})
		},
	},
	{
		ID: "20260817",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.DedicatedToPremiumPrecheck{})
		},
	},
	{
		ID: "20260811",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(
				&schema.DedicatedToPremiumPrecheck{},
				&schema.DedicatedToPremiumPrecheckItem{},
			)
		},
	},
	{
		ID: "1",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.Cluster{})
		},
	},
	{
		ID: "2",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.ClusterCustomStatus{})
		},
	},
	{
		ID: "202400108",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.Cluster{})
		},
	},
	{
		ID: "20240118",
		Migrate: func(g *gorm.DB) error {
			// In most cases, the cluster_custom_statuses table has no data,
			// so we need to check it before making changes.
			// Deleting rename operations after changes are made
			err := g.Migrator().RenameTable("cluster_custom_statuses", "cluster_custom_statuses_old")
			if err != nil {
				return err
			}
			return g.AutoMigrate(&schema.ClusterCustomStatus{})
		},
	},
	{
		ID: "202400120",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.Cluster{})
		},
	},
	{
		ID: "20240424",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(
				&schema.MaintenanceWindow{},
				&schema.MaintenanceTask{},
				&schema.MaintenanceTaskExecution{},
				&clusterevent.EventModel{},
			)
		},
	},
	{
		// Tempo proxy merge response from mgmt and core. For one entity id, only one side should success.
		// We must ensure there is no same id between mgmt and core.
		ID: "20240425",
		Migrate: func(g *gorm.DB) error {
			if err := g.Exec("ALTER TABLE `maintenance_windows` AUTO_INCREMENT = 1000000000").Error; err != nil {
				return err
			}
			if err := g.Exec("ALTER TABLE `maintenance_tasks` AUTO_INCREMENT = 1000000000").Error; err != nil {
				return err
			}
			if err := g.Exec("ALTER TABLE `maintenance_task_executions` AUTO_INCREMENT = 1000000000").Error; err != nil {
				return err
			}
			return nil
		},
	},
	{
		ID: "20241015",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.Cluster{})
		},
	},
	{
		ID: "20250609",
		Migrate: func(g *gorm.DB) error {
			if err := g.AutoMigrate(&schema.Cluster{}); err != nil {
				return err
			}
			return g.Exec(
				"ALTER TABLE `clusters` MODIFY COLUMN `deploy_type` enum('DEPLOY_TYPE_DEDICATED', 'DEPLOY_TYPE_SERVERLESS', 'DEPLOY_TYPE_NEXTGEN') NOT NULL",
			).Error
		},
	},
	{
		ID: "20250929",
		Migrate: func(g *gorm.DB) error {
			// select all not deleted cdc custom statuses and update the muteux status type to empty
			if err := g.Exec(
				"UPDATE cluster_custom_statuses SET custom_mutex = JSON_REMOVE(COALESCE(custom_mutex, JSON_OBJECT()), '$.status_type'), updated_at = NOW(3) WHERE status_type = 'CUSTOM_STATUS_TYPE_CDC' AND deleted_at IS NULL",
			).Error; err != nil {
				return err
			}
			// select all not deleted import custom statuses and update the muteux status type to empty
			if err := g.Exec(
				"UPDATE cluster_custom_statuses SET custom_mutex = JSON_REMOVE(COALESCE(custom_mutex, JSON_OBJECT()), '$.status_type'), updated_at = NOW(3) WHERE status_type = 'CUSTOM_STATUS_TYPE_IMPORT' AND deleted_at IS NULL",
			).Error; err != nil {
				return err
			}
			return nil
		},
	},
	{
		ID: "20251020",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.MigrationCluster{})
		},
	},
	{
		ID: "20251125",
		Migrate: func(g *gorm.DB) error {
			//
			if err := g.Exec(
				"update clusters set service_plan='Essential' where deploy_type='DEPLOY_TYPE_SERVERLESS' and cluster_id in (10866561689819670783,10996243763753172258,10013823542496040222,10644079912408521757,10955674733028894371,10748908140467772577,10696719661066021061,10137874632072101016,10198021976582142638,10583422775462737012,10519467875922537427,10769646744068751967,10891677148905786771,10151312725707072181,10350162221383496533,10849008254506931650,10083138255351454426,10797785821275351267,10256748627750804759,10687257136483254993,10181088182237415904,10411037257771061535,10132274027719704687,10549739251385696265,10460503022297867029,10770148451104421564,10873403169922872353,10274331272236652513,10651326047252223753,10753978988071424710,10988197146074943750,10336249301302063444,10799264498379404243,10558189045808951023,10258512238919666581,10050232226720247615,10034767802846689594,10768354069202094086,10312408252929644465,10195445195341557389,10896434555262688189,10710432231385023393,10364640759505998874,10815976627889079634,10464050259600801438,10903236587268854792,10674819552346637589,10084544325586168868,10288851202148744803,10671233990548293202,10821642679432589811)",
			).Error; err != nil {
				return err
			}
			return nil
		},
	},
	{
		ID: "20251201",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.MigrationCluster{})
		},
	},
	{
		ID: "20251205",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.MigrationCluster{})
		},
	},
	{
		ID: "20251208",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.MigrationCluster{})
		},
	},
	{
		ID: "20251209",
		Migrate: func(g *gorm.DB) error {
			// Change br_custom_options column from VARCHAR(255) to TEXT to support longer strings
			return g.Exec(
				"ALTER TABLE `migration_clusters` MODIFY COLUMN `br_custom_options` TEXT",
			).Error
		},
	},
	{
		ID: "20251217",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.MigrationCluster{})
		},
	},
	{
		ID: "20260119",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.Cluster{})
		},
	},
	{
		ID: "20260204",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.DataSynchronization{})
		},
	},
	{
		ID: "20260205",
		Migrate: func(g *gorm.DB) error {
			err := g.AutoMigrate(&schema.Cluster{})
			if err != nil {
				return err
			}
			err = g.AutoMigrate(&schema.ShadowPool{})
			if err != nil {
				return err
			}
			return nil
		},
	},
	{
		ID: "20260206",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.ActivationCode{})
		},
	},
	{
		ID: "20260226",
		Migrate: func(g *gorm.DB) error {
			return g.Exec(
				"update clusters set service_plan = 'Dedicated' where deleted_at is null and deploy_type = 'DEPLOY_TYPE_DEDICATED' and service_plan = 'SERVICE_PLAN_UNSPECIFIED'",
			).Error
		},
	},
	{
		ID: "20260228",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.ZeroRentRecord{})
		},
	},
	{
		ID: "20260305",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.ZeroRentRecord{})
		},
	},
	{
		ID: "20260306",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.ProjectMigrationTask{})
		},
	},
	{
		ID: "20260311",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.ShadowPool{})
		},
	},
	{
		ID: "20260312",
		Migrate: func(g *gorm.DB) error {
			if err := g.AutoMigrate(&schema.OrganizationRuConfig{}); err != nil {
				return err
			}
			// Add ru_version column to clusters table
			return g.AutoMigrate(&schema.Cluster{})
		},
	},
	{
		ID: "20260325",
		Migrate: func(g *gorm.DB) error {
			// This migration is intentionally re-entrant and only affects orgs that
			// currently match the mixed-project criteria:
			// 1. create a new task for newly matched mixed-project orgs,
			// 2. refresh SUCCEED/PENDING tasks for currently mixed-project orgs
			//    based on the latest cluster snapshot (via upsert),
			// 3. keep RUNNING/FAILED tasks untouched, and leave tasks for orgs that
			//    no longer match the mixed-project criteria unchanged.
			return g.Exec(
				`INSERT INTO project_migration_tasks (created_at, updated_at, org_id, state, migrations)
				SELECT
					NOW(3),
					NOW(3),
					organization_id,
					'PENDING',
					JSON_ARRAYAGG(
						JSON_OBJECT('source_project_id', project_id)
					)
				FROM (
					SELECT organization_id, project_id
					FROM clusters
					WHERE deleted_at IS NULL
					GROUP BY organization_id, project_id
					HAVING
						SUM(CASE WHEN deploy_type = 'DEPLOY_TYPE_DEDICATED' THEN 1 ELSE 0 END) > 0
						AND
						SUM(CASE WHEN deploy_type = 'DEPLOY_TYPE_SERVERLESS' THEN 1 ELSE 0 END) > 0
				) AS mixed_projects
				GROUP BY organization_id
				ON DUPLICATE KEY UPDATE
					state = IF(
						state IN ('PENDING', 'SUCCEED'),
						'PENDING',
						state
					),
					updated_at = IF(
						state IN ('PENDING', 'SUCCEED'),
						VALUES(updated_at),
						updated_at
					),
					migrations = IF(
						state IN ('PENDING', 'SUCCEED'),
						VALUES(migrations),
						migrations
					)`,
			).Error
		},
	},
	{
		ID: "20260403",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.ResourceTag{}, &schema.ClusterTagBinding{})
		},
	},
	{
		ID: "2026080401",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&lease.Lease{})
		},
	},
	{
		ID: "20260416",
		Migrate: func(g *gorm.DB) error {
			if !g.Migrator().HasColumn("migration_clusters", "restore_mode") {
				if err := g.Exec(
					"ALTER TABLE `migration_clusters` " +
						"ADD COLUMN `restore_mode` VARCHAR(64) NOT NULL DEFAULT '' AFTER `backup_id`",
				).Error; err != nil {
					return err
				}
			}

			if !g.Migrator().HasColumn("migration_clusters", "target_region_id") {
				if err := g.Exec(
					"ALTER TABLE `migration_clusters` " +
						"ADD COLUMN `target_region_id` VARCHAR(64) NOT NULL DEFAULT '' AFTER `target_project_id`",
				).Error; err != nil {
					return err
				}
			}

			if !g.Migrator().HasColumn("migration_clusters", "backup_region_id") {
				if err := g.Exec(
					"ALTER TABLE `migration_clusters` " +
						"ADD COLUMN `backup_region_id` VARCHAR(64) NOT NULL DEFAULT '' AFTER `target_region_id`",
				).Error; err != nil {
					return err
				}
			}

			return nil
		},
	},
	{
		ID: "2026081701",
		Migrate: func(g *gorm.DB) error {
			return g.AutoMigrate(&schema.Cluster{})
		},
	},
}
