/**
 * 登录核心服务测试套件
 * 
 * 功能描述：
 * - 测试LoginCoreService的所有核心认证功能
 * - 验证用户登录、注册、密码管理等业务逻辑
 * - 确保OAuth登录和验证码登录功能正常
 * - 测试异常处理和边界条件
 * - 验证与依赖服务的交互正确性
 * 
 * 测试覆盖范围：
 * - 用户认证：密码登录、验证码登录、OAuth登录
 * - 用户注册：邮箱验证、密码强度、唯一性检查
 * - 密码管理：密码修改、密码重置、验证码发送
 * - 邮箱验证：验证码发送、验证、重发机制
 * - 异常处理：各种业务异常和系统异常
 * - 边界条件：参数验证、状态检查、权限控制
 * 
 * 测试策略：
 * - 单元测试：独立测试每个方法的功能逻辑
 * - Mock测试：模拟所有外部依赖服务
 * - 异常测试：验证各种错误情况的处理
 * - 边界测试：测试参数验证和业务规则
 * - 集成测试：验证服务间的交互逻辑
 * 
 * 依赖模块：
 * - Jest: 测试框架和Mock功能
 * - NestJS Testing: 提供测试模块和依赖注入
 * - UsersService: 用户数据操作服务
 * - EmailService: 邮件发送服务
 * - VerificationService: 验证码管理服务
 * 
 * 测试用例统计：
 * - 总计：32个测试用例
 * - login: 4个测试（成功登录、用户不存在、密码错误、用户状态）
 * - register: 4个测试（成功注册、邮箱验证、异常处理、密码验证）
 * - githubOAuth: 2个测试（现有用户、新用户）
 * - sendPasswordResetCode: 2个测试（成功发送、用户不存在）
 * - resetPassword: 4个测试（成功重置、冷却清理、异常处理、验证码错误）
 * - changePassword: 2个测试（成功修改、旧密码错误）
 * - sendLoginVerificationCode: 4个测试（成功发送、测试模式、未验证邮箱、用户不存在）
 * - verificationCodeLogin: 10个测试（邮箱登录、手机登录、冷却清理、异常处理等）
 * 
 * 最近修改：
 * - 2026-01-08: 架构分层优化 - 修正导入路径，从Core层直接导入UserStatus枚举 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2025-12-17
 * @lastModified 2026-01-08
 */

import { Test, TestingModule } from '@nestjs/testing';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { LoginCoreService } from './login_core.service';
import { UsersService } from '../db/users/users.service';
import { EmailService } from '../utils/email/email.service';
import { VerificationService, VerificationCodeType } from '../utils/verification/verification.service';
import { UnauthorizedException, NotFoundException, BadRequestException, ForbiddenException } from '@nestjs/common';
import { UserStatus } from '../db/users/user_status.enum';

describe('LoginCoreService', () => {
  let service: LoginCoreService;
  let usersService: jest.Mocked<UsersService>;
  let emailService: jest.Mocked<EmailService>;
  let verificationService: jest.Mocked<VerificationService>;
  let jwtService: jest.Mocked<JwtService>;
  let configService: jest.Mocked<ConfigService>;

  /**
   * 测试用户数据模拟
   * 
   * 包含完整的用户字段，用于各种测试场景：
   * - 基本信息：用户名、邮箱、手机号、昵称
   * - 认证信息：密码哈希、GitHub ID、头像
   * - 状态信息：角色、邮箱验证状态、用户状态
   * - 时间戳：创建时间、更新时间
   */
  const mockUser = {
    id: BigInt(1),
    username: 'testuser',
    email: 'test@example.com',
    phone: '+8613800138000',
    password_hash: '$2b$12$hashedpassword',
    nickname: '测试用户',
    github_id: null as string | null,
    avatar_url: null as string | null,
    role: 1,
    email_verified: false,
    status: UserStatus.ACTIVE, // 使用正确的枚举类型
    created_at: new Date(),
    updated_at: new Date()
  };

  /**
   * 测试环境初始化
   * 
   * 为每个测试用例准备干净的测试环境：
   * - 创建测试模块和依赖注入
   * - 配置所有外部服务的Mock对象
   * - 确保测试之间的隔离性
   */
  beforeEach(async () => {
    const mockUsersService = {
      findByUsername: jest.fn(),
      findByEmail: jest.fn(),
      findByGithubId: jest.fn(),
      findAll: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      findOne: jest.fn(),
    };

    const mockEmailService = {
      sendVerificationCode: jest.fn(),
      sendWelcomeEmail: jest.fn(),
    };

    const mockVerificationService = {
      generateCode: jest.fn(),
      verifyCode: jest.fn(),
      clearCooldown: jest.fn(),
    };

    const mockJwtService = {
      signAsync: jest.fn(),
      verifyAsync: jest.fn(),
      sign: jest.fn(),
      verify: jest.fn(),
    };

    const mockConfigService = {
      get: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        LoginCoreService,
        {
          provide: 'UsersService',
          useValue: mockUsersService,
        },
        {
          provide: EmailService,
          useValue: mockEmailService,
        },
        {
          provide: VerificationService,
          useValue: mockVerificationService,
        },
        {
          provide: JwtService,
          useValue: mockJwtService,
        },
        {
          provide: ConfigService,
          useValue: mockConfigService,
        },
      ],
    }).compile();

    service = module.get<LoginCoreService>(LoginCoreService);
    usersService = module.get('UsersService');
    emailService = module.get(EmailService);
    verificationService = module.get(VerificationService);
    jwtService = module.get(JwtService);
    configService = module.get(ConfigService);
  });

  /**
   * 服务实例化测试
   * 
   * 验证LoginCoreService能够正确实例化，
   * 确保依赖注入和模块配置正常工作。
   */
  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  /**
   * 用户登录功能测试组
   * 
   * 测试范围：
   * - 正常登录流程：用户名/邮箱/手机号登录
   * - 异常情况：用户不存在、密码错误、用户状态异常
   * - 安全验证：密码哈希验证、用户状态检查
   * - 多种登录方式：支持不同标识符类型的登录
   */
  describe('login', () => {
    it('should login successfully with valid credentials', async () => {
      usersService.findByUsername.mockResolvedValue(mockUser);
      jest.spyOn(service as any, 'verifyPassword').mockResolvedValue(true);

      const result = await service.login({
        identifier: 'testuser',
        password: 'password123'
      });

      expect(result.user).toEqual(mockUser);
      expect(result.isNewUser).toBe(false);
    });

    it('should throw UnauthorizedException for invalid user', async () => {
      usersService.findByUsername.mockResolvedValue(null);
      usersService.findByEmail.mockResolvedValue(null);
      usersService.findAll.mockResolvedValue([]);

      await expect(service.login({
        identifier: 'nonexistent',
        password: 'password123'
      })).rejects.toThrow(UnauthorizedException);
    });

    it('should throw UnauthorizedException for wrong password', async () => {
      // 创建一个正常状态的用户来测试密码验证
      const activeUser = { ...mockUser, status: UserStatus.ACTIVE };
      usersService.findByUsername.mockResolvedValue(activeUser);
      jest.spyOn(service as any, 'verifyPassword').mockResolvedValue(false);

      await expect(service.login({
        identifier: 'testuser',
        password: 'wrongpassword'
      })).rejects.toThrow(UnauthorizedException);
    });

    it('should throw ForbiddenException for inactive user', async () => {
      // 测试非活跃用户状态
      const inactiveUser = { ...mockUser, status: UserStatus.INACTIVE };
      usersService.findByUsername.mockResolvedValue(inactiveUser);

      await expect(service.login({
        identifier: 'testuser',
        password: 'password123'
      })).rejects.toThrow(ForbiddenException);
    });
  });

  /**
   * 用户注册功能测试组
   * 
   * 测试范围：
   * - 基本注册流程：用户名、密码、昵称注册
   * - 邮箱注册：邮箱验证码验证、邮箱唯一性检查
   * - 数据验证：密码强度、用户名唯一性、手机号唯一性
   * - 异常处理：验证码错误、数据冲突、验证失败
   * - 后续操作：欢迎邮件发送、验证码冷却清理
   */
  describe('register', () => {
    it('should register successfully', async () => {
      usersService.create.mockResolvedValue(mockUser);
      jest.spyOn(service as any, 'hashPassword').mockResolvedValue('hashedpassword');
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {});

      const result = await service.register({
        username: 'testuser',
        password: 'password123',
        nickname: '测试用户'
      });

      expect(result.user).toEqual(mockUser);
      expect(result.isNewUser).toBe(true);
    });

    it('should register with email and clear cooldown', async () => {
      const email = 'test@example.com';
      usersService.findByUsername.mockResolvedValue(null);
      usersService.findByEmail.mockResolvedValue(null);
      usersService.create.mockResolvedValue({ ...mockUser, email });
      verificationService.verifyCode.mockResolvedValue(true);
      verificationService.clearCooldown.mockResolvedValue(undefined);
      emailService.sendWelcomeEmail.mockResolvedValue(undefined);
      jest.spyOn(service as any, 'hashPassword').mockResolvedValue('hashedpassword');
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {});

      const result = await service.register({
        username: 'testuser',
        password: 'password123',
        nickname: '测试用户',
        email,
        email_verification_code: '123456'
      });

      expect(result.user.email).toBe(email);
      expect(result.isNewUser).toBe(true);
      expect(verificationService.clearCooldown).toHaveBeenCalledWith(
        email,
        VerificationCodeType.EMAIL_VERIFICATION
      );
    });

    it('should handle cooldown clearing failure gracefully', async () => {
      const email = 'test@example.com';
      usersService.findByUsername.mockResolvedValue(null);
      usersService.findByEmail.mockResolvedValue(null);
      usersService.create.mockResolvedValue({ ...mockUser, email });
      verificationService.verifyCode.mockResolvedValue(true);
      verificationService.clearCooldown.mockRejectedValue(new Error('Redis error'));
      emailService.sendWelcomeEmail.mockResolvedValue(undefined);
      jest.spyOn(service as any, 'hashPassword').mockResolvedValue('hashedpassword');
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {});
      
      // Mock console.warn to avoid test output noise
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

      const result = await service.register({
        username: 'testuser',
        password: 'password123',
        nickname: '测试用户',
        email,
        email_verification_code: '123456'
      });

      expect(result.user.email).toBe(email);
      expect(result.isNewUser).toBe(true);
      expect(verificationService.clearCooldown).toHaveBeenCalledWith(
        email,
        VerificationCodeType.EMAIL_VERIFICATION
      );
      expect(consoleSpy).toHaveBeenCalledWith(
        `清除验证码冷却时间失败: ${email}`,
        expect.any(Error)
      );

      consoleSpy.mockRestore();
    });

    it('should validate password strength', async () => {
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {
        throw new BadRequestException('密码长度至少8位');
      });

      await expect(service.register({
        username: 'testuser',
        password: '123',
        nickname: '测试用户'
      })).rejects.toThrow(BadRequestException);
    });
  });

  /**
   * GitHub OAuth登录功能测试组
   * 
   * 测试范围：
   * - 现有用户登录：GitHub ID匹配、用户信息更新
   * - 新用户注册：用户名冲突处理、自动用户名生成
   * - 用户信息同步：昵称、邮箱、头像更新
   * - 欢迎流程：新用户欢迎邮件发送
   */
  describe('githubOAuth', () => {
    it('should login existing GitHub user', async () => {
      usersService.findByGithubId.mockResolvedValue(mockUser);
      usersService.update.mockResolvedValue(mockUser);

      const result = await service.githubOAuth({
        github_id: 'github123',
        username: 'githubuser',
        nickname: 'GitHub用户'
      });

      expect(result.user).toEqual(mockUser);
      expect(result.isNewUser).toBe(false);
    });

    it('should create new GitHub user', async () => {
      usersService.findByGithubId.mockResolvedValue(null);
      usersService.findByUsername.mockResolvedValue(null);
      usersService.create.mockResolvedValue(mockUser);

      const result = await service.githubOAuth({
        github_id: 'github123',
        username: 'githubuser',
        nickname: 'GitHub用户'
      });

      expect(result.user).toEqual(mockUser);
      expect(result.isNewUser).toBe(true);
    });
  });

  /**
   * 密码重置验证码发送功能测试组
   * 
   * 测试范围：
   * - 邮箱验证码发送：验证码生成、邮件发送
   * - 手机验证码发送：短信验证码（测试模式）
   * - 用户验证：用户存在性检查、邮箱验证状态检查
   * - 异常处理：用户不存在、邮箱未验证、发送失败
   */
  describe('sendPasswordResetCode', () => {
    it('should send reset code for email', async () => {
      const verifiedUser = { ...mockUser, email_verified: true };
      usersService.findByEmail.mockResolvedValue(verifiedUser);
      verificationService.generateCode.mockResolvedValue('123456');
      emailService.sendVerificationCode.mockResolvedValue({
        success: true,
        isTestMode: true
      });

      const result = await service.sendPasswordResetCode('test@example.com');

      expect(result.code).toMatch(/^\d{6}$/);
      expect(result.isTestMode).toBe(true);
    });

    it('should throw NotFoundException for non-existent user', async () => {
      usersService.findByEmail.mockResolvedValue(null);

      await expect(service.sendPasswordResetCode('nonexistent@example.com'))
        .rejects.toThrow(NotFoundException);
    });
  });

  /**
   * 密码重置功能测试组
   * 
   * 测试范围：
   * - 密码重置流程：验证码验证、密码更新
   * - 安全验证：新密码强度验证、验证码有效性
   * - 用户查找：邮箱/手机号用户匹配
   * - 后续处理：验证码冷却清理、异常处理容错
   */
  describe('resetPassword', () => {
    it('should reset password successfully', async () => {
      verificationService.verifyCode.mockResolvedValue(true);
      usersService.findByEmail.mockResolvedValue(mockUser);
      usersService.update.mockResolvedValue(mockUser);
      jest.spyOn(service as any, 'hashPassword').mockResolvedValue('newhashedpassword');
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {});

      const result = await service.resetPassword({
        identifier: 'test@example.com',
        verificationCode: '123456',
        newPassword: 'newpassword123'
      });

      expect(result).toEqual(mockUser);
    });

    it('should reset password and clear cooldown', async () => {
      const identifier = 'test@example.com';
      verificationService.verifyCode.mockResolvedValue(true);
      verificationService.clearCooldown.mockResolvedValue(undefined);
      usersService.findByEmail.mockResolvedValue(mockUser);
      usersService.update.mockResolvedValue(mockUser);
      jest.spyOn(service as any, 'hashPassword').mockResolvedValue('newhashedpassword');
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {});

      const result = await service.resetPassword({
        identifier,
        verificationCode: '123456',
        newPassword: 'newpassword123'
      });

      expect(result).toEqual(mockUser);
      expect(verificationService.clearCooldown).toHaveBeenCalledWith(
        identifier,
        VerificationCodeType.PASSWORD_RESET
      );
    });

    it('should handle cooldown clearing failure gracefully during password reset', async () => {
      const identifier = 'test@example.com';
      verificationService.verifyCode.mockResolvedValue(true);
      verificationService.clearCooldown.mockRejectedValue(new Error('Redis error'));
      usersService.findByEmail.mockResolvedValue(mockUser);
      usersService.update.mockResolvedValue(mockUser);
      jest.spyOn(service as any, 'hashPassword').mockResolvedValue('newhashedpassword');
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {});
      
      // Mock console.warn to avoid test output noise
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

      const result = await service.resetPassword({
        identifier,
        verificationCode: '123456',
        newPassword: 'newpassword123'
      });

      expect(result).toEqual(mockUser);
      expect(verificationService.clearCooldown).toHaveBeenCalledWith(
        identifier,
        VerificationCodeType.PASSWORD_RESET
      );
      expect(consoleSpy).toHaveBeenCalledWith(
        `清除验证码冷却时间失败: ${identifier}`,
        expect.any(Error)
      );

      consoleSpy.mockRestore();
    });

    it('should throw BadRequestException for invalid verification code', async () => {
      verificationService.verifyCode.mockResolvedValue(false);

      await expect(service.resetPassword({
        identifier: 'test@example.com',
        verificationCode: 'invalid',
        newPassword: 'newpassword123'
      })).rejects.toThrow(BadRequestException);
    });
  });

  /**
   * 密码修改功能测试组
   * 
   * 测试范围：
   * - 密码修改流程：旧密码验证、新密码设置
   * - 安全验证：旧密码正确性、新密码强度
   * - OAuth用户处理：无密码用户的异常处理
   * - 权限验证：用户身份确认、操作权限检查
   */
  describe('changePassword', () => {
    it('should change password successfully', async () => {
      usersService.findOne.mockResolvedValue(mockUser);
      usersService.update.mockResolvedValue(mockUser);
      jest.spyOn(service as any, 'verifyPassword').mockResolvedValue(true);
      jest.spyOn(service as any, 'hashPassword').mockResolvedValue('newhashedpassword');
      jest.spyOn(service as any, 'validatePasswordStrength').mockImplementation(() => {});

      const result = await service.changePassword(BigInt(1), 'oldpassword', 'newpassword123');

      expect(result).toEqual(mockUser);
    });

    it('should throw UnauthorizedException for wrong old password', async () => {
      usersService.findOne.mockResolvedValue(mockUser);
      jest.spyOn(service as any, 'verifyPassword').mockResolvedValue(false);

      await expect(service.changePassword(BigInt(1), 'wrongpassword', 'newpassword123'))
        .rejects.toThrow(UnauthorizedException);
    });
  });

  /**
   * 登录验证码发送功能测试组
   * 
   * 测试范围：
   * - 邮箱验证码发送：已验证邮箱的验证码发送
   * - 手机验证码发送：手机号验证码发送（测试模式）
   * - 用户状态验证：用户存在性、邮箱验证状态
   * - 测试模式处理：测试环境和生产环境的区别
   */
  describe('sendLoginVerificationCode', () => {
    it('should successfully send email login verification code', async () => {
      const verifiedUser = { ...mockUser, email_verified: true };
      usersService.findByEmail.mockResolvedValue(verifiedUser);
      verificationService.generateCode.mockResolvedValue('123456');
      emailService.sendVerificationCode.mockResolvedValue({
        success: true,
        isTestMode: false
      });

      const result = await service.sendLoginVerificationCode('test@example.com');

      expect(result.code).toBe('123456');
      expect(result.isTestMode).toBe(false);
      expect(emailService.sendVerificationCode).toHaveBeenCalledWith({
        email: 'test@example.com',
        code: '123456',
        nickname: mockUser.nickname,
        purpose: 'login_verification'
      });
    });

    it('should return verification code in test mode', async () => {
      const verifiedUser = { ...mockUser, email_verified: true };
      usersService.findByEmail.mockResolvedValue(verifiedUser);
      verificationService.generateCode.mockResolvedValue('123456');
      emailService.sendVerificationCode.mockResolvedValue({
        success: true,
        isTestMode: true
      });

      const result = await service.sendLoginVerificationCode('test@example.com');

      expect(result.code).toBe('123456');
      expect(result.isTestMode).toBe(true);
    });

    it('should reject unverified email', async () => {
      usersService.findByEmail.mockResolvedValue(mockUser); // email_verified: false

      await expect(service.sendLoginVerificationCode('test@example.com'))
        .rejects.toThrow('邮箱未验证，无法使用验证码登录');
    });

    it('should reject non-existent user', async () => {
      usersService.findByEmail.mockResolvedValue(null);

      await expect(service.sendLoginVerificationCode('nonexistent@example.com'))
        .rejects.toThrow('用户不存在');
    });
  });

  /**
   * 验证码登录功能测试组
   * 
   * 测试范围：
   * - 邮箱验证码登录：邮箱用户的验证码登录流程
   * - 手机验证码登录：手机号用户的验证码登录流程
   * - 验证码验证：验证码正确性、有效性检查
   * - 用户状态检查：邮箱验证状态、用户存在性
   * - 异常处理：验证码错误、用户不存在、格式错误
   * - 后续处理：验证码冷却清理、异常容错处理
   */
  describe('verificationCodeLogin', () => {
    it('should successfully login with email verification code', async () => {
      const verifiedUser = { ...mockUser, email_verified: true };
      usersService.findByEmail.mockResolvedValue(verifiedUser);
      verificationService.verifyCode.mockResolvedValue(true);

      const result = await service.verificationCodeLogin({
        identifier: 'test@example.com',
        verificationCode: '123456'
      });

      expect(result.user).toEqual(verifiedUser);
      expect(result.isNewUser).toBe(false);
      expect(verificationService.verifyCode).toHaveBeenCalledWith(
        'test@example.com',
        VerificationCodeType.EMAIL_VERIFICATION,
        '123456'
      );
    });

    it('should successfully login with email verification code and clear cooldown', async () => {
      const identifier = 'test@example.com';
      const verifiedUser = { ...mockUser, email_verified: true };
      usersService.findByEmail.mockResolvedValue(verifiedUser);
      verificationService.verifyCode.mockResolvedValue(true);
      verificationService.clearCooldown.mockResolvedValue(undefined);

      const result = await service.verificationCodeLogin({
        identifier,
        verificationCode: '123456'
      });

      expect(result.user).toEqual(verifiedUser);
      expect(result.isNewUser).toBe(false);
      expect(verificationService.clearCooldown).toHaveBeenCalledWith(
        identifier,
        VerificationCodeType.EMAIL_VERIFICATION
      );
    });

    it('should successfully login with phone verification code and clear cooldown', async () => {
      const identifier = '+8613800138000';
      const phoneUser = { ...mockUser, phone: identifier };
      usersService.findAll.mockResolvedValue([phoneUser]);
      verificationService.verifyCode.mockResolvedValue(true);
      verificationService.clearCooldown.mockResolvedValue(undefined);

      const result = await service.verificationCodeLogin({
        identifier,
        verificationCode: '123456'
      });

      expect(result.user).toEqual(phoneUser);
      expect(result.isNewUser).toBe(false);
      expect(verificationService.clearCooldown).toHaveBeenCalledWith(
        identifier,
        VerificationCodeType.SMS_VERIFICATION
      );
    });

    it('should handle cooldown clearing failure gracefully during verification code login', async () => {
      const identifier = 'test@example.com';
      const verifiedUser = { ...mockUser, email_verified: true };
      usersService.findByEmail.mockResolvedValue(verifiedUser);
      verificationService.verifyCode.mockResolvedValue(true);
      verificationService.clearCooldown.mockRejectedValue(new Error('Redis error'));
      
      // Mock console.warn to avoid test output noise
      const consoleSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});

      const result = await service.verificationCodeLogin({
        identifier,
        verificationCode: '123456'
      });

      expect(result.user).toEqual(verifiedUser);
      expect(result.isNewUser).toBe(false);
      expect(verificationService.clearCooldown).toHaveBeenCalledWith(
        identifier,
        VerificationCodeType.EMAIL_VERIFICATION
      );
      expect(consoleSpy).toHaveBeenCalledWith(
        `清除验证码冷却时间失败: ${identifier}`,
        expect.any(Error)
      );

      consoleSpy.mockRestore();
    });

    it('should successfully login with phone verification code', async () => {
      const phoneUser = { ...mockUser, phone: '+8613800138000' };
      usersService.findAll.mockResolvedValue([phoneUser]);
      verificationService.verifyCode.mockResolvedValue(true);

      const result = await service.verificationCodeLogin({
        identifier: '+8613800138000',
        verificationCode: '123456'
      });

      expect(result.user).toEqual(phoneUser);
      expect(result.isNewUser).toBe(false);
      expect(verificationService.verifyCode).toHaveBeenCalledWith(
        '+8613800138000',
        VerificationCodeType.SMS_VERIFICATION,
        '123456'
      );
    });

    it('should reject unverified email user', async () => {
      usersService.findByEmail.mockResolvedValue(mockUser); // email_verified: false

      await expect(service.verificationCodeLogin({
        identifier: 'test@example.com',
        verificationCode: '123456'
      })).rejects.toThrow('邮箱未验证，请先验证邮箱后再使用验证码登录');
    });

    it('should reject non-existent user', async () => {
      usersService.findByEmail.mockResolvedValue(null);

      await expect(service.verificationCodeLogin({
        identifier: 'nonexistent@example.com',
        verificationCode: '123456'
      })).rejects.toThrow('用户不存在，请先注册账户');
    });

    it('should reject invalid verification code', async () => {
      const verifiedUser = { ...mockUser, email_verified: true };
      usersService.findByEmail.mockResolvedValue(verifiedUser);
      verificationService.verifyCode.mockResolvedValue(false);

      await expect(service.verificationCodeLogin({
        identifier: 'test@example.com',
        verificationCode: '999999'
      })).rejects.toThrow('验证码验证失败');
    });

    it('should reject invalid identifier format', async () => {
      await expect(service.verificationCodeLogin({
        identifier: 'invalid-identifier',
        verificationCode: '123456'
      })).rejects.toThrow('请提供有效的邮箱或手机号');
    });
  });
});