/**
 * 配置验证属性测试
 * 
 * 功能描述：
 * - 使用fast-check进行配置验证的属性测试
 * - 验证配置验证逻辑的正确性和完整性
 * - 测试各种边界情况和随机输入
 * 
 * 职责分离：
 * - 属性测试：验证配置验证的数学属性
 * - 随机测试：使用随机生成的数据验证逻辑
 * - 边界测试：测试各种边界条件
 * 
 * 最近修改：
 * - 2026-01-12: 代码规范优化 - 从单元测试中分离属性测试 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2026-01-12
 * @lastModified 2026-01-12
 */

import { Test, TestingModule } from '@nestjs/testing';
import * as fc from 'fast-check';
import { ConfigManagerService } from '../../src/core/zulip_core/services/config_manager.service';
import { AppLoggerService } from '../../src/core/utils/logger/logger.service';
import * as fs from 'fs';

// Mock fs module
jest.mock('fs');

describe('ConfigManagerService Property Tests', () => {
  let service: ConfigManagerService;
  let mockLogger: jest.Mocked<AppLoggerService>;
  const mockFs = fs as jest.Mocked<typeof fs>;

  // 默认有效配置
  const validMapConfig = {
    maps: [
      {
        mapId: 'novice_village',
        mapName: '新手村',
        zulipStream: 'Novice Village',
        interactionObjects: [
          {
            objectId: 'notice_board',
            objectName: '公告板',
            zulipTopic: 'Notice Board',
            position: { x: 100, y: 150 }
          }
        ]
      }
    ]
  };

  beforeEach(async () => {
    jest.clearAllMocks();

    // 设置测试环境变量
    process.env.NODE_ENV = 'test';
    process.env.ZULIP_SERVER_URL = 'https://test-zulip.com';
    process.env.ZULIP_BOT_EMAIL = 'test-bot@test.com';
    process.env.ZULIP_BOT_API_KEY = 'test-api-key';
    process.env.ZULIP_API_KEY_ENCRYPTION_KEY = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';

    mockLogger = {
      info: jest.fn(),
      warn: jest.fn(),
      error: jest.fn(),
      debug: jest.fn(),
    } as any;

    // 默认mock fs行为
    mockFs.existsSync.mockReturnValue(true);
    mockFs.readFileSync.mockReturnValue(JSON.stringify(validMapConfig));
    mockFs.writeFileSync.mockImplementation(() => {});
    mockFs.mkdirSync.mockImplementation(() => undefined);

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ConfigManagerService,
        {
          provide: AppLoggerService,
          useValue: mockLogger,
        },
      ],
    }).compile();

    service = module.get<ConfigManagerService>(ConfigManagerService);
    await service.loadMapConfig();
  });

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

  /**
   * 属性测试: 配置验证
   * 
   * **Feature: zulip-integration, Property 12: 配置验证**
   * **Validates: Requirements 10.5**
   * 
   * 对于任何系统配置，系统应该在启动时验证配置的有效性，
   * 并在发现无效配置时报告详细的错误信息
   */
  describe('Property 12: 配置验证', () => {
    /**
     * 属性: 对于任何有效的地图配置，验证应该返回valid=true
     * 验证需求 10.5: 验证配置时系统应在启动时检查配置的有效性并报告错误
     */
    it('对于任何有效的地图配置，验证应该返回valid=true', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的mapId
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的mapName
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          // 生成有效的zulipStream
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          // 生成有效的交互对象数组
          fc.array(
            fc.record({
              objectId: fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
              objectName: fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
              zulipTopic: fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
              position: fc.record({
                x: fc.integer({ min: 0, max: 10000 }),
                y: fc.integer({ min: 0, max: 10000 }),
              }),
            }),
            { minLength: 0, maxLength: 10 }
          ),
          async (mapId, mapName, zulipStream, interactionObjects) => {
            const config = {
              mapId: mapId.trim(),
              mapName: mapName.trim(),
              zulipStream: zulipStream.trim(),
              interactionObjects: interactionObjects.map(obj => ({
                objectId: obj.objectId.trim(),
                objectName: obj.objectName.trim(),
                zulipTopic: obj.zulipTopic.trim(),
                position: obj.position,
              })),
            };

            const result = service.validateMapConfigDetailed(config);
            
            // 有效配置应该通过验证
            expect(result.valid).toBe(true);
            expect(result.errors).toHaveLength(0);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何缺少必填字段的配置，验证应该返回valid=false并包含错误信息
     * 验证需求 10.5: 验证配置时系统应在启动时检查配置的有效性并报告错误
     */
    it('对于任何缺少mapId的配置，验证应该返回valid=false', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的mapName
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          // 生成有效的zulipStream
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          async (mapName, zulipStream) => {
            const config = {
              // 缺少mapId
              mapName: mapName.trim(),
              zulipStream: zulipStream.trim(),
              interactionObjects: [] as any[],
            };

            const result = service.validateMapConfigDetailed(config);
            
            // 缺少mapId应该验证失败
            expect(result.valid).toBe(false);
            expect(result.errors.some(e => e.includes('mapId'))).toBe(true);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何缺少mapName的配置，验证应该返回valid=false
     */
    it('对于任何缺少mapName的配置，验证应该返回valid=false', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的mapId
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的zulipStream
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          async (mapId, zulipStream) => {
            const config = {
              mapId: mapId.trim(),
              // 缺少mapName
              zulipStream: zulipStream.trim(),
              interactionObjects: [] as any[],
            };

            const result = service.validateMapConfigDetailed(config);
            
            // 缺少mapName应该验证失败
            expect(result.valid).toBe(false);
            expect(result.errors.some(e => e.includes('mapName'))).toBe(true);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何缺少zulipStream的配置，验证应该返回valid=false
     */
    it('对于任何缺少zulipStream的配置，验证应该返回valid=false', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的mapId
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的mapName
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          async (mapId, mapName) => {
            const config = {
              mapId: mapId.trim(),
              mapName: mapName.trim(),
              // 缺少zulipStream
              interactionObjects: [] as any[],
            };

            const result = service.validateMapConfigDetailed(config);
            
            // 缺少zulipStream应该验证失败
            expect(result.valid).toBe(false);
            expect(result.errors.some(e => e.includes('zulipStream'))).toBe(true);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 验证结果的错误数量应该与实际错误数量一致
     */
    it('验证结果的错误数量应该与实际错误数量一致', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 随机决定是否包含各个字段
          fc.boolean(),
          fc.boolean(),
          fc.boolean(),
          // 生成字段值
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
          async (includeMapId, includeMapName, includeZulipStream, mapId, mapName, zulipStream) => {
            const config: any = {
              interactionObjects: [] as any[],
            };

            let expectedErrors = 0;

            if (includeMapId) {
              config.mapId = mapId.trim();
            } else {
              expectedErrors++;
            }

            if (includeMapName) {
              config.mapName = mapName.trim();
            } else {
              expectedErrors++;
            }

            if (includeZulipStream) {
              config.zulipStream = zulipStream.trim();
            } else {
              expectedErrors++;
            }

            const result = service.validateMapConfigDetailed(config);
            
            // 错误数量应该与预期一致
            expect(result.errors.length).toBe(expectedErrors);
            expect(result.valid).toBe(expectedErrors === 0);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 配置验证的幂等性
     */
    it('配置验证应该是幂等的', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.record({
            mapId: fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
            mapName: fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
            zulipStream: fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
            interactionObjects: fc.array(
              fc.record({
                objectId: fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
                objectName: fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
                zulipTopic: fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
                position: fc.record({
                  x: fc.integer({ min: 0, max: 10000 }),
                  y: fc.integer({ min: 0, max: 10000 }),
                }),
              }),
              { maxLength: 5 }
            ),
          }),
          async (config) => {
            // 多次验证同一个配置应该返回相同结果
            const result1 = service.validateMapConfigDetailed(config);
            const result2 = service.validateMapConfigDetailed(config);
            const result3 = service.validateMapConfigDetailed(config);
            
            expect(result1.valid).toBe(result2.valid);
            expect(result2.valid).toBe(result3.valid);
            expect(result1.errors).toEqual(result2.errors);
            expect(result2.errors).toEqual(result3.errors);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);
  });
});