/**
 * 登录核心服务
 *
 * 功能描述：
 * - 提供用户认证的核心功能实现
 * - 处理登录、注册、密码重置等核心逻辑
 * - 为业务层提供基础的认证服务
 *
 * 职责分离：
 * - 专注于认证功能的核心实现
 * - 不处理HTTP请求和响应格式化
 * - 为business层提供可复用的服务
 *
 * 最近修改：
 * - 2026-01-15: 代码规范优化 - 提取手机号查找为私有方法消除重复代码 (修改者: moyin)
 * - 2026-01-12: 代码规范优化 - 提取魔法数字为常量，拆分过长方法，消除代码重复 (修改者: moyin)
 * - 2026-01-12: 代码规范优化 - 添加LoginCoreService类注释，完善类职责和方法说明 (修改者: moyin)
 * - 2026-01-12: 代码规范优化 - 处理TODO项，移除短信发送相关的TODO注释 (修改者: moyin)
 * - 2025-01-07: 代码规范优化 - 清理未使用的导入(EmailSendResult, crypto)
 *
 * @author moyin
 * @version 1.1.1
 * @since 2025-12-17
 * @lastModified 2026-01-15
 */

import { Injectable, UnauthorizedException, ConflictException, NotFoundException, BadRequestException, ForbiddenException, Inject } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import * as jwt from 'jsonwebtoken';
import { Users } from '../db/users/users.entity';
import { UsersService } from '../db/users/users.service';
import { EmailService } from '../utils/email/email.service';
import { VerificationService, VerificationCodeType } from '../utils/verification/verification.service';
import { UserStatus, canUserLogin, getUserStatusErrorMessage } from '../db/users/user_status.enum';
import { UserSocialCleanupService } from '../session_core/user_social_cleanup.service';
import * as bcrypt from 'bcrypt';

/**
 * 登录请求数据接口
 */
export interface LoginRequest {
  /** 登录标识符：用户名、邮箱或手机号 */
  identifier: string;
  /** 密码 */
  password: string;
}

/**
 * 注册请求数据接口
 */
export interface RegisterRequest {
  /** 用户名 */
  username: string;
  /** 密码 */
  password: string;
  /** 昵称 */
  nickname: string;
  /** 邮箱（可选） */
  email?: string;
  /** 手机号（可选） */
  phone?: string;
  /** 邮箱验证码（当提供邮箱时必填） */
  email_verification_code?: string;
  /** 初始角色皮肤ID（可选） */
  skin_id?: string;
}

/**
 * GitHub OAuth登录请求数据接口
 */
export interface GitHubOAuthRequest {
  /** GitHub用户ID */
  github_id: string;
  /** 用户名 */
  username: string;
  /** 昵称 */
  nickname: string;
  /** 邮箱 */
  email?: string;
  /** 头像URL */
  avatar_url?: string;
}

/**
 * 密码重置请求数据接口
 */
export interface PasswordResetRequest {
  /** 邮箱或手机号 */
  identifier: string;
  /** 验证码 */
  verificationCode: string;
  /** 新密码 */
  newPassword: string;
}

/**
 * 认证结果接口
 */
export interface AuthResult {
  /** 用户信息 */
  user: Users;
  /** 是否为新用户 */
  isNewUser?: boolean;
}

/**
 * JWT载荷接口
 */
export interface JwtPayload {
  /** 用户ID */
  sub: string;
  /** 用户名 */
  username: string;
  /** 用户角色 */
  role: number;
  /** 邮箱 */
  email?: string;
  /** 令牌类型 */
  type: 'access' | 'refresh';
  /** 签发时间 */
  iat?: number;
  /** 过期时间 */
  exp?: number;
  /** 签发者 */
  iss?: string;
  /** 受众 */
  aud?: string;
}

/**
 * 令牌对接口
 */
export interface TokenPair {
  /** 访问令牌 */
  access_token: string;
  /** 刷新令牌 */
  refresh_token: string;
  /** 访问令牌过期时间（秒） */
  expires_in: number;
  /** 令牌类型 */
  token_type: string;
}

/**
 * 验证码发送结果接口 by angjustinl 2025-12-17
 */
export interface VerificationCodeResult {
  /** 验证码 */
  code: string;
  /** 是否为测试模式 */
  isTestMode: boolean;
}

/**
 * 验证码登录请求数据接口
 */
export interface VerificationCodeLoginRequest {
  /** 登录标识符：邮箱或手机号 */
  identifier: string;
  /** 验证码 */
  verificationCode: string;
}

// 常量定义
const SALT_ROUNDS = 12; // 密码哈希盐值轮数
const MIN_PASSWORD_LENGTH = 8; // 密码最小长度
const MAX_PASSWORD_LENGTH = 128; // 密码最大长度
const REFRESH_TOKEN_EXPIRES_IN = '30d'; // 刷新令牌过期时间
const DEFAULT_ACCESS_TOKEN_EXPIRES_DAYS = 7; // 默认访问令牌过期天数
const USERNAME_CONFLICT_MAX_ATTEMPTS = 100; // 用户名冲突处理最大尝试次数
const DEFAULT_USER_ROLE = 1; // 默认用户角色（普通用户）
const PHONE_MIN_DIGITS = 10; // 手机号最少位数
const PHONE_MAX_DIGITS = 11; // 手机号最多位数
const COUNTRY_CODE_MAX_DIGITS = 3; // 国家代码最多位数
const JWT_ISSUER = 'whale-town'; // JWT签发者
const JWT_AUDIENCE = 'whale-town-users'; // JWT受众

/**
 * 登录核心服务类
 *
 * 职责：
 * - 提供用户认证的核心功能实现（密码登录、验证码登录、OAuth登录）
 * - 处理用户注册、密码管理和邮箱验证等核心逻辑
 * - 为业务层提供基础的认证服务，不处理HTTP请求和响应格式化
 * - 管理JWT令牌的生成、验证和刷新功能
 * - 协调用户数据、邮件服务、验证码服务的集成
 *
 * 主要方法：
 * - login() - 用户名/邮箱/手机号密码登录
 * - verificationCodeLogin() - 验证码登录
 * - githubOAuth() - GitHub OAuth第三方登录
 * - register() - 用户注册（支持邮箱验证）
 * - changePassword() - 修改用户密码
 * - resetPassword() - 通过验证码重置密码
 * - sendPasswordResetCode() - 发送密码重置验证码
 * - sendEmailVerification() - 发送邮箱验证码
 * - verifyEmailCode() - 验证邮箱验证码
 * - generateTokenPair() - 生成JWT令牌对
 * - verifyToken() - 验证JWT令牌
 * - refreshAccessToken() - 刷新访问令牌
 *
 * 使用场景：
 * - 在业务控制器中调用进行用户认证
 * - 作为认证相关功能的核心服务层
 * - 在中间件中验证用户身份和权限
 * - 为其他业务服务提供用户认证支持
 *
 * 安全特性：
 * - 密码哈希存储（bcrypt，12轮盐值）
 * - JWT令牌安全生成和验证
 * - 用户状态和权限检查
 * - 验证码冷却机制防刷
 * - OAuth用户保护机制
 */
@Injectable()
export class LoginCoreService {
  constructor(
    @Inject('UsersService') private readonly usersService: UsersService,
    private readonly emailService: EmailService,
    private readonly verificationService: VerificationService,
    private readonly jwtService: JwtService,
    private readonly configService: ConfigService,
    private readonly userSocialCleanupService: UserSocialCleanupService,
  ) {}

  /**
   * 用户名密码登录
   *
   * @param loginRequest 登录请求数据
   * @returns 认证结果
   * @throws UnauthorizedException 认证失败时
   */
  async login(loginRequest: LoginRequest): Promise<AuthResult> {
    const { identifier, password } = loginRequest;

    // 查找用户（支持用户名、邮箱、手机号登录）
    let user: Users | null = null;

    // 尝试用户名查找
    user = await this.usersService.findByUsername(identifier);

    // 如果用户名未找到，尝试邮箱查找
    if (!user && this.isEmail(identifier)) {
      user = await this.usersService.findByEmail(identifier);
    }

    // 如果邮箱未找到，尝试手机号查找（简单验证）
    if (!user && this.isPhoneNumber(identifier)) {
      user = await this.findUserByPhone(identifier);
    }

    // 用户不存在
    if (!user) {
      throw new UnauthorizedException('用户名、邮箱或手机号不存在');
    }

    // 检查用户状态
    if (!canUserLogin(user.status)) {
      throw new ForbiddenException(getUserStatusErrorMessage(user.status));
    }

    // 检查是否为OAuth用户（没有密码）
    if (!user.password_hash) {
      throw new UnauthorizedException('该账户使用第三方登录，请使用对应的登录方式');
    }

    // 验证密码
    const isPasswordValid = await this.verifyPassword(password, user.password_hash);
    if (!isPasswordValid) {
      throw new UnauthorizedException('密码错误');
    }

    return {
      user,
      isNewUser: false
    };
  }

  /**
   * 用户注册
   *
   * @param registerRequest 注册请求数据
   * @returns 认证结果
   * @throws ConflictException 用户已存在时
   * @throws BadRequestException 数据验证失败时
   */
  async register(registerRequest: RegisterRequest): Promise<AuthResult> {
    const { username, password, nickname, email, phone, email_verification_code } = registerRequest;

    // 检查用户唯一性
    await this.validateUserUniqueness(username, email, phone);

    // 验证邮箱验证码（如果提供了邮箱）
    if (email) {
      await this.validateEmailVerificationCode(email, email_verification_code);
    }

    // 验证密码强度并创建用户
    this.validatePasswordStrength(password);
    const passwordHash = await this.hashPassword(password);

    const user = await this.createNewUser({
      username,
      passwordHash,
      nickname,
      email,
      phone
    });

    await this.userSocialCleanupService.clearUserSocialData(user.id.toString());

    // 注册后处理
    await this.handlePostRegistration(email);

    return {
      user,
      isNewUser: true
    };
  }

  /**
   * 验证用户唯一性
   *
   * @param username 用户名
   * @param email 邮箱
   * @param phone 手机号
   * @throws ConflictException 用户已存在时
   * @private
   */
  private async validateUserUniqueness(username: string, email?: string, phone?: string): Promise<void> {
    // 检查用户名是否已存在
    const existingUser = await this.usersService.findByUsername(username);
    if (existingUser) {
      throw new ConflictException('用户名已存在');
    }

    // 检查邮箱是否已存在
    if (email) {
      const existingEmail = await this.usersService.findByEmail(email);
      if (existingEmail) {
        throw new ConflictException('邮箱已存在');
      }
    }

    // 检查手机号是否已存在
    if (phone) {
      const phoneExists = await this.isPhoneExists(phone);
      if (phoneExists) {
        throw new ConflictException('手机号已存在');
      }
    }
  }

  /**
   * 验证邮箱验证码
   *
   * @param email 邮箱地址
   * @param emailVerificationCode 验证码
   * @throws BadRequestException 验证码错误时
   * @private
   */
  private async validateEmailVerificationCode(email: string, emailVerificationCode?: string): Promise<void> {
    if (!emailVerificationCode) {
      throw new BadRequestException('提供邮箱时必须提供邮箱验证码');
    }

    // 验证邮箱验证码
    await this.verificationService.verifyCode(
      email,
      VerificationCodeType.EMAIL_VERIFICATION,
      emailVerificationCode
    );
  }

  /**
   * 创建新用户
   *
   * @param userData 用户数据
   * @returns 创建的用户
   * @private
   */
  private async createNewUser(userData: {
    username: string;
    passwordHash: string;
    nickname: string;
    email?: string;
    phone?: string;
  }): Promise<Users> {
    const { username, passwordHash, nickname, email, phone } = userData;

    return await this.usersService.create({
      username,
      password_hash: passwordHash,
      nickname,
      email,
      phone,
      role: DEFAULT_USER_ROLE, // 默认普通用户
      status: UserStatus.ACTIVE, // 默认激活状态
      email_verified: email ? true : false // 如果提供了邮箱且验证码验证通过，则标记为已验证
    });
  }

  /**
   * 注册后处理
   *
   * @param email 邮箱地址
   * @private
   */
  private async handlePostRegistration(email?: string): Promise<void> {
    if (!email) return;

    // 注册成功后清除验证码冷却时间，方便用户后续操作
    try {
      await this.verificationService.clearCooldown(
        email,
        VerificationCodeType.EMAIL_VERIFICATION
      );
    } catch (error) {
      // 清除冷却时间失败不影响注册流程，只记录日志
      console.warn(`清除验证码冷却时间失败: ${email}`, error);
    }
  }

  /**
   * GitHub OAuth登录/注册
   *
   * @param oauthRequest OAuth请求数据
   * @returns 认证结果
   */
  async githubOAuth(oauthRequest: GitHubOAuthRequest): Promise<AuthResult> {
    const { github_id, username, nickname, email, avatar_url } = oauthRequest;

    // 查找是否已存在GitHub用户
    let user = await this.usersService.findByGithubId(github_id);

    if (user) {
      // 用户已存在，更新信息
      user = await this.usersService.update(user.id, {
        nickname,
        email,
        avatar_url
      });

      return {
        user,
        isNewUser: false
      };
    }

    // 处理用户名冲突并创建新用户
    const finalUsername = await this.resolveUsernameConflict(username);
    user = await this.createGitHubUser({
      username: finalUsername,
      nickname,
      email,
      github_id,
      avatar_url
    });

    await this.userSocialCleanupService.clearUserSocialData(user.id.toString());

    return {
      user,
      isNewUser: true
    };
  }

  /**
   * 解决用户名冲突
   *
   * @param username 原始用户名
   * @returns 可用的用户名
   * @private
   */
  private async resolveUsernameConflict(username: string): Promise<string> {
    let finalUsername = username;
    let counter = DEFAULT_USER_ROLE;

    while (await this.usersService.findByUsername(finalUsername) && counter <= USERNAME_CONFLICT_MAX_ATTEMPTS) {
      finalUsername = `${username}_${counter}`;
      counter++;
    }

    return finalUsername;
  }

  /**
   * 创建GitHub用户
   *
   * @param userData GitHub用户数据
   * @returns 创建的用户
   * @private
   */
  private async createGitHubUser(userData: {
    username: string;
    nickname: string;
    email?: string;
    github_id: string;
    avatar_url?: string;
  }): Promise<Users> {
    const { username, nickname, email, github_id, avatar_url } = userData;

    return await this.usersService.create({
      username,
      nickname,
      email,
      github_id,
      avatar_url,
      role: DEFAULT_USER_ROLE, // 默认普通用户
      status: UserStatus.ACTIVE, // GitHub用户直接激活
      email_verified: email ? true : false // GitHub邮箱直接验证
    });
  }

  /**
   * 发送密码重置验证码
   *
   * @param identifier 邮箱或手机号
   * @returns 验证码结果
   * @throws NotFoundException 用户不存在时
   */
  async sendPasswordResetCode(identifier: string): Promise<VerificationCodeResult> {
    // 查找用户
    let user: Users | null = null;

    if (this.isEmail(identifier)) {
      user = await this.usersService.findByEmail(identifier);

      // 检查邮箱是否已验证
      if (user && !user.email_verified) {
        throw new BadRequestException('邮箱未验证，无法重置密码');
      }
    } else if (this.isPhoneNumber(identifier)) {
      user = await this.findUserByPhone(identifier);
    }

    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    // 生成验证码
    const verificationCode = await this.verificationService.generateCode(
      identifier,
      VerificationCodeType.PASSWORD_RESET
    );

    // 发送验证码（仅支持邮箱）
    if (!this.isEmail(identifier)) {
      throw new BadRequestException('当前仅支持邮箱验证码，请使用邮箱地址');
    }

    const result = await this.emailService.sendVerificationCode({
      email: identifier,
      code: verificationCode,
      nickname: user.nickname,
      purpose: 'password_reset'
    });

    if (!result.success) {
      throw new BadRequestException('验证码发送失败，请稍后重试');
    }

    return { code: verificationCode, isTestMode: result.isTestMode };
  }

  /**
   * 重置密码
   *
   * @param resetRequest 重置请求数据
   * @returns 更新后的用户信息
   * @throws NotFoundException 用户不存在时
   * @throws BadRequestException 验证码错误时
   */
  async resetPassword(resetRequest: PasswordResetRequest): Promise<Users> {
    const { identifier, verificationCode, newPassword } = resetRequest;

    // 验证验证码
    const isValidCode = await this.verificationService.verifyCode(
      identifier,
      VerificationCodeType.PASSWORD_RESET,
      verificationCode
    );

    if (!isValidCode) {
      throw new BadRequestException('验证码验证失败');
    }

    // 查找用户
    let user: Users | null = null;

    if (this.isEmail(identifier)) {
      user = await this.usersService.findByEmail(identifier);
    } else if (this.isPhoneNumber(identifier)) {
      user = await this.findUserByPhone(identifier);
    }

    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    // 验证密码强度
    this.validatePasswordStrength(newPassword);

    // 加密新密码
    const passwordHash = await this.hashPassword(newPassword);

    // 更新密码
    const updatedUser = await this.usersService.update(user.id, {
      password_hash: passwordHash
    });

    // 密码重置成功后清除验证码冷却时间
    try {
      await this.verificationService.clearCooldown(
        identifier,
        VerificationCodeType.PASSWORD_RESET
      );
    } catch (error) {
      // 清除冷却时间失败不影响重置流程，只记录日志
      console.warn(`清除验证码冷却时间失败: ${identifier}`, error);
    }

    return updatedUser;
  }

  /**
   * 修改密码
   *
   * @param userId 用户ID
   * @param oldPassword 旧密码
   * @param newPassword 新密码
   * @returns 更新后的用户信息
   * @throws UnauthorizedException 旧密码错误时
   */
  async changePassword(userId: bigint, oldPassword: string, newPassword: string): Promise<Users> {
    // 获取用户信息
    const user = await this.usersService.findOne(userId);

    // 检查是否为OAuth用户
    if (!user.password_hash) {
      throw new BadRequestException('OAuth用户无法修改密码');
    }

    // 验证旧密码
    const isOldPasswordValid = await this.verifyPassword(oldPassword, user.password_hash);
    if (!isOldPasswordValid) {
      throw new UnauthorizedException('旧密码错误');
    }

    // 验证新密码强度
    this.validatePasswordStrength(newPassword);

    // 加密新密码
    const passwordHash = await this.hashPassword(newPassword);

    // 更新密码
    return await this.usersService.update(userId, {
      password_hash: passwordHash
    });
  }

  /**
   * 验证用户密码
   *
   * @param password 明文密码
   * @param hash 密码哈希值
   * @returns 是否匹配
   */
  private async verifyPassword(password: string, hash: string): Promise<boolean> {
    try {
      return await bcrypt.compare(password, hash);
    } catch (error) {
      return false;
    }
  }

  /**
   * 加密密码
   *
   * @param password 明文密码
   * @returns 密码哈希值
   */
  private async hashPassword(password: string): Promise<string> {
    return await bcrypt.hash(password, SALT_ROUNDS);
  }

  /**
   * 验证密码强度
   *
   * @param password 密码
   * @throws BadRequestException 密码强度不足时
   */
  private validatePasswordStrength(password: string): void {
    if (password.length < MIN_PASSWORD_LENGTH) {
      throw new BadRequestException(`密码长度至少${MIN_PASSWORD_LENGTH}位`);
    }

    if (password.length > MAX_PASSWORD_LENGTH) {
      throw new BadRequestException(`密码长度不能超过${MAX_PASSWORD_LENGTH}位`);
    }

    // 检查是否包含字母和数字
    const hasLetter = /[a-zA-Z]/.test(password);
    const hasNumber = /\d/.test(password);

    if (!hasLetter || !hasNumber) {
      throw new BadRequestException('密码必须包含字母和数字');
    }
  }

  /**
   * 发送邮箱验证码
   *
   * @param email 邮箱地址
   * @param nickname 用户昵称
   * @returns 验证码结果
   */
  async sendEmailVerification(email: string, nickname?: string): Promise<VerificationCodeResult> {
    // 首先检查邮箱是否已经被注册，避免发送无用的验证码
    const existingUser = await this.usersService.findByEmail(email);
    if (existingUser) {
      throw new ConflictException('邮箱已被注册，请使用其他邮箱或直接登录');
    }

    // 生成验证码
    const verificationCode = await this.verificationService.generateCode(
      email,
      VerificationCodeType.EMAIL_VERIFICATION
    );

    // 发送验证邮件
    const result = await this.emailService.sendVerificationCode({
      email,
      code: verificationCode,
      nickname,
      purpose: 'email_verification'
    });

    if (!result.success) {
      await this.verificationService.deleteCode(email, VerificationCodeType.EMAIL_VERIFICATION);
      throw new BadRequestException('验证邮件发送失败，请稍后重试');
    }

    return { code: verificationCode, isTestMode: result.isTestMode };
  }

  /**
   * 验证邮箱验证码
   *
   * @param email 邮箱地址
   * @param code 验证码
   * @returns 验证结果
   */
  async verifyEmailCode(email: string, code: string): Promise<boolean> {
    // 验证验证码
    const isValid = await this.verificationService.verifyCode(
      email,
      VerificationCodeType.EMAIL_VERIFICATION,
      code
    );

    if (isValid) {
      // 更新用户邮箱验证状态
      const user = await this.usersService.findByEmail(email);
      if (user) {
        await this.usersService.update(user.id, {
          email_verified: true
        });
      }
    }

    return isValid;
  }

  /**
   * 重新发送邮箱验证码
   *
   * @param email 邮箱地址
   * @returns 验证码结果
   */
  async resendEmailVerification(email: string): Promise<VerificationCodeResult> {
    const user = await this.usersService.findByEmail(email);

    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    if (user.email_verified) {
      throw new BadRequestException('邮箱已验证，无需重复验证');
    }

    return await this.sendEmailVerification(email, user.nickname);
  }

  /**
   * 检查是否为邮箱格式
   *
   * @param str 字符串
   * @returns 是否为邮箱
   */
  private isEmail(str: string): boolean {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(str);
  }

  /**
   * 检查是否为手机号格式（简单验证）
   *
   * @param str 字符串
   * @returns 是否为手机号
   */
  private isPhoneNumber(str: string): boolean {
    // 简单的手机号验证，支持国际格式
    const phoneRegex = new RegExp(`^(\\+\\d{1,${COUNTRY_CODE_MAX_DIGITS}}[- ]?)?\\d{${PHONE_MIN_DIGITS},${PHONE_MAX_DIGITS}}$`);
    return phoneRegex.test(str.replace(/\s/g, ''));
  }

  /**
   * 通过手机号查找用户
   *
   * @param phone 手机号
   * @returns 用户信息或null
   * @private
   */
  private async findUserByPhone(phone: string): Promise<Users | null> {
    const users = await this.usersService.findAll();
    return users.find((u: Users) => u.phone === phone) || null;
  }

  /**
   * 检查手机号是否已存在
   *
   * @param phone 手机号
   * @returns 是否存在
   * @private
   */
  private async isPhoneExists(phone: string): Promise<boolean> {
    const user = await this.findUserByPhone(phone);
    return user !== null;
  }

  /**
   * 验证码登录
   *
   * 功能描述：
   * 使用邮箱或手机号验证码进行用户登录
   *
   * 业务逻辑：
   * 1. 验证参数格式
   * 2. 查找对应用户
   * 3. 验证验证码
   * 4. 返回认证结果
   *
   * @param loginRequest 验证码登录请求数据
   * @returns 认证结果
   * @throws BadRequestException 参数错误时
   * @throws NotFoundException 用户不存在时
   * @throws UnauthorizedException 验证码错误时
   */
  async verificationCodeLogin(loginRequest: VerificationCodeLoginRequest): Promise<AuthResult> {
    const { identifier, verificationCode } = loginRequest;

    // 1. 验证参数
    if (!identifier || !verificationCode) {
      throw new BadRequestException('邮箱/手机号和验证码不能为空');
    }

    // 2. 查找用户
    let user: Users | null = null;
    let verificationType: VerificationCodeType;

    if (this.isEmail(identifier)) {
      // 邮箱登录
      user = await this.usersService.findByEmail(identifier);
      verificationType = VerificationCodeType.EMAIL_VERIFICATION;

      // 检查邮箱是否已验证
      if (user && !user.email_verified) {
        throw new BadRequestException('邮箱未验证，请先验证邮箱后再使用验证码登录');
      }
    } else if (this.isPhoneNumber(identifier)) {
      // 手机号登录
      user = await this.findUserByPhone(identifier);
      verificationType = VerificationCodeType.SMS_VERIFICATION;
    } else {
      throw new BadRequestException('请提供有效的邮箱或手机号');
    }

    // 3. 检查用户是否存在
    if (!user) {
      throw new NotFoundException('用户不存在，请先注册账户');
    }

    // 4. 验证验证码
    try {
      const isValidCode = await this.verificationService.verifyCode(
        identifier,
        verificationType,
        verificationCode
      );

      if (!isValidCode) {
        throw new UnauthorizedException('验证码验证失败');
      }
    } catch (error) {
      if (error instanceof BadRequestException) {
        // 验证码相关的业务异常（过期、错误等）
        throw new UnauthorizedException(error.message);
      }
      throw error;
    }

    // 5. 验证成功后清除验证码冷却时间
    try {
      await this.verificationService.clearCooldown(identifier, verificationType);
    } catch (error) {
      // 清除冷却时间失败不影响登录流程，只记录日志
      console.warn(`清除验证码冷却时间失败: ${identifier}`, error);
    }

    // 6. 验证成功，返回用户信息
    return {
      user,
      isNewUser: false
    };
  }

  /**
   * 发送登录验证码
   *
   * 功能描述：
   * 为验证码登录发送验证码到用户的邮箱或手机号
   *
   * 业务逻辑：
   * 1. 验证标识符格式
   * 2. 检查用户是否存在
   * 3. 生成并发送验证码
   *
   * @param identifier 邮箱或手机号
   * @returns 验证码结果
   * @throws NotFoundException 用户不存在时
   * @throws BadRequestException 邮箱未验证时
   */
  async sendLoginVerificationCode(identifier: string): Promise<VerificationCodeResult> {
    // 1. 查找用户
    let user: Users | null = null;
    let verificationType: VerificationCodeType;

    if (this.isEmail(identifier)) {
      user = await this.usersService.findByEmail(identifier);
      verificationType = VerificationCodeType.EMAIL_VERIFICATION;

      // 检查邮箱是否已验证
      if (user && !user.email_verified) {
        throw new BadRequestException('邮箱未验证，无法使用验证码登录');
      }
    } else if (this.isPhoneNumber(identifier)) {
      user = await this.findUserByPhone(identifier);
      verificationType = VerificationCodeType.SMS_VERIFICATION;
    } else {
      throw new BadRequestException('请提供有效的邮箱或手机号');
    }

    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    // 2. 生成验证码
    const verificationCode = await this.verificationService.generateCode(
      identifier,
      verificationType
    );

    // 3. 发送验证码
    let isTestMode = false;
    // 发送验证码（仅支持邮箱）
    if (!this.isEmail(identifier)) {
      throw new BadRequestException('当前仅支持邮箱验证码，请使用邮箱地址');
    }

    const result = await this.emailService.sendVerificationCode({
      email: identifier,
      code: verificationCode,
      nickname: user.nickname,
      purpose: 'login_verification'
    });

    if (!result.success) {
      throw new BadRequestException('验证码发送失败，请稍后重试');
    }

    return { code: verificationCode, isTestMode: result.isTestMode };
  }

  /**
   * 调试验证码信息
   *
   * @param email 邮箱地址
   * @returns 调试信息
   */
  async debugVerificationCode(email: string): Promise<any> {
    return await this.verificationService.debugCodeInfo(
      email,
      VerificationCodeType.EMAIL_VERIFICATION
    );
  }

  /**
   * 删除用户
   *
   * 功能描述：
   * 删除指定的用户记录，用于注册失败时的回滚操作
   *
   * 业务逻辑：
   * 1. 验证用户是否存在
   * 2. 执行用户删除操作
   * 3. 返回删除结果
   *
   * @param userId 用户ID
   * @returns Promise<boolean> 是否删除成功
   * @throws NotFoundException 用户不存在时
   */
  async deleteUser(userId: bigint): Promise<boolean> {
    // 1. 验证用户是否存在
    const user = await this.usersService.findOne(userId);
    if (!user) {
      throw new NotFoundException('用户不存在');
    }

    // 2. 执行删除操作
    try {
      await this.userSocialCleanupService.clearUserSocialData(userId.toString());
      await this.usersService.remove(userId);
      return true;
    } catch (error) {
      console.error(`删除用户失败: ${userId}`, error);
      return false;
    }
  }

  /**
   * 生成JWT令牌对
   *
   * 功能描述：
   * 为用户生成访问令牌和刷新令牌，符合JWT标准和安全最佳实践
   *
   * 业务逻辑：
   * 1. 创建访问令牌载荷（短期有效）
   * 2. 创建刷新令牌载荷（长期有效）
   * 3. 使用配置的密钥签名令牌
   * 4. 返回完整的令牌对信息
   *
   * @param user 用户信息
   * @returns Promise<TokenPair> JWT令牌对
   *
   * @throws Error 当令牌生成失败时
   *
   * @example
   * ```typescript
   * const tokenPair = await this.generateTokenPair(user);
   * console.log(tokenPair.access_token); // JWT访问令牌
   * console.log(tokenPair.refresh_token); // JWT刷新令牌
   * ```
   */
  async generateTokenPair(user: Users): Promise<TokenPair> {
    try {
      const jwtSecret = this.configService.get<string>('JWT_SECRET');
      const expiresIn = this.configService.get<string>('JWT_EXPIRES_IN', '7d');

      if (!jwtSecret) {
        throw new Error('JWT_SECRET未配置');
      }

      // 创建令牌载荷
      const { accessPayload, refreshPayload } = this.createTokenPayloads(user);

      // 生成令牌
      const { accessToken, refreshToken } = await this.signTokens(accessPayload, refreshPayload, jwtSecret);

      // 计算过期时间
      const expiresInSeconds = this.parseExpirationTime(expiresIn);

      return {
        access_token: accessToken,
        refresh_token: refreshToken,
        expires_in: expiresInSeconds,
        token_type: 'Bearer',
      };

    } catch (error) {
      const err = error as Error;
      throw new Error(`令牌生成失败: ${err.message}`);
    }
  }

  /**
   * 创建令牌载荷
   *
   * @param user 用户信息
   * @returns 访问令牌和刷新令牌载荷
   * @private
   */
  private createTokenPayloads(user: Users): {
    accessPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'>;
    refreshPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'>;
  } {
    const accessPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'> = {
      sub: user.id.toString(),
      username: user.username,
      role: user.role,
      email: user.email,
      type: 'access',
    };

    const refreshPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'> = {
      sub: user.id.toString(),
      username: user.username,
      role: user.role,
      type: 'refresh',
    };

    return { accessPayload, refreshPayload };
  }

  /**
   * 签名令牌
   *
   * @param accessPayload 访问令牌载荷
   * @param refreshPayload 刷新令牌载荷
   * @param jwtSecret JWT密钥
   * @returns 签名后的令牌
   * @private
   */
  private async signTokens(
    accessPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'>,
    refreshPayload: Omit<JwtPayload, 'iss' | 'aud' | 'iat' | 'exp'>,
    jwtSecret: string
  ): Promise<{ accessToken: string; refreshToken: string }> {
    // 生成访问令牌（使用NestJS JwtService，通过options传递iss和aud）
    const accessToken = await this.jwtService.signAsync(accessPayload, {
      issuer: JWT_ISSUER,
      audience: JWT_AUDIENCE,
    });

    // 生成刷新令牌（有效期30天）
    const refreshToken = jwt.sign(refreshPayload, jwtSecret, {
      expiresIn: REFRESH_TOKEN_EXPIRES_IN,
      issuer: JWT_ISSUER,
      audience: JWT_AUDIENCE,
    });

    return { accessToken, refreshToken };
  }

  /**
   * 验证JWT令牌
   *
   * 功能描述：
   * 验证JWT令牌的有效性，包括签名、过期时间和载荷格式
   *
   * 业务逻辑：
   * 1. 验证令牌签名和格式
   * 2. 检查令牌是否过期
   * 3. 验证载荷数据完整性
   * 4. 返回解码后的载荷信息
   *
   * @param token JWT令牌字符串
   * @param tokenType 令牌类型（access 或 refresh）
   * @returns Promise<JwtPayload> 解码后的载荷
   *
   * @throws Error 当令牌无效时
   */
  async verifyToken(token: string, tokenType: 'access' | 'refresh' = 'access'): Promise<JwtPayload> {
    try {
      const jwtSecret = this.configService.get<string>('JWT_SECRET');

      if (!jwtSecret) {
        throw new Error('JWT_SECRET未配置');
      }

      // 1. 验证令牌并解码载荷
      const payload = jwt.verify(token, jwtSecret, {
        issuer: JWT_ISSUER,
        audience: JWT_AUDIENCE,
      }) as JwtPayload;

      // 2. 验证令牌类型
      if (payload.type !== tokenType) {
        throw new Error(`令牌类型不匹配，期望: ${tokenType}，实际: ${payload.type}`);
      }

      // 3. 验证载荷完整性
      if (!payload.sub || !payload.username || payload.role === undefined) {
        throw new Error('令牌载荷数据不完整');
      }

      // 4. 开发阶段会频繁清空用户表，必须拒绝已经没有数据库用户的旧token。
      const user = await this.usersService.findOne(BigInt(payload.sub));
      if (!user) {
        throw new Error('用户不存在或已被删除');
      }
      if (!canUserLogin(user.status)) {
        throw new Error(getUserStatusErrorMessage(user.status));
      }

      return payload;

    } catch (error) {
      const err = error as Error;
      throw new Error(`令牌验证失败: ${err.message}`);
    }
  }

  /**
   * 刷新访问令牌
   *
   * 功能描述：
   * 使用有效的刷新令牌生成新的访问令牌，实现无感知的令牌续期
   *
   * 业务逻辑：
   * 1. 验证刷新令牌的有效性
   * 2. 从数据库获取最新用户信息
   * 3. 生成新的访问令牌
   * 4. 可选择性地轮换刷新令牌
   *
   * @param refreshToken 刷新令牌
   * @returns Promise<TokenPair> 新的令牌对
   *
   * @throws Error 当刷新令牌无效或用户不存在时
   */
  async refreshAccessToken(refreshToken: string): Promise<TokenPair> {
    try {
      // 1. 验证刷新令牌
      const payload = await this.verifyToken(refreshToken, 'refresh');

      // 2. 获取最新用户信息
      const user = await this.usersService.findOne(BigInt(payload.sub));
      if (!user) {
        throw new Error('用户不存在或已被禁用');
      }

      // 3. 生成新的令牌对
      const newTokenPair = await this.generateTokenPair(user);

      return newTokenPair;

    } catch (error) {
      const err = error as Error;
      throw new Error(`令牌刷新失败: ${err.message}`);
    }
  }

  /**
   * 解析过期时间字符串
   *
   * 功能描述：
   * 将时间字符串（如 '7d', '24h', '60m'）转换为秒数
   *
   * @param expiresIn 过期时间字符串
   * @returns number 过期时间（秒）
   * @private
   */
  private parseExpirationTime(expiresIn: string): number {
    if (!expiresIn || typeof expiresIn !== 'string') {
      return DEFAULT_ACCESS_TOKEN_EXPIRES_DAYS * 24 * 60 * 60; // 默认7天
    }

    const timeUnit = expiresIn.slice(-1);
    const timeValue = parseInt(expiresIn.slice(0, -1));

    if (isNaN(timeValue)) {
      return DEFAULT_ACCESS_TOKEN_EXPIRES_DAYS * 24 * 60 * 60; // 默认7天
    }

    switch (timeUnit) {
      case 's': return timeValue;
      case 'm': return timeValue * 60;
      case 'h': return timeValue * 60 * 60;
      case 'd': return timeValue * 24 * 60 * 60;
      case 'w': return timeValue * 7 * 24 * 60 * 60;
      default: return DEFAULT_ACCESS_TOKEN_EXPIRES_DAYS * 24 * 60 * 60; // 默认7天
    }
  }
}
