package service

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

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

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

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

// NewAuthService 构造函数
func NewAuthService(userRepo userAuthStore, tokenSvc tokenProvider, cfg *config.Config, siteSettings *config.SiteSettings) *AuthService {
	return &AuthService{userRepo: userRepo, tokenService: tokenSvc, 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 比对
// 在 init() 中按当前 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
}

// Register 注册
// 流程：密码强度 → 邮箱查重 → 哈希 → 生成唯一用户名 → 创建 → access JWT + refresh JWT
// rememberMe=true: refresh Cookie 持久化（30天）；false: session cookie（关浏览器即清除）
// regIP: 注册 IP 地址
func (s *AuthService) Register(req model.RegisterRequest, regIP string) (string, string, *model.User, error) {
	// 0. 检查注册开关
	if !s.siteSettings.IsRegistrationEnabled() {
		return "", "", nil, common.ErrRegistrationDisabled
	}

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

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

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

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

	// 5. 创建用户
	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
	}

	// 6. 生成 access JWT
	accessToken, err := s.tokenService.BuildAccessToken(user)
	if err != nil {
		return "", "", nil, err
	}

	// 7. 生成 refresh JWT（不勾选"记住我"也用 session cookie，关浏览器即清除）
	refreshToken, err := s.tokenService.BuildRefreshToken(user, req.RememberMe)
	if err != nil {
		return "", "", nil, err
	}

	return accessToken, refreshToken, user, nil
}

// Login 登录
// 流程：按邮箱查找 → 已注销则验证密码后要求二次确认 → 检查状态 → 验证密码 → 记录登录 IP/时间 → access JWT + refresh JWT
// 防时序攻击：邮箱不存在时仍执行完整 bcrypt 比对
// rememberMe=true: refresh Cookie 持久化（30天）；false: session cookie（关浏览器即清除）
// loginIP: 登录 IP 地址
// 若用户处于 deleted 状态且密码正确 → 返回 ErrNeedsConfirmRestore（不自动恢复，不签发 token）
func (s *AuthService) Login(req model.LoginRequest, loginIP string) (string, string, *model.User, error) {
	user, err := s.userRepo.FindByEmail(req.Email)
	if err != nil {
		_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
		return "", "", nil, common.ErrInvalidCred
	}

	// 已注销（deleted）→ 验证密码后要求二次确认，不自动恢复
	if user.Status == model.StatusDeleted {
		if !common.CheckPassword(req.Password, user.PasswordHash) {
			_ = 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 {
		return "", "", nil, common.ErrUserBanned
	}

	if !common.CheckPassword(req.Password, user.PasswordHash) {
		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
	}

	// 生成 access JWT
	accessToken, err := s.tokenService.BuildAccessToken(user)
	if err != nil {
		return "", "", nil, err
	}

	// 生成 refresh JWT（不勾选"记住我"也生成，Cookie 用 session 模式）
	refreshToken, err := s.tokenService.BuildRefreshToken(user, req.RememberMe)
	if err != nil {
		return "", "", nil, err
	}

	return accessToken, refreshToken, user, nil
}

// ConfirmRestore 二次确认恢复已注销账号
// 流程：按邮箱查找 → 验证密码 → 恢复为 active → 记录登录 IP/时间 → 签发 token
func (s *AuthService) ConfirmRestore(req model.LoginRequest, loginIP string) (string, string, *model.User, error) {
	user, err := s.userRepo.FindByEmail(req.Email)
	if err != nil {
		_ = bcrypt.CompareHashAndPassword(dummyHash, []byte(req.Password))
		return "", "", nil, common.ErrInvalidCred
	}

	// 仅 deleted 状态允许恢复
	if user.Status != model.StatusDeleted {
		return "", "", nil, common.ErrUserNotFound
	}

	if !common.CheckPassword(req.Password, user.PasswordHash) {
		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
	}

	accessToken, err := s.tokenService.BuildAccessToken(user)
	if err != nil {
		return "", "", nil, err
	}

	refreshToken, err := s.tokenService.BuildRefreshToken(user, req.RememberMe)
	if err != nil {
		return "", "", nil, err
	}

	return accessToken, refreshToken, user, nil
}

// CheckEmail 检查邮箱是否已被注册（无需认证的轻量检查）
func (s *AuthService) CheckEmail(email string) (bool, error) {
	return s.userRepo.ExistsByEmail(email)
}

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

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

	// 验证当前密码
	if !common.CheckPassword(currentPassword, user.PasswordHash) {
		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
	}

	// 强制所有设备重新登录（安全性）
	return s.InvalidateSessions(userID)
}

// DeleteAccount 用户自主注销
// 流程：检查角色 → 验证密码 → 记录原因 → 设置 deleted 状态 → 吊销所有 JWT → 7 天冷却期内登录需二次确认恢复
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
	}

	// 验证当前密码（防止 CSRF 或未授权操作）
	if !common.CheckPassword(password, user.PasswordHash) {
		return common.ErrIncorrectPassword
	}

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

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

	// 递增 token_version，即时吊销所有 JWT 强制退登
	return s.InvalidateSessions(userID)
}

// InvalidateSessions 吊销某用户所有 JWT（递增 token_version，强制所有设备重新登录）
// 适用场景：修改密码、账号被盗、管理员强制下线
func (s *AuthService) InvalidateSessions(userID uint) error {
	return s.userRepo.IncrementTokenVersion(userID)
}

// UpdateProfile 修改个人资料（用户名 + 个性签名）
// 用户名校验：非空、1-16 字符、白名单、去重
// 个性签名校验：0-128 字符纯文本，Go 模板自动 HTML 转义防 XSS
// 任一字段无变更时跳过该字段的写库操作
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)
}
