/**
 * LoginService Zulip账号创建属性测试
 * 
 * 功能描述：
 * - 测试用户注册时Zulip账号创建的一致性
 * - 验证账号关联和数据完整性
 * - 测试失败回滚机制
 * 
 * 属性测试：
 * - 属性 13: Zulip账号创建一致性
 * - 验证需求: 账号创建成功率和数据一致性
 * 
 * 最近修改：
 * - 2026-01-08: 文件重命名 - 修正kebab-case为snake_case命名规范 (修改者: moyin)
 * 
 * @author angjustinl
 * @version 1.0.1
 * @since 2025-01-05
 * @lastModified 2026-01-08
 */

import { Test, TestingModule } from '@nestjs/testing';
import { JwtService } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import * as fc from 'fast-check';
import { LoginService } from './login.service';
import { LoginCoreService, RegisterRequest } 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';

describe('LoginService - Zulip账号创建属性测试', () => {
  let loginService: LoginService;
  let loginCoreService: jest.Mocked<LoginCoreService>;
  let zulipAccountService: jest.Mocked<ZulipAccountService>;
  let zulipAccountsService: jest.Mocked<ZulipAccountsService>;
  let apiKeySecurityService: jest.Mocked<ApiKeySecurityService>;

  // 测试用的模拟数据生成器
  const validEmailArb = fc.string({ minLength: 5, maxLength: 50 })
    .filter(s => s.includes('@') && s.includes('.'))
    .map(s => `test_${s.replace(/[^a-zA-Z0-9@._-]/g, '')}@example.com`);

  const validUsernameArb = fc.string({ minLength: 3, maxLength: 20 })
    .filter(s => /^[a-zA-Z0-9_]+$/.test(s));

  const validNicknameArb = fc.string({ minLength: 2, maxLength: 50 })
    .filter(s => s.trim().length > 0);

  const validPasswordArb = fc.string({ minLength: 8, maxLength: 20 })
    .filter(s => /[a-zA-Z]/.test(s) && /\d/.test(s));

  const registerRequestArb = fc.record({
    username: validUsernameArb,
    email: validEmailArb,
    nickname: validNicknameArb,
    password: validPasswordArb,
  });

  beforeEach(async () => {
    // 创建模拟服务
    const mockLoginCoreService = {
      register: jest.fn(),
      deleteUser: 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,
        },
        {
          provide: JwtService,
          useValue: {
            sign: jest.fn().mockReturnValue('mock_jwt_token'),
            signAsync: jest.fn().mockResolvedValue('mock_jwt_token'),
            verify: jest.fn(),
            decode: jest.fn(),
          },
        },
        {
          provide: ConfigService,
          useValue: {
            get: jest.fn((key: string) => {
              switch (key) {
                case 'JWT_SECRET':
                  return 'test_jwt_secret_key_for_testing';
                case 'JWT_EXPIRES_IN':
                  return '7d';
                default:
                  return undefined;
              }
            }),
          },
        },
        {
          provide: 'UsersService',
          useValue: {
            findById: jest.fn(),
            findByUsername: jest.fn(),
            findByEmail: jest.fn(),
            create: jest.fn(),
            update: jest.fn(),
            delete: jest.fn(),
          },
        },
      ],
    }).compile();

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

    // 设置默认的mock返回值
    const mockTokenPair = {
      access_token: 'mock_access_token',
      refresh_token: 'mock_refresh_token',
      expires_in: 604800,
      token_type: 'Bearer'
    };
    loginCoreService.generateTokenPair.mockResolvedValue(mockTokenPair);

    // Mock LoginService 的 initializeZulipAdminClient 方法
    jest.spyOn(loginService as any, 'initializeZulipAdminClient').mockResolvedValue(undefined);

    // 设置环境变量模拟
    process.env.ZULIP_SERVER_URL = 'https://test.zulip.com';
    process.env.ZULIP_BOT_EMAIL = 'bot@test.zulip.com';
    process.env.ZULIP_BOT_API_KEY = 'test_api_key_123';
  });

  afterEach(() => {
    jest.clearAllMocks();
    // 清理环境变量
    delete process.env.ZULIP_SERVER_URL;
    delete process.env.ZULIP_BOT_EMAIL;
    delete process.env.ZULIP_BOT_API_KEY;
  });

  /**
   * 属性 13: Zulip账号创建一致性
   * 
   * 验证需求: 账号创建成功率和数据一致性
   * 
   * 测试内容：
   * 1. 成功注册时，游戏账号和Zulip账号都应该被创建
   * 2. 账号关联信息应该正确存储
   * 3. Zulip账号创建失败时，游戏账号应该被回滚
   * 4. 数据一致性：邮箱、昵称等信息应该保持一致
   */
  describe('属性 13: Zulip账号创建一致性', () => {
    it('应该在成功注册时创建一致的游戏账号和Zulip账号', async () => {
      await fc.assert(
        fc.asyncProperty(registerRequestArb, async (registerRequest) => {
          // 准备测试数据
          const mockGameUser: Users = {
            id: BigInt(Math.floor(Math.random() * 1000000)),
            username: registerRequest.username,
            email: registerRequest.email,
            nickname: registerRequest.nickname,
            password_hash: 'hashed_password',
            role: 1,
            created_at: new Date(),
            updated_at: new Date(),
          } as Users;

          const mockZulipResult = {
            success: true,
            userId: Math.floor(Math.random() * 1000000),
            email: registerRequest.email,
            apiKey: 'zulip_api_key_' + Math.random().toString(36),
          };

          const mockZulipAccount = {
            id: mockGameUser.id.toString(),
            gameUserId: mockGameUser.id.toString(),
            zulipUserId: mockZulipResult.userId,
            zulipEmail: mockZulipResult.email,
            zulipFullName: registerRequest.nickname,
            zulipApiKeyEncrypted: 'encrypted_' + mockZulipResult.apiKey,
            status: 'active' as const,
            retryCount: 0,
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString(),
          };

          // 设置模拟行为
          loginCoreService.register.mockResolvedValue({
            user: mockGameUser,
            isNewUser: true,
          });
          zulipAccountsService.findByGameUserId.mockResolvedValue(null);
          zulipAccountService.createZulipAccount.mockResolvedValue(mockZulipResult);
          apiKeySecurityService.storeApiKey.mockResolvedValue(undefined);
          zulipAccountsService.create.mockResolvedValue(mockZulipAccount);
          zulipAccountService.linkGameAccount.mockResolvedValue(true);

          // 执行注册
          const result = await loginService.register(registerRequest);

          // 验证结果
          expect(result.success).toBe(true);
          expect(result.data?.user.username).toBe(registerRequest.username);
          expect(result.data?.user.email).toBe(registerRequest.email);
          expect(result.data?.user.nickname).toBe(registerRequest.nickname);
          expect(result.data?.is_new_user).toBe(true);

          // 验证Zulip管理员客户端初始化
          expect(loginService['initializeZulipAdminClient']).toHaveBeenCalled();

          // 验证游戏用户注册
          expect(loginCoreService.register).toHaveBeenCalledWith(registerRequest);

          // 验证Zulip账号创建
          expect(zulipAccountService.createZulipAccount).toHaveBeenCalledWith({
            email: registerRequest.email,
            fullName: registerRequest.nickname,
            password: registerRequest.password,
          });

          // 验证API Key存储
          expect(apiKeySecurityService.storeApiKey).toHaveBeenCalledWith(
            mockGameUser.id.toString(),
            mockZulipResult.apiKey
          );

          // 验证账号关联创建
          expect(zulipAccountsService.create).toHaveBeenCalledWith({
            gameUserId: mockGameUser.id.toString(),
            zulipUserId: mockZulipResult.userId,
            zulipEmail: mockZulipResult.email,
            zulipFullName: registerRequest.nickname,
            zulipApiKeyEncrypted: 'stored_in_redis',
            status: 'active',
          });

          // 验证内存关联
          expect(zulipAccountService.linkGameAccount).toHaveBeenCalledWith(
            mockGameUser.id.toString(),
            mockZulipResult.userId,
            mockZulipResult.email,
            mockZulipResult.apiKey
          );
        }),
        { numRuns: 100 }
      );
    });

    it('应该在Zulip账号创建失败时回滚游戏账号', async () => {
      await fc.assert(
        fc.asyncProperty(registerRequestArb, async (registerRequest) => {
          // 准备测试数据
          const mockGameUser: Users = {
            id: BigInt(Math.floor(Math.random() * 1000000)),
            username: registerRequest.username,
            email: registerRequest.email,
            nickname: registerRequest.nickname,
            password_hash: 'hashed_password',
            role: 1,
            created_at: new Date(),
            updated_at: new Date(),
          } as Users;

          // 设置模拟行为 - Zulip账号创建失败
          loginCoreService.register.mockResolvedValue({
            user: mockGameUser,
            isNewUser: true,
          });
          zulipAccountsService.findByGameUserId.mockResolvedValue(null);
          zulipAccountService.createZulipAccount.mockResolvedValue({
            success: false,
            error: 'Zulip服务器连接失败',
            errorCode: 'CONNECTION_FAILED',
          });
          loginCoreService.deleteUser.mockResolvedValue(true);

          // 执行注册
          const result = await loginService.register(registerRequest);

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

          // 验证游戏用户被创建
          expect(loginCoreService.register).toHaveBeenCalledWith(registerRequest);

          // 验证Zulip账号创建尝试
          expect(zulipAccountService.createZulipAccount).toHaveBeenCalledWith({
            email: registerRequest.email,
            fullName: registerRequest.nickname,
            password: registerRequest.password,
          });

          // 验证游戏用户被回滚删除
          expect(loginCoreService.deleteUser).toHaveBeenCalledWith(mockGameUser.id);

          // 验证没有创建账号关联
          expect(zulipAccountsService.create).not.toHaveBeenCalled();
          expect(zulipAccountService.linkGameAccount).not.toHaveBeenCalled();
        }),
        { numRuns: 100 }
      );
    });

    it('应该正确处理已存在Zulip账号关联的情况', async () => {
      await fc.assert(
        fc.asyncProperty(registerRequestArb, async (registerRequest) => {
          // 准备测试数据
          const mockGameUser: Users = {
            id: BigInt(Math.floor(Math.random() * 1000000)),
            username: registerRequest.username,
            email: registerRequest.email,
            nickname: registerRequest.nickname,
            password_hash: 'hashed_password',
            role: 1,
            created_at: new Date(),
            updated_at: new Date(),
          } as Users;

          const existingZulipAccount = {
            id: Math.floor(Math.random() * 1000000).toString(),
            gameUserId: mockGameUser.id.toString(),
            zulipUserId: 12345,
            zulipEmail: registerRequest.email,
            zulipFullName: registerRequest.nickname,
            zulipApiKeyEncrypted: 'existing_encrypted_key',
            status: 'active' as const,
            retryCount: 0,
            createdAt: new Date().toISOString(),
            updatedAt: new Date().toISOString(),
          };

          // 设置模拟行为 - 已存在Zulip账号关联
          loginCoreService.register.mockResolvedValue({
            user: mockGameUser,
            isNewUser: true,
          });
          zulipAccountsService.findByGameUserId.mockResolvedValue(existingZulipAccount);

          // 执行注册
          const result = await loginService.register(registerRequest);

          // 验证结果 - 注册应该成功
          expect(result.success).toBe(true);
          expect(result.data?.user.username).toBe(registerRequest.username);

          // 验证游戏用户被创建
          expect(loginCoreService.register).toHaveBeenCalledWith(registerRequest);

          // 验证检查了现有关联
          expect(zulipAccountsService.findByGameUserId).toHaveBeenCalledWith(mockGameUser.id.toString());

          // 验证没有尝试创建新的Zulip账号
          expect(zulipAccountService.createZulipAccount).not.toHaveBeenCalled();
          expect(zulipAccountsService.create).not.toHaveBeenCalled();
        }),
        { numRuns: 100 }
      );
    });

    it('应该正确处理缺少邮箱或密码的注册请求', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.record({
            username: validUsernameArb,
            nickname: validNicknameArb,
            email: fc.option(validEmailArb, { nil: undefined }),
            password: fc.option(validPasswordArb, { nil: undefined }),
          }),
          async (registerRequest) => {
            // 只测试缺少邮箱或密码的情况
            if (registerRequest.email && registerRequest.password) {
              return; // 跳过完整数据的情况
            }

            // 准备测试数据
            const mockGameUser: Users = {
              id: BigInt(Math.floor(Math.random() * 1000000)),
              username: registerRequest.username,
              email: registerRequest.email || null,
              nickname: registerRequest.nickname,
              password_hash: registerRequest.password ? 'hashed_password' : null,
              role: 1,
              created_at: new Date(),
              updated_at: new Date(),
            } as Users;

            // 设置模拟行为
            loginCoreService.register.mockResolvedValue({
              user: mockGameUser,
              isNewUser: true,
            });

            // 执行注册
            const result = await loginService.register(registerRequest as RegisterRequest);

            // 验证结果 - 注册应该成功，但跳过Zulip账号创建
            expect(result.success).toBe(true);
            expect(result.data?.user.username).toBe(registerRequest.username);
            expect(result.data?.message).toBe('注册成功'); // 不包含Zulip创建信息

            // 验证游戏用户被创建
            expect(loginCoreService.register).toHaveBeenCalledWith(registerRequest);

            // 验证没有尝试创建Zulip账号
            expect(zulipAccountService.createZulipAccount).not.toHaveBeenCalled();
            expect(zulipAccountsService.create).not.toHaveBeenCalled();
          }
        ),
        { numRuns: 50 }
      );
    });

    it('应该正确处理Zulip管理员客户端初始化失败', async () => {
      await fc.assert(
        fc.asyncProperty(registerRequestArb, async (registerRequest) => {
          // 设置模拟行为 - 管理员客户端初始化失败
          jest.spyOn(loginService as any, 'initializeZulipAdminClient')
            .mockRejectedValue(new Error('Zulip管理员客户端初始化失败'));

          // 执行注册
          const result = await loginService.register(registerRequest);

          // 验证结果 - 注册应该失败
          expect(result.success).toBe(false);
          expect(result.message).toContain('Zulip管理员客户端初始化失败');

          // 验证没有尝试创建游戏用户
          expect(loginCoreService.register).not.toHaveBeenCalled();

          // 验证没有尝试创建Zulip账号
          expect(zulipAccountService.createZulipAccount).not.toHaveBeenCalled();

          // 恢复 mock
          jest.spyOn(loginService as any, 'initializeZulipAdminClient').mockResolvedValue(undefined);
        }),
        { numRuns: 50 }
      );
    });

    it('应该正确处理环境变量缺失的情况', async () => {
      await fc.assert(
        fc.asyncProperty(registerRequestArb, async (registerRequest) => {
          // 清除环境变量
          delete process.env.ZULIP_SERVER_URL;
          delete process.env.ZULIP_BOT_EMAIL;
          delete process.env.ZULIP_BOT_API_KEY;

          // 重新设置 mock 以模拟环境变量缺失的错误
          jest.spyOn(loginService as any, 'initializeZulipAdminClient')
            .mockRejectedValue(new Error('Zulip管理员配置不完整，请检查环境变量 ZULIP_SERVER_URL, ZULIP_BOT_EMAIL, ZULIP_BOT_API_KEY'));

          // 执行注册
          const result = await loginService.register(registerRequest);

          // 验证结果 - 注册应该失败
          expect(result.success).toBe(false);
          expect(result.message).toContain('Zulip管理员配置不完整');

          // 验证没有尝试创建游戏用户
          expect(loginCoreService.register).not.toHaveBeenCalled();

          // 恢复环境变量和 mock
          process.env.ZULIP_SERVER_URL = 'https://test.zulip.com';
          process.env.ZULIP_BOT_EMAIL = 'bot@test.zulip.com';
          process.env.ZULIP_BOT_API_KEY = 'test_api_key_123';
          jest.spyOn(loginService as any, 'initializeZulipAdminClient').mockResolvedValue(undefined);
        }),
        { numRuns: 30 }
      );
    });
  });

  /**
   * 数据一致性验证测试
   * 
   * 验证游戏账号和Zulip账号之间的数据一致性
   */
  describe('数据一致性验证', () => {
    it('应该确保游戏账号和Zulip账号使用相同的邮箱和昵称', async () => {
      await fc.assert(
        fc.asyncProperty(registerRequestArb, async (registerRequest) => {
          // 准备测试数据
          const mockGameUser: Users = {
            id: BigInt(Math.floor(Math.random() * 1000000)),
            username: registerRequest.username,
            email: registerRequest.email,
            nickname: registerRequest.nickname,
            password_hash: 'hashed_password',
            role: 1,
            created_at: new Date(),
            updated_at: new Date(),
          } as Users;

          const mockZulipResult = {
            success: true,
            userId: Math.floor(Math.random() * 1000000),
            email: registerRequest.email,
            apiKey: 'zulip_api_key_' + Math.random().toString(36),
          };

          // 设置模拟行为
          loginCoreService.register.mockResolvedValue({
            user: mockGameUser,
            isNewUser: true,
          });
          zulipAccountsService.findByGameUserId.mockResolvedValue(null);
          zulipAccountService.createZulipAccount.mockResolvedValue(mockZulipResult);
          apiKeySecurityService.storeApiKey.mockResolvedValue(undefined);
          zulipAccountsService.create.mockResolvedValue({} as any);
          zulipAccountService.linkGameAccount.mockResolvedValue(true);

          // 执行注册
          await loginService.register(registerRequest);

          // 验证Zulip账号创建时使用了正确的数据
          expect(zulipAccountService.createZulipAccount).toHaveBeenCalledWith({
            email: registerRequest.email, // 相同的邮箱
            fullName: registerRequest.nickname, // 相同的昵称
            password: registerRequest.password, // 相同的密码
          });

          // 验证账号关联存储了正确的数据
          expect(zulipAccountsService.create).toHaveBeenCalledWith(
            expect.objectContaining({
              gameUserId: mockGameUser.id.toString(),
              zulipUserId: mockZulipResult.userId,
              zulipEmail: registerRequest.email, // 相同的邮箱
              zulipFullName: registerRequest.nickname, // 相同的昵称
              zulipApiKeyEncrypted: 'stored_in_redis',
              status: 'active',
            })
          );
        }),
        { numRuns: 100 }
      );
    });
  });
});