package initialize

import (
	"errors"
	"time"

	gomigrate "github.com/golang-migrate/migrate/v4"
	"github.com/perfect-panel/server/internal/model/user"
	"gorm.io/gorm"

	"github.com/perfect-panel/server/initialize/migrate"
	"github.com/perfect-panel/server/internal/svc"
	"github.com/perfect-panel/server/pkg/logger"
	"github.com/perfect-panel/server/pkg/orm"
)

func Migrate(ctx *svc.ServiceContext) {
	mc := orm.Mysql{
		Config: ctx.Config.MySQL,
	}
	now := time.Now()
	migrator := migrate.Migrate(mc.Dsn())
	if err := migrator.Up(); err != nil {
		if errors.Is(err, migrate.NoChange) {
			logger.Info("[Migrate] database not change")
		} else {
			var dirtyErr gomigrate.ErrDirty
			if errors.As(err, &dirtyErr) {
				logger.Errorf("[Migrate] Dirty database version %d detected, trying auto-recovery", dirtyErr.Version)
				forceVersion := int(dirtyErr.Version) - 1
				if forceVersion < 0 {
					forceVersion = 0
				}
				if forceErr := migrator.Force(forceVersion); forceErr != nil {
					logger.Errorf("[Migrate] Force version error: %v", forceErr.Error())
					panic(forceErr)
				}
				logger.Infof("[Migrate] Force version to %d, retrying migration", forceVersion)
				if retryErr := migrator.Up(); retryErr != nil && !errors.Is(retryErr, migrate.NoChange) {
					logger.Errorf("[Migrate] Retry Up error: %v", retryErr.Error())
					panic(retryErr)
				}
			} else {
				logger.Errorf("[Migrate] Up error: %v", err.Error())
				panic(err)
			}
		}
	} else {
		logger.Info("[Migrate] Database change, took " + time.Since(now).String())
	}

	if err := EnsureSchemaCompatibility(ctx); err != nil {
		logger.Errorf("[SchemaCompat] repair failed: %v", err.Error())
		panic(err)
	}

	// if not found admin user
	err := ctx.DB.Transaction(func(tx *gorm.DB) error {
		var count int64
		if err := tx.Model(&user.User{}).Count(&count).Error; err != nil {
			return err
		}
		if count == 0 {
			if err := migrate.CreateAdminUser(ctx.Config.Administrator.Email, ctx.Config.Administrator.Password, tx); err != nil {
				logger.Errorf("[Migrate] CreateAdminUser error: %v", err.Error())
				return err
			}
			logger.Info("[Migrate] Create admin user success")
		}
		return nil
	})
	if err != nil {
		panic(err)
	}
}
