/**
 * 登录业务服务测试
 * 
 * 功能描述：
 * - 测试登录相关的业务逻辑
 * - 测试业务层与核心层的集成
 * - 测试各种异常情况处理
 * 
 * 注意：JWT相关功能已移至Core层，此测试专注于Business层逻辑
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2025-01-06
 * @lastModified 2026-01-07
 */

import { Test, TestingModule } from '@nestjs/testing';
import { LoginService } from './login.service';
import { LoginCoreService } from '../../core/login_core/login_core.service';
import { ZulipAccountService } from '../../core/zulip_core/services/zulip_account.service';
import { ZulipAccountsService } from '../../core/db/zulip_accounts/zulip_accounts.service';
import { ApiKeySecurityService } from '../../core/zulip_core/services/api_key_security.service';
import { UserStatus } from '../../core/db/users/user_status.enum';

describe('LoginService', () => {
  let service: LoginService;
  let loginCoreService: jest.Mocked<LoginCoreService>;
  let zulipAccountService: jest.Mocked<ZulipAccountService>;
  let zulipAccountsService: jest.Mocked<ZulipAccountsService>;
  let apiKeySecurityService: jest.Mocked<ApiKeySecurityService>;

  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,
    status: UserStatus.ACTIVE,
    email_verified: true,
    created_at: new Date(),
    updated_at: new Date(),
  };

  const mockTokenPair = {
    access_token: 'mock_access_token',
    refresh_token: 'mock_refresh_token',
    expires_in: 604800,
    token_type: 'Bearer'
  };

  beforeEach(async () => {
    // Mock environment variables for Zulip
    process.env.ZULIP_SERVER_URL = 'https://test.zulipchat.com';
    process.env.ZULIP_BOT_EMAIL = 'test-bot@test.zulipchat.com';
    process.env.ZULIP_BOT_API_KEY = 'test_api_key_12345';

    const mockLoginCoreService = {
      login: jest.fn(),
      githubOAuth: jest.fn(),
      sendPasswordResetCode: jest.fn(),
      resetPassword: jest.fn(),
      changePassword: jest.fn(),
      verificationCodeLogin: jest.fn(),
      sendLoginVerificationCode: jest.fn(),
      debugVerificationCode: jest.fn(),
      refreshAccessToken: jest.fn(),
      generateTokenPair: jest.fn(),
    };

    const mockZulipAccountService = {
      initializeAdminClient: jest.fn(),
      createZulipAccount: jest.fn(),
      linkGameAccount: jest.fn(),
    };

    const mockZulipAccountsService = {
      findByGameUserId: jest.fn(),
      create: jest.fn(),
      deleteByGameUserId: jest.fn(),
    };

    const mockApiKeySecurityService = {
      storeApiKey: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        LoginService,
        {
          provide: LoginCoreService,
          useValue: mockLoginCoreService,
        },
        {
          provide: ZulipAccountService,
          useValue: mockZulipAccountService,
        },
        {
          provide: 'ZulipAccountsService',
          useValue: mockZulipAccountsService,
        },
        {
          provide: ApiKeySecurityService,
          useValue: mockApiKeySecurityService,
        },
      ],
    }).compile();

    service = module.get<LoginService>(LoginService);
    loginCoreService = module.get(LoginCoreService);
    zulipAccountService = module.get(ZulipAccountService);
    zulipAccountsService = module.get('ZulipAccountsService');
    apiKeySecurityService = module.get(ApiKeySecurityService);

    // Setup default mocks
    loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);
    zulipAccountService.initializeAdminClient.mockResolvedValue(true);
    zulipAccountService.createZulipAccount.mockResolvedValue({
      success: true,
      userId: 123,
      email: 'test@example.com',
      apiKey: 'mock_api_key'
    });
    zulipAccountsService.findByGameUserId.mockResolvedValue(null);
    zulipAccountsService.create.mockResolvedValue({} as any);
    apiKeySecurityService.storeApiKey.mockResolvedValue(undefined);
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('login', () => {
    it('should login successfully and return JWT tokens', async () => {
      loginCoreService.login.mockResolvedValue({
        user: mockUser,
        isNewUser: false
      });

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

      expect(result.success).toBe(true);
      expect(result.data?.user.username).toBe('testuser');
      expect(result.data?.access_token).toBe(mockTokenPair.access_token);
      expect(result.data?.refresh_token).toBe(mockTokenPair.refresh_token);
      expect(loginCoreService.login).toHaveBeenCalledWith({
        identifier: 'testuser',
        password: 'password123'
      });
      expect(loginCoreService.generateTokenPair).toHaveBeenCalledWith(mockUser);
    });

    it('should handle login failure', async () => {
      loginCoreService.login.mockRejectedValue(new Error('用户名或密码错误'));

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

      expect(result.success).toBe(false);
      expect(result.message).toBe('用户名或密码错误');
      expect(result.error_code).toBe('LOGIN_FAILED');
    });
  });

  describe('githubOAuth', () => {
    it('should handle GitHub OAuth successfully', async () => {
      loginCoreService.githubOAuth.mockResolvedValue({
        user: mockUser,
        isNewUser: false
      });

      const result = await service.githubOAuth({
        github_id: '12345',
        username: 'githubuser',
        nickname: 'GitHub用户',
        email: 'github@example.com'
      });

      expect(result.success).toBe(true);
      expect(result.data?.user.username).toBe('testuser');
      expect(result.data?.access_token).toBe(mockTokenPair.access_token);
      expect(loginCoreService.githubOAuth).toHaveBeenCalled();
      expect(loginCoreService.generateTokenPair).toHaveBeenCalledWith(mockUser);
    });
  });

  describe('sendPasswordResetCode', () => {
    it('should handle sendPasswordResetCode in test mode', async () => {
      loginCoreService.sendPasswordResetCode.mockResolvedValue({
        code: '123456',
        isTestMode: true
      });

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

      expect(result.success).toBe(false); // Test mode returns false
      expect(result.data?.verification_code).toBe('123456');
      expect(result.data?.is_test_mode).toBe(true);
      expect(loginCoreService.sendPasswordResetCode).toHaveBeenCalledWith('test@example.com');
    });
  });

  describe('resetPassword', () => {
    it('should handle resetPassword successfully', async () => {
      loginCoreService.resetPassword.mockResolvedValue(undefined);

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

      expect(result.success).toBe(true);
      expect(result.message).toBe('密码重置成功');
      expect(loginCoreService.resetPassword).toHaveBeenCalled();
    });
  });

  describe('changePassword', () => {
    it('should handle changePassword successfully', async () => {
      loginCoreService.changePassword.mockResolvedValue(undefined);

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

      expect(result.success).toBe(true);
      expect(result.message).toBe('密码修改成功');
      expect(loginCoreService.changePassword).toHaveBeenCalledWith(BigInt(1), 'oldpassword', 'newpassword');
    });
  });

  describe('verificationCodeLogin', () => {
    it('should handle verificationCodeLogin successfully', async () => {
      loginCoreService.verificationCodeLogin.mockResolvedValue({
        user: mockUser,
        isNewUser: false
      });

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

      expect(result.success).toBe(true);
      expect(result.data?.user.username).toBe('testuser');
      expect(result.data?.access_token).toBe(mockTokenPair.access_token);
      expect(loginCoreService.verificationCodeLogin).toHaveBeenCalled();
      expect(loginCoreService.generateTokenPair).toHaveBeenCalledWith(mockUser);
    });
  });

  describe('sendLoginVerificationCode', () => {
    it('should handle sendLoginVerificationCode successfully', async () => {
      loginCoreService.sendLoginVerificationCode.mockResolvedValue({
        code: '123456',
        isTestMode: true
      });

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

      expect(result.success).toBe(false); // Test mode returns false
      expect(result.data?.verification_code).toBe('123456');
      expect(result.data?.is_test_mode).toBe(true);
      expect(loginCoreService.sendLoginVerificationCode).toHaveBeenCalledWith('test@example.com');
    });
  });

  describe('debugVerificationCode', () => {
    it('should handle debugVerificationCode successfully', async () => {
      const mockDebugInfo = {
        email: 'test@example.com',
        hasCode: true,
        codeExpiry: new Date()
      };

      loginCoreService.debugVerificationCode.mockResolvedValue(mockDebugInfo);

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

      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockDebugInfo);
      expect(loginCoreService.debugVerificationCode).toHaveBeenCalledWith('test@example.com');
    });
  });
});