package adminauth

import (
	"context"
	"errors"
	"strings"
	"unicode"

	"hfb_sys/backend/internal/model"

	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type BootstrapConfig struct {
	AppEnv   string
	Username string
	Password string
	Nickname string
}

// BootstrapAdmin 在管理员表为空时按部署配置创建首个超级管理员。
func BootstrapAdmin(ctx context.Context, db *gorm.DB, cfg BootstrapConfig) error {
	if db == nil {
		return nil
	}
	var count int64
	if err := db.WithContext(ctx).Model(&model.AdminUser{}).Count(&count).Error; err != nil {
		if isAdminTableMissing(err) {
			return nil
		}
		return err
	}
	if count > 0 {
		return nil
	}

	username := strings.TrimSpace(cfg.Username)
	password := cfg.Password
	if username == "" || password == "" {
		if strings.EqualFold(strings.TrimSpace(cfg.AppEnv), "production") {
			return errors.New("admin bootstrap credentials are required when no admin exists in production")
		}
		return nil
	}
	if strings.EqualFold(strings.TrimSpace(cfg.AppEnv), "production") && !bootstrapPasswordStrong(password) {
		return errors.New("ADMIN_BOOTSTRAP_PASSWORD must be at least 12 chars and include letters and digits in production")
	}
	nickname := strings.TrimSpace(cfg.Nickname)
	if nickname == "" {
		nickname = username
	}

	hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		admin := model.AdminUser{
			Username:           username,
			PasswordHash:       string(hash),
			Nickname:           nickname,
			Status:             "active",
			TokenVersion:       1,
			PasswordMustChange: true,
		}
		if err := tx.Create(&admin).Error; err != nil {
			return err
		}
		var superAdminRole model.Role
		if err := tx.Where("code = ?", "super_admin").First(&superAdminRole).Error; err != nil {
			return err
		}
		return tx.Create(&model.AdminUserRole{
			AdminID: admin.ID,
			RoleID:  superAdminRole.ID,
		}).Error
	})
}

func bootstrapPasswordStrong(value string) bool {
	if len([]rune(value)) < 12 {
		return false
	}
	hasLetter := false
	hasDigit := false
	for _, r := range value {
		if unicode.IsLetter(r) {
			hasLetter = true
		}
		if unicode.IsDigit(r) {
			hasDigit = true
		}
	}
	return hasLetter && hasDigit
}

func isAdminTableMissing(err error) bool {
	message := strings.ToLower(err.Error())
	return strings.Contains(message, "admin_users") && (strings.Contains(message, "doesn't exist") || strings.Contains(message, "no such table"))
}
