package migrate

import (
	"fmt"
	"sort"

	gormigrate "github.com/go-gormigrate/gormigrate/v2"
	"gorm.io/gorm"
)

// RunCentralMigrate runs migration queue against the given gorm db.
// One must call `migrate.AddCentralMigrateRecord` first to init migration queue.
func RunCentralMigrate(db *gorm.DB) error {
	db.DisableForeignKeyConstraintWhenMigrating = true
	return doMigrate(db, initCentralDB, centralDBMigrationRecords)
}

// AddCentralMigrateRecord add a migration functions to migration queue.
// @param - m - the migration function.
func AddCentralMigrateRecord(version int, m func(*gorm.DB) error) bool {
	checkDuplicateMigrationVersion(version, centralDBMigrationRecords)
	centralDBMigrationRecords = append(centralDBMigrationRecords, &migration{
		version: version,
		migrate: m,
	})
	return true
}

var centralDBMigrationRecords []*migration

type migration struct {
	migrate func(*gorm.DB) error
	version int
}

// byVersion implements sort.Interface based on the version field.
type byVersion []*migration

func (a byVersion) Len() int           { return len(a) }
func (a byVersion) Less(i, j int) bool { return a[i].version < a[j].version }
func (a byVersion) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

//todo: backup and restore database
func doMigrate(db *gorm.DB, initSchema gormigrate.InitSchemaFunc, migrations []*migration) error {
	sort.Sort(byVersion(migrations))
	var gormMigrations []*gormigrate.Migration
	for _, item := range migrations {
		gormMigrations = append(gormMigrations, &gormigrate.Migration{
			ID:      fmt.Sprintf("%d", item.version),
			Migrate: item.migrate,
		})
	}
	goMigration := gormigrate.New(db, gormigrate.DefaultOptions, gormMigrations)
	goMigration.InitSchema(initSchema)
	return goMigration.Migrate()
}

func checkDuplicateMigrationVersion(version int, records []*migration) {
	for _, item := range records {
		if item.version == version {
			panic(fmt.Sprintf("migration version is already added: %d", version))
		}
	}
}

func GetCentralDBVersion() int {
	return centralDBMigrationRecords[0].version
}
