package migrate

import (
	"fmt"
	"sort"

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

var dbMigrationRecords []*migrationRecord

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

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

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] }

// 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 {
	db.DisableForeignKeyConstraintWhenMigrating = true // disable foreign key constraint

	sort.Sort(byVersion(dbMigrationRecords))
	var gormMigrations []*gormigrate.Migration
	for _, item := range dbMigrationRecords {
		gormMigrations = append(gormMigrations, &gormigrate.Migration{
			ID:      item.version,
			Migrate: item.migrate,
		})
	}
	options := &gormigrate.Options{
		TableName:                 "serverless_migrations",
		IDColumnName:              "id",
		IDColumnSize:              255, //nolint: gomnd // not need
		UseTransaction:            false,
		ValidateUnknownMigrations: false,
	}
	goMigration := gormigrate.New(db, options, gormMigrations)
	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
}
