package db

import (
	"fmt"
	"math"
	"time"

	"github.com/pingcap/log"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

var _ = AddMigrateRecord(
	1,
	func(db *gorm.DB) error {
		if _, err := createRoleForSuperGroup(db, "approver", "operation", 1); err != nil {
			return err
		}

		role, err := createRoleForSuperGroup(db, "admin", "user", 7)
		if err != nil {
			return err
		}

		var u User
		if err := NewUserQuerySet(db).UserNameEq("qifangfang@pingcap.com").InternalEq(true).One(&u); err != nil {
			if err == gorm.ErrRecordNotFound {
				return nil
			}

			return err
		}

		return db.Exec("INSERT INTO casbin_rule(p_type, v0, v1, v2) values(?, ?, ?, ?)", "g", fmt.Sprintf("user/%d", u.ID), fmt.Sprintf("role/%d", role.ID), role.OrganizationID).Error
	},
)

func createRoleForSuperGroup(db *gorm.DB, roleName, resource string, action uint) (*Role, error) {
	role := &Role{
		Model: gorm.Model{
			CreatedAt: time.Now(),
			UpdatedAt: time.Now(),
		},
		RoleName:       roleName,
		OrganizationID: math.MaxUint,
		Description:    roleName,
	}

	if err := db.Transaction(func(tx *gorm.DB) error {
		return tx.Create(role).Error
	}); err != nil {
		return nil, err
	}

	return role, db.Exec("INSERT INTO casbin_rule(p_type, v0, v1, v2, v3) values(?, ?, ?, ?, ?)", "p", fmt.Sprintf("role/%d", role.ID), role.OrganizationID, resource, action).Error
}

var _ = AddMigrateRecord(
	3,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&ComparisonReport{})
	},
)

var _ = AddMigrateRecord(4,
	func(db *gorm.DB) error {
		if err := db.Exec("ALTER TABLE health_reports ADD COLUMN `start_unix` bigint unsigned NOT NULL DEFAULT 0, ADD COLUMN `end_unix` bigint unsigned NOT NULL DEFAULT 0, ADD COLUMN `org_id` bigint unsigned NOT NULL DEFAULT 0, ADD COLUMN `cluster_id` VARCHAR(255) NOT NULL DEFAULT '', ADD INDEX idx_cluster_id (`cluster_id`);").Error; err != nil {
			log.Error("do migration failed", zap.Error(err))
			return err
		}
		if err := db.Exec("UPDATE health_reports JOIN upload_files ON health_reports.file_uuid = upload_files.file_uuid SET health_reports.cluster_id = upload_files.cluster_id, health_reports.org_id = upload_files.org_id WHERE health_reports.org_type != 'cloud'").Error; err != nil {
			log.Error("do migration failed", zap.Error(err))
			return err
		}
		return nil
	})

var _ = AddMigrateRecord(
	2,
	func(db *gorm.DB) error {
		if err := db.Exec("alter table clinic.upload_files add column upload_status VARCHAR(15) default 'running'").Error; err != nil {
			return err
		}

		return db.Exec("update clinic.upload_files set upload_status = 'success' where completed = 1").Error
	},
)

var _ = AddMigrateRecord(
	5,
	func(db *gorm.DB) error {
		if err := db.Exec("alter table clinic.clusters MODIFY COLUMN status VARCHAR(25)").Error; err != nil {
			return err
		}

		return db.Exec("update clinic.clusters set status = 'normal'").Error
	},
)

var _ = AddMigrateRecord(
	6,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Report{})
	},
)

var _ = AddMigrateRecord(
	10,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&UploadFile{})
	},
)

var _ = AddMigrateRecord(
	11,
	func(db *gorm.DB) error {
		return db.Exec("alter table org_quota add column `rebuild_concurrency` bigint default 1").Error
	},
)

var _ = AddMigrateRecord(
	12,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Slogan{})
	},
)

var _ = AddMigrateRecord(
	15,
	func(db *gorm.DB) error {
		return db.Exec(`insert into users (user_name,email,external_identifier,internal) values ("clinic@pingcap.com", "clinic@pingcap.com", "clinic@pingcap.com", true)`).Error
	},
)

var _ = AddMigrateRecord(16,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&APICredential{})
	},
)

var _ = AddMigrateRecord(19,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&UploadFile{})
	},
)

var _ = AddMigrateRecord(20,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Report{})
	},
)

var _ = AddMigrateRecord(21,
	func(db *gorm.DB) error {
		if err := db.AutoMigrate(&TiDBDefaultConfiguration{}); err != nil {
			return err
		}

		if err := db.AutoMigrate(&TiKVDefaultConfiguration{}); err != nil {
			return err
		}

		if err := db.AutoMigrate(&PDDefaultConfiguration{}); err != nil {
			return err
		}

		return db.AutoMigrate(&TiFlashDefaultConfiguration{})
	},
)

var _ = AddMigrateRecord(26,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(32,
	func(db *gorm.DB) error {
		_ = db.Migrator().DropIndex(&Organization{}, "org_key")
		return db.AutoMigrate(&Organization{})
	},
)

var _ = AddMigrateRecord(35,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(36,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Organization{})
	},
)

var _ = AddMigrateRecord(41,
	func(db *gorm.DB) error {
		_ = db.Migrator().DropColumn(&Cluster{}, "creation_finished_at")
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(42,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(43,
	func(db *gorm.DB) error {
		return db.AutoMigrate(
			&PerformanceInsightCluster{},
			&PerformanceInsightTask{},
			&PlanInsightResultStatement{},
			&PlanInsightResultPlan{},
		)
	},
)

var _ = AddMigrateRecord(44,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&UploadFile{})
	},
)

var _ = AddMigrateRecord(46,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&IndexAdvisorResult{})
	},
)

var _ = AddMigrateRecord(50,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(51,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&PerformanceInsightTask{})
	},
)

var _ = AddMigrateRecord(60,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(70,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(80,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Task{})
	},
)

var _ = AddMigrateRecord(81,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Organization{})
	},
)

var _ = AddMigrateRecord(82,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(83,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Organization{})
	},
)

// Add parent_id
var _ = AddMigrateRecord(84,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

// Change deploy_type length
var _ = AddMigrateRecord(85,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

// Change change status default value
var _ = AddMigrateRecord(86,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

var _ = AddMigrateRecord(87,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&InternalAPICredential{})
	},
)

// Add health check strategy and result tables
var _ = AddMigrateRecord(88,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&HealthCheckStrategy{}, &HealthCheckResult{})
	},
)

// Add time range expression fields to health_check_strategies for relative time support
var _ = AddMigrateRecord(89,
	func(db *gorm.DB) error {
		// Use raw SQL to add columns instead of AutoMigrate to avoid GORM constraint issues
		columns := []string{
			"time_range_a_start_expr",
			"time_range_a_end_expr",
			"time_range_b_start_expr",
			"time_range_b_end_expr",
		}
		for _, col := range columns {
			if !db.Migrator().HasColumn(&HealthCheckStrategy{}, col) {
				if err := db.Exec("ALTER TABLE health_check_strategies ADD COLUMN " + col + " VARCHAR(255)").Error; err != nil {
					return err
				}
			}
		}
		return nil
	},
)

// Index clusters.org_id for join organizations and dashboard numeric search (dbaas_org_id branch).
var _ = AddMigrateRecord(90,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&Cluster{})
	},
)

// Index deploy_type_v2 for dashboard default filtering and created_at ordering.
var _ = AddMigrateRecord(91,
	func(db *gorm.DB) error {
		if db.Migrator().HasIndex(&Cluster{}, ClusterDeployTypeV2CreatedAtIndex) {
			return nil
		}
		return db.Exec("CREATE INDEX " + ClusterDeployTypeV2CreatedAtIndex + " ON clusters (deploy_type_v2, created_at)").Error
	},
)

// Index health_check_results for backlog scans ordered by oldest pending job.
var _ = AddMigrateRecord(92,
	func(db *gorm.DB) error {
		const indexName = "idx_health_check_results_status_created_at_id"
		if db.Migrator().HasIndex(&HealthCheckResult{}, indexName) {
			return nil
		}
		return db.Exec("CREATE INDEX " + indexName + " ON health_check_results (status, created_at, id)").Error
	},
)

// Index report task tables for backlog scans ordered by oldest pending/running job.
var _ = AddMigrateRecord(93,
	func(db *gorm.DB) error {
		indexes := []struct {
			name  string
			model any
			table string
		}{
			{name: "idx_health_reports_status_created_at_id", model: &HealthReport{}, table: "health_reports"},
			{name: "idx_comparison_reports_status_created_at_id", model: &ComparisonReport{}, table: "comparison_reports"},
			{name: "idx_reports_status_created_at_id", model: &Report{}, table: "reports"},
		}
		for _, idx := range indexes {
			if db.Migrator().HasIndex(idx.model, idx.name) {
				continue
			}
			if err := db.Exec("CREATE INDEX " + idx.name + " ON " + idx.table + " (status, created_at, id)").Error; err != nil {
				return err
			}
		}
		return nil
	},
)

// Index exact organization searches in their default created_at order.
var _ = AddMigrateRecord(94,
	func(db *gorm.DB) error {
		if db.Migrator().HasIndex(&Cluster{}, ClusterOrgIDCreatedAtIndex) {
			return nil
		}
		return db.Exec("ALTER TABLE clusters ADD INDEX " + ClusterOrgIDCreatedAtIndex + " (org_id, created_at), ALGORITHM=INPLACE, LOCK=NONE").Error
	},
)

// Index exact cluster-name searches in their default created_at order. Name is
// LONGTEXT in existing databases; production data is at most 64 bytes long.
var _ = AddMigrateRecord(95,
	func(db *gorm.DB) error {
		if db.Migrator().HasIndex(&Cluster{}, ClusterNameCreatedAtIndex) {
			return nil
		}
		return db.Exec("ALTER TABLE clusters ADD INDEX " + ClusterNameCreatedAtIndex + " (name(64), created_at), ALGORITHM=INPLACE, LOCK=NONE").Error
	},
)

// Cache logical-cluster to O11y VM-account routes for Essential V2 shared
// resource dashboards. The clusters table remains the membership source of
// truth, which keeps stale cache rows harmless after deletion or migration.
var _ = AddMigrateRecord(96,
	func(db *gorm.DB) error {
		return db.AutoMigrate(&ResourcePoolO11yRoute{})
	},
)
