package migration

import (
	"cmp"
	"context"
	"fmt"
	"slices"

	"github.com/go-gormigrate/gormigrate/v2"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"gorm.io/gorm"

	"github.com/tidbcloud/tidb-management-service/dedicated-global-service/infra/repoimpl/schema"
)

var dbMigrationRecords []*migrationRecord

type migrationRecord struct {
	migrate func(*gorm.DB) error
	version string
}

// RunDBMigrate runs migration queue against the given gorm db.
// One must call `migrate.AddDBMigrateRecord` first to init migration queue.
func RunDBMigrate(db *gorm.DB) error {
	slices.SortFunc(dbMigrationRecords, func(a, b *migrationRecord) int {
		return cmp.Compare(a.version, b.version)
	})
	var gormMigrations []*gormigrate.Migration
	for _, item := range dbMigrationRecords {
		gormMigrations = append(gormMigrations, &gormigrate.Migration{
			ID:      item.version,
			Migrate: item.migrate,
		})
	}
	options := &gormigrate.Options{
		TableName:                 "dedicated_global_service_migrations",
		IDColumnName:              "id",
		IDColumnSize:              255,
		UseTransaction:            false,
		ValidateUnknownMigrations: false,
	}
	goMigration := gormigrate.New(db, options, gormMigrations)
	goMigration.InitSchema(initSchema)
	return goMigration.Migrate()
}

// AddDBMigrateRecord add a migration functions to migration queue.
func AddDBMigrateRecord(version string, m func(*gorm.DB) error) bool {
	for _, item := range dbMigrationRecords {
		if item.version == version {
			panic(fmt.Sprintf("migration version is already added: %s", version))
		}
	}

	dbMigrationRecords = append(dbMigrationRecords, &migrationRecord{
		version: version,
		migrate: m,
	})
	return true
}

func initSchema(db *gorm.DB) error {
	// create the tables in an empty database
	if err := db.AutoMigrate(schema.Models...); err != nil {
		return err
	}
	tilog.Info(context.Background(), "auto migrate schema successfully")
	return nil
}
