/**
 * 登录服务Zulip集成测试
 * 
 * 功能描述：
 * - 测试用户注册时的Zulip账号创建/绑定逻辑
 * - 测试用户登录时的Zulip集成处理
 * - 验证API Key的获取和存储机制
 * - 测试各种异常情况的处理
 * 
 * 测试场景：
 * - 注册时Zulip中没有用户：创建新账号
 * - 注册时Zulip中已有用户：绑定已有账号
 * - 登录时没有Zulip关联：尝试创建/绑定
 * - 登录时已有Zulip关联：刷新API Key
 * - 各种错误情况的处理和回滚
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-10
 * @lastModified 2026-01-10
 */

import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@nestjs/common';
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 { Users } from '../../core/db/users/users.entity';
import { ZulipAccountResponseDto } from '../../core/db/zulip_accounts/zulip_accounts.dto';

describe('LoginService - Zulip Integration', () => {
  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: Users = {
    id: BigInt(12345),
    username: 'testuser',
    nickname: '测试用户',
    email: 'test@example.com',
    email_verified: false,
    phone: null,
    password_hash: 'hashedpassword',
    github_id: null,
    avatar_url: null,
    role: 1,
    status: 'active',
    created_at: new Date(),
    updated_at: new Date(),
  } as Users;

  beforeEach(async () => {
    const mockLoginCoreService = {
      register: jest.fn(),
      login: jest.fn(),
      generateTokenPair: jest.fn(),
    };

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

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

    const mockApiKeySecurityService = {
      storeApiKey: jest.fn(),
      getApiKey: 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);

    // 模拟Logger以避免日志输出
    jest.spyOn(Logger.prototype, 'log').mockImplementation();
    jest.spyOn(Logger.prototype, 'error').mockImplementation();
    jest.spyOn(Logger.prototype, 'warn').mockImplementation();
    jest.spyOn(Logger.prototype, 'debug').mockImplementation();
  });

  describe('用户注册时的Zulip集成', () => {
    it('应该在Zulip中不存在用户时创建新账号', async () => {
      // 准备测试数据
      const registerRequest = {
        username: 'testuser',
        password: 'password123',
        nickname: '测试用户',
        email: 'test@example.com',
      };

      const mockAuthResult = {
        user: mockUser,
        isNewUser: true,
      };

      const mockTokenPair = {
        access_token: 'access_token',
        refresh_token: 'refresh_token',
        expires_in: 3600,
        token_type: 'Bearer',
      };

      const mockZulipCreateResult = {
        success: true,
        userId: 67890,
        email: 'test@example.com',
        apiKey: 'test_api_key_12345678901234567890',
        isExistingUser: false,
      };

      // 设置模拟返回值
      loginCoreService.register.mockResolvedValue(mockAuthResult);
      loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);
      zulipAccountsService.findByGameUserId.mockResolvedValue(null);
      zulipAccountService.createZulipAccount.mockResolvedValue(mockZulipCreateResult);
      apiKeySecurityService.storeApiKey.mockResolvedValue({ success: true, message: 'Stored' });
      zulipAccountsService.create.mockResolvedValue({} as any);

      // 模拟私有方法
      const checkZulipUserExistsSpy = jest.spyOn(service as any, 'checkZulipUserExists')
        .mockResolvedValue({ exists: false });
      jest.spyOn(service as any, 'initializeZulipAdminClient')
        .mockResolvedValue(true);

      // 执行测试
      const result = await service.register(registerRequest);

      // 验证结果
      expect(result.success).toBe(true);
      expect(result.data?.is_new_user).toBe(true);
      expect(result.data?.message).toContain('Zulip');

      // 验证调用
      expect(loginCoreService.register).toHaveBeenCalledWith(registerRequest);
      expect(zulipAccountsService.findByGameUserId).toHaveBeenCalledWith('12345');
      expect(checkZulipUserExistsSpy).toHaveBeenCalledWith('test@example.com');
      expect(zulipAccountService.createZulipAccount).toHaveBeenCalledWith({
        email: 'test@example.com',
        fullName: '测试用户',
        password: 'password123',
      });
      expect(apiKeySecurityService.storeApiKey).toHaveBeenCalledWith('12345', 'test_api_key_12345678901234567890');
      expect(zulipAccountsService.create).toHaveBeenCalledWith({
        gameUserId: '12345',
        zulipUserId: 67890,
        zulipEmail: 'test@example.com',
        zulipFullName: '测试用户',
        zulipApiKeyEncrypted: 'stored_in_redis',
        status: 'active',
        lastVerifiedAt: expect.any(Date),
      });
    });

    it('应该在Zulip中已存在用户时绑定账号', async () => {
      // 准备测试数据
      const registerRequest = {
        username: 'testuser',
        password: 'password123',
        nickname: '测试用户',
        email: 'test@example.com',
      };

      const mockAuthResult = {
        user: mockUser,
        isNewUser: true,
      };

      const mockTokenPair = {
        access_token: 'access_token',
        refresh_token: 'refresh_token',
        expires_in: 3600,
        token_type: 'Bearer',
      };

      // 设置模拟返回值
      loginCoreService.register.mockResolvedValue(mockAuthResult);
      loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);
      zulipAccountsService.findByGameUserId.mockResolvedValue(null);
      apiKeySecurityService.storeApiKey.mockResolvedValue({ success: true, message: 'Stored' });
      zulipAccountsService.create.mockResolvedValue({} as any);

      // 模拟私有方法
      jest.spyOn(service as any, 'checkZulipUserExists')
        .mockResolvedValue({ exists: true, userId: 67890 });
      const refreshZulipApiKeySpy = jest.spyOn(service as any, 'refreshZulipApiKey')
        .mockResolvedValue({ success: true, apiKey: 'existing_api_key_12345678901234567890' });
      jest.spyOn(service as any, 'initializeZulipAdminClient')
        .mockResolvedValue(true);

      // 执行测试
      const result = await service.register(registerRequest);

      // 验证结果
      expect(result.success).toBe(true);
      expect(result.data?.message).toContain('绑定');

      // 验证调用
      expect(refreshZulipApiKeySpy).toHaveBeenCalledWith('test@example.com', 'password123');
      expect(apiKeySecurityService.storeApiKey).toHaveBeenCalledWith('12345', 'existing_api_key_12345678901234567890');
      expect(zulipAccountsService.create).toHaveBeenCalledWith({
        gameUserId: '12345',
        zulipUserId: 67890,
        zulipEmail: 'test@example.com',
        zulipFullName: '测试用户',
        zulipApiKeyEncrypted: 'stored_in_redis',
        status: 'active',
        lastVerifiedAt: expect.any(Date),
      });
    });
  });

  describe('用户登录时的Zulip集成', () => {
    it('应该在用户没有Zulip关联时尝试创建/绑定', async () => {
      // 准备测试数据
      const loginRequest = {
        identifier: 'testuser',
        password: 'password123',
      };

      const mockAuthResult = {
        user: mockUser,
        isNewUser: false,
      };

      const mockTokenPair = {
        access_token: 'access_token',
        refresh_token: 'refresh_token',
        expires_in: 3600,
        token_type: 'Bearer',
      };

      const mockZulipCreateResult = {
        success: true,
        userId: 67890,
        email: 'test@example.com',
        apiKey: 'new_api_key_12345678901234567890',
        isExistingUser: false,
      };

      // 设置模拟返回值
      loginCoreService.login.mockResolvedValue(mockAuthResult);
      loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);
      zulipAccountsService.findByGameUserId.mockResolvedValue(null);
      zulipAccountService.createZulipAccount.mockResolvedValue(mockZulipCreateResult);
      apiKeySecurityService.storeApiKey.mockResolvedValue({ success: true, message: 'Stored' });
      zulipAccountsService.create.mockResolvedValue({} as any);

      // 模拟私有方法
      jest.spyOn(service as any, 'checkZulipUserExists')
        .mockResolvedValue({ exists: false });
      jest.spyOn(service as any, 'initializeZulipAdminClient')
        .mockResolvedValue(true);

      // 执行测试
      const result = await service.login(loginRequest);

      // 验证结果
      expect(result.success).toBe(true);
      expect(result.data?.is_new_user).toBe(false);

      // 验证调用
      expect(loginCoreService.login).toHaveBeenCalledWith(loginRequest);
      expect(zulipAccountsService.findByGameUserId).toHaveBeenCalledWith('12345');
      expect(zulipAccountService.createZulipAccount).toHaveBeenCalledWith({
        email: 'test@example.com',
        fullName: '测试用户',
        password: 'password123',
      });
    });

    it('应该在用户已有Zulip关联时刷新API Key', async () => {
      // 准备测试数据
      const loginRequest = {
        identifier: 'testuser',
        password: 'password123',
      };

      const mockAuthResult = {
        user: mockUser,
        isNewUser: false,
      };

      const mockTokenPair = {
        access_token: 'access_token',
        refresh_token: 'refresh_token',
        expires_in: 3600,
        token_type: 'Bearer',
      };

      const mockExistingAccount: ZulipAccountResponseDto = {
        id: '1',
        gameUserId: '12345',
        zulipUserId: 67890,
        zulipEmail: 'test@example.com',
        zulipFullName: '测试用户',
        status: 'active' as const,
        lastVerifiedAt: new Date().toISOString(),
        retryCount: 0,
        createdAt: new Date().toISOString(),
        updatedAt: new Date().toISOString(),
      };

      // 设置模拟返回值
      loginCoreService.login.mockResolvedValue(mockAuthResult);
      loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);
      zulipAccountsService.findByGameUserId.mockResolvedValue(mockExistingAccount);
      apiKeySecurityService.storeApiKey.mockResolvedValue({ success: true, message: 'Stored' });
      zulipAccountsService.updateByGameUserId.mockResolvedValue({} as any);

      // 模拟私有方法
      const refreshZulipApiKeySpy = jest.spyOn(service as any, 'refreshZulipApiKey')
        .mockResolvedValue({ success: true, apiKey: 'refreshed_api_key_12345678901234567890' });

      // 执行测试
      const result = await service.login(loginRequest);

      // 验证结果
      expect(result.success).toBe(true);

      // 验证调用
      expect(zulipAccountsService.findByGameUserId).toHaveBeenCalledWith('12345');
      expect(refreshZulipApiKeySpy).toHaveBeenCalledWith('test@example.com', 'password123');
      expect(apiKeySecurityService.storeApiKey).toHaveBeenCalledWith('12345', 'refreshed_api_key_12345678901234567890');
      expect(zulipAccountsService.updateByGameUserId).toHaveBeenCalledWith('12345', {
        lastVerifiedAt: expect.any(Date),
        status: 'active',
        errorMessage: null,
      });
    });
  });

  describe('错误处理', () => {
    it('应该在Zulip创建失败时回滚用户注册', async () => {
      // 准备测试数据
      const registerRequest = {
        username: 'testuser',
        password: 'password123',
        nickname: '测试用户',
        email: 'test@example.com',
      };

      const mockAuthResult = {
        user: mockUser,
        isNewUser: true,
      };

      // 设置模拟返回值
      loginCoreService.register.mockResolvedValue(mockAuthResult);
      loginCoreService.deleteUser = jest.fn().mockResolvedValue(true);
      zulipAccountsService.findByGameUserId.mockResolvedValue(null);

      // 模拟Zulip创建失败
      jest.spyOn(service as any, 'checkZulipUserExists')
        .mockResolvedValue({ exists: false });
      jest.spyOn(service as any, 'initializeZulipAdminClient')
        .mockResolvedValue(true);
      
      zulipAccountService.createZulipAccount.mockResolvedValue({
        success: false,
        error: 'Zulip服务器错误',
      });

      // 执行测试
      const result = await service.register(registerRequest);

      // 验证结果
      expect(result.success).toBe(false);
      expect(result.message).toContain('Zulip账号创建失败');

      // 验证回滚调用
      expect(loginCoreService.deleteUser).toHaveBeenCalledWith(mockUser.id);
    });

    it('应该在登录时Zulip集成失败但不影响登录', async () => {
      // 准备测试数据
      const loginRequest = {
        identifier: 'testuser',
        password: 'password123',
      };

      const mockAuthResult = {
        user: mockUser,
        isNewUser: false,
      };

      const mockTokenPair = {
        access_token: 'access_token',
        refresh_token: 'refresh_token',
        expires_in: 3600,
        token_type: 'Bearer',
      };

      // 设置模拟返回值
      loginCoreService.login.mockResolvedValue(mockAuthResult);
      loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);
      zulipAccountsService.findByGameUserId.mockResolvedValue(null);

      // 模拟Zulip集成失败
      jest.spyOn(service as any, 'initializeZulipAdminClient')
        .mockRejectedValue(new Error('Zulip服务器不可用'));

      // 执行测试
      const result = await service.login(loginRequest);

      // 验证结果 - 登录应该成功，即使Zulip集成失败
      expect(result.success).toBe(true);
      expect(result.data?.access_token).toBe('access_token');
    });
  });
});