package service

import (
	"regexp"
	"time"
	"unicode/utf8"

	"metazone.cc/metalab/internal/common"
	"metazone.cc/metalab/internal/config"
	"metazone.cc/metalab/internal/model"
	"metazone.cc/metalab/internal/session"

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

// AuthService 认证业务逻辑
type AuthService struct {
	userRepo       userAuthStore
	sessionManager *session.Manager
	cfg            *config.Config
	siteSettings   *config.SiteSettings
}

// NewAuthService 构造函数
func NewAuthService(userRepo userAuthStore, sm *session.Manager, cfg *config.Config, siteSettings *config.SiteSettings) *AuthService {
	return &AuthService{userRepo: userRepo, sessionManager: sm, cfg: cfg, siteSettings: siteSettings}
}

var pwLetter = regexp.MustCompile(`[a-zA-Z]`)
var pwDigit = regexp.MustCompile(`\d`)

// usernamePattern 用户名合法字符：中文、英文大小写、数字、下划线、连字符
var usernamePattern = regexp.MustCompile(`^[\p{Han}a-zA-Z0-9_-]+$`)

// dummyHash 防时序攻击：当用户不存在时，仍对虚拟哈希执行完整的 bcrypt 比对
var dummyHash []byte

func init() {
	hash, err := bcrypt.GenerateFromPassword([]byte("metazone-dummy-hash-2026"), 12)
	if err != nil {
		panic("failed to generate bcrypt dummy hash: " + err.Error())
	}
	dummyHash = hash
}

// validatePassword 密码强度：至少 8 位 + 包含字母 + 包含数字
func validatePassword(pw string) error {
	if len(pw) < 8 || !pwLetter.MatchString(pw) || !pwDigit.MatchString(pw) {
		return common.ErrWeakPassword
	}
	return nil
}

// IsRegistrationEnabled 检查是否允许新用户注册
func (s *AuthService) IsRegistrationEnabled() bool {
	return s.siteSettings.IsRegistrationEnabled()
}

// Register 注册
// 流程：密码强度 → 邮箱查重 → 哈希 → 生成唯一用户名 → 创建 → 返回 user（session 由 controller 创建）
func (s *AuthService) Register(req model.RegisterRequest, regIP string) (*model.User, error) {
	// 0. 维护期间禁止注册
	if s.siteSettings.IsMaintenanceEnabled() {
		return nil, common.ErrMaintenanceMode
	}
	// 1. 检查注册开关
	if !s.siteSettings.IsRegistrationEnabled() {
		return nil, common.ErrRegistrationDisabled
	}

	// 2. 密码强度
	if err := validatePassword(req.Password); err != nil {
		return nil, err
	}

	// 3. 检查邮箱
	exists, err := s.userRepo.ExistsByEmail(req.Email)
	if err != nil {
		return nil, err
	}
	if exists {
		return nil, common.ErrEmailExists
	}

	// 4. 哈希密码
	hash, err := common.HashPassword(req.Password, s.cfg.Bcrypt.Cost)
	if err != nil {
		return nil, err
	}

	// 5. 生成唯一用户名
	username, err := common.GenerateUsername(s.userRepo, 20)
	if err != nil {
		return nil, err
	}

	// 6. 创建用户
	user := &model.User{
		Email:        req.Email,
		PasswordHash: hash,
		Username:     username,
		Role:         model.RoleUser,
		Status:       model.StatusActive,
		RegIP:        regIP,
	}
	if err := s.userRepo.Create(user); err != nil {
		return nil, err
	}

	return user, nil
}

// Login 登录
// 流程：按邮箱查找 → 维护期间非站长统一拦截 → 状态检查 → 验证密码 → 记录登录信息 → 返回 user
func (s *AuthService) Login(req model.LoginRequest, loginIP string) (*model.User, error) {
	user, err := s.userRepo.FindByEmail(req.Email)

	// 维护期间：非站长一律返回统一提示（模拟 bcrypt 防时序攻击）
	if s.siteSettings.IsMaintenanceEnabled() {
		if err != nil {
			_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
			return nil, common.ErrMaintenanceMode
		}
		if !model.HasMinRole(user.Role, model.RoleOwner) {
			_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
			return nil, common.ErrMaintenanceMode
		}
	}

	if err != nil {
		_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
		return nil, common.ErrInvalidCred
	}

	// 已注销（deleted）→ 验证密码后要求二次确认
	if user.Status == model.StatusDeleted {
		if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)) != nil {
			_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
			return nil, common.ErrInvalidCred
		}
		return nil, common.ErrNeedsConfirmRestore
	}

	// 永久锁定
	if user.Status == model.StatusLocked {
		_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
		return nil, common.ErrUserLocked
	}

	// 封禁
	if user.Status == model.StatusBanned {
		_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
		return nil, common.ErrUserBanned
	}

	if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)) != nil {
		return nil, common.ErrInvalidCred
	}

	// 记录登录 IP 和时间
	now := time.Now()
	user.LastLoginIP = loginIP
	user.LastLoginAt = &now
	if err := s.userRepo.Update(user); err != nil {
		return nil, err
	}

	return user, nil
}

// ConfirmRestore 二次确认恢复已注销账号
func (s *AuthService) ConfirmRestore(req model.LoginRequest, loginIP string) (*model.User, error) {
	user, err := s.userRepo.FindByEmail(req.Email)
	if err != nil {
		_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
		return nil, common.ErrInvalidCred
	}

	if user.Status != model.StatusDeleted {
		return nil, common.ErrUserNotFound
	}

	if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)) != nil {
		return nil, common.ErrInvalidCred
	}

	// 恢复账号
	user.Status = model.StatusActive
	user.DeletedAt = gorm.DeletedAt{}
	now := time.Now()
	user.LastLoginIP = loginIP
	user.LastLoginAt = &now
	if err := s.userRepo.Update(user); err != nil {
		return nil, err
	}

	return user, nil
}

// CheckEmail 检查邮箱是否已被注册
func (s *AuthService) CheckEmail(email string) (bool, error) {
	return s.userRepo.ExistsByEmail(email)
}

// GetProfile 获取当前用户资料
func (s *AuthService) GetProfile(userID uint) (*model.User, error) {
	return s.userRepo.FindByID(userID)
}

// ChangePassword 修改密码
// 流程：验证当前密码 → 校验新密码强度 → 哈希 → 更新 → 删除所有 session（强制重新登录）
func (s *AuthService) ChangePassword(userID uint, currentPassword, newPassword string) error {
	user, err := s.userRepo.FindByID(userID)
	if err != nil {
		return common.ErrUserNotFound
	}

	if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(currentPassword)) != nil {
		return common.ErrIncorrectPassword
	}

	if err := validatePassword(newPassword); err != nil {
		return err
	}

	hash, err := common.HashPassword(newPassword, s.cfg.Bcrypt.Cost)
	if err != nil {
		return err
	}

	user.PasswordHash = hash
	if err := s.userRepo.Update(user); err != nil {
		return err
	}

	// 删除所有 session，强制所有设备重新登录
	return s.invalidateSessions(userID)
}

// DeleteAccount 用户自主注销
func (s *AuthService) DeleteAccount(userID uint, password, reason string) error {
	user, err := s.userRepo.FindByID(userID)
	if err != nil {
		return common.ErrUserNotFound
	}

	if user.Role == model.RoleOwner {
		return common.ErrOwnerCannotDelete
	}

	if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)) != nil {
		return common.ErrIncorrectPassword
	}

	user.Status = model.StatusDeleted
	user.DeleteReason = reason

	if err := s.userRepo.Update(user); err != nil {
		return err
	}

	return s.invalidateSessions(userID)
}

// invalidateSessions 销毁某用户的所有 session（内部方法）
func (s *AuthService) invalidateSessions(userID uint) error {
	return s.sessionManager.DestroyByUID(userID)
}

// InvalidateSessions 公开方法：销毁用户所有 session（供 admin 等外部调用）
func (s *AuthService) InvalidateSessions(userID uint) error {
	return s.sessionManager.DestroyByUID(userID)
}

// UpdateProfile 修改个人资料
func (s *AuthService) UpdateProfile(userID uint, username, bio string) error {
	user, err := s.userRepo.FindByID(userID)
	if err != nil {
		return common.ErrUserNotFound
	}

	needsUpdate := false

	if n := utf8.RuneCountInString(username); n == 0 {
		return common.ErrUsernameInvalid
	} else if n > 16 {
		return common.ErrUsernameInvalid
	}
	if !usernamePattern.MatchString(username) {
		return common.ErrUsernameInvalid
	}
	if username != user.Username {
		exists, err := s.userRepo.ExistsByUsername(username)
		if err != nil {
			return err
		}
		if exists {
			return common.ErrUsernameTaken
		}
		user.Username = username
		needsUpdate = true
	}

	if utf8.RuneCountInString(bio) > 128 {
		return common.ErrBioTooLong
	}
	if bio != user.Bio {
		user.Bio = bio
		needsUpdate = true
	}

	if !needsUpdate {
		return nil
	}
	return s.userRepo.Update(user)
}
