package models

import (
	"fmt"
	"time"

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

type RoleName string

const (
	RoleSystemAdmin RoleName = "system_admin"
	RoleAdmin       RoleName = "admin"
	RoleUser        RoleName = "user"
)

func (r RoleName) String() string { return string(r) }

func (r RoleName) Rank() int {
	switch r {
	case RoleSystemAdmin:
		return 3
	case RoleAdmin:
		return 2
	case RoleUser:
		return 1
	default:
		return 0
	}
}

func (r RoleName) CanManage(target RoleName) bool {
	return r.Rank() > target.Rank()
}

func ParseRoleName(s string) (RoleName, error) {
	switch s {
	case "system_admin":
		return RoleSystemAdmin, nil
	case "admin":
		return RoleAdmin, nil
	case "user":
		return RoleUser, nil
	default:
		return "", fmt.Errorf("unknown role: %s", s)
	}
}

type Role struct {
	ID          int32           `json:"id" gorm:"primaryKey;autoIncrement"`
	Name        string          `json:"name" gorm:"uniqueIndex;size:32;not null"`
	Description *string         `json:"description"`
	Permissions string          `json:"permissions" gorm:"type:jsonb;default:'[]'"`
	CreatedAt   time.Time       `json:"created_at"`
	Users       []User          `json:"-" gorm:"foreignKey:RoleID"`
}

func (r *Role) NameEnum() RoleName {
	role, _ := ParseRoleName(r.Name)
	return role
}

type User struct {
	ID           string    `json:"id" gorm:"type:uuid;primaryKey;default:gen_random_uuid()"`
	Username     string    `json:"username" gorm:"uniqueIndex;size:32;not null"`
	Email        *string   `json:"email" gorm:"uniqueIndex;size:128"`
	PasswordHash string    `json:"-" gorm:"size:255"`
	RoleID       int32     `json:"role_id" gorm:"not null"`
	Role         Role      `json:"role,omitempty" gorm:"foreignKey:RoleID;references:ID"`
	Status       string    `json:"status" gorm:"size:16;default:active"`
	CreatedAt    time.Time `json:"created_at"`
	UpdatedAt    time.Time `json:"updated_at"`
}

type PublicUserInfo struct {
	ID        string    `json:"id"`
	Username  string    `json:"username"`
	Email     *string   `json:"email"`
	Role      RoleName  `json:"role"`
	Status    string    `json:"status"`
	CreatedAt time.Time `json:"created_at"`
}

func (u *User) ToPublicInfo() PublicUserInfo {
	return PublicUserInfo{
		ID:        u.ID,
		Username:  u.Username,
		Email:     u.Email,
		Role:      u.Role.NameEnum(),
		Status:    u.Status,
		CreatedAt: u.CreatedAt,
	}
}

func AutoMigrate(db *gorm.DB) error {
	return db.AutoMigrate(&Role{}, &User{})
}

func SeedRoles(db *gorm.DB) error {
	roles := []Role{
		{Name: string(RoleSystemAdmin), Description: strPtr("系统管理员，可管理管理员与系统配置"), Permissions: `["*"]`},
		{Name: string(RoleAdmin), Description: strPtr("管理员，可管理普通用户"), Permissions: `["users.read", "users.write", "users.create"]`},
		{Name: string(RoleUser), Description: strPtr("普通用户，可访问业务功能"), Permissions: `["dashboard.read", "profile.write"]`},
	}
	for _, role := range roles {
		var existing Role
		if err := db.Where("name = ?", role.Name).First(&existing).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				if err := db.Create(&role).Error; err != nil {
					return err
				}
			} else {
				return err
			}
		}
	}
	return nil
}

// SeedSystemAdmin 在没有 system_admin 用户时创建默认系统管理员账号。
func SeedSystemAdmin(db *gorm.DB) error {
	var existing User
	if err := db.Where("username = ?", "system_admin").First(&existing).Error; err == nil {
		return nil
	} else if err != gorm.ErrRecordNotFound {
		return err
	}

	var role Role
	if err := db.Where("name = ?", RoleSystemAdmin).First(&role).Error; err != nil {
		return err
	}

	hash, err := bcrypt.GenerateFromPassword([]byte("system_admin"), bcrypt.DefaultCost)
	if err != nil {
		return err
	}

	return db.Create(&User{
		Username:     "system_admin",
		PasswordHash: string(hash),
		RoleID:       role.ID,
		Status:       "active",
	}).Error
}

func strPtr(s string) *string {
	return &s
}
