/**
 * 操作日志属性测试
 * 
 * Property 11: 操作日志完整性
 * 
 * Validates: Requirements 7.1, 7.2, 7.3, 7.4, 7.5
 * 
 * 测试目标：
 * - 验证操作日志记录的完整性和准确性
 * - 确保敏感操作被正确记录
 * - 验证日志查询和统计功能
 * 
 * 最近修改：
 * - 2026-01-08: 注释规范优化 - 修正@author字段，更新版本号和修改记录 (修改者: moyin)
 * - 2026-01-08: 功能新增 - 创建操作日志属性测试 (修改者: assistant)
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2026-01-08
 * @lastModified 2026-01-08
 */

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AdminDatabaseController } from './admin_database.controller';
import { AdminOperationLogController } from './admin_operation_log.controller';
import { DatabaseManagementService } from './database_management.service';
import { AdminOperationLogService } from './admin_operation_log.service';
import { AdminOperationLogInterceptor } from './admin_operation_log.interceptor';
import { AdminDatabaseExceptionFilter } from './admin_database_exception.filter';
import { AdminGuard } from './admin.guard';
import { UserStatus } from '../user_mgmt/user_status.enum';
import {
  PropertyTestRunner,
  PropertyTestGenerators,
  PropertyTestAssertions,
  DEFAULT_PROPERTY_CONFIG
} from './admin_property_test.base';

describe('Property Test: 操作日志功能', () => {
  let app: INestApplication;
  let module: TestingModule;
  let databaseController: AdminDatabaseController;
  let logController: AdminOperationLogController;
  let mockLogService: any;
  let logEntries: any[] = [];

  beforeAll(async () => {
    logEntries = [];
    
    mockLogService = {
      createLog: jest.fn().mockImplementation((logData) => {
        const logEntry = {
          id: `log_${logEntries.length + 1}`,
          ...logData,
          created_at: new Date().toISOString()
        };
        logEntries.push(logEntry);
        return Promise.resolve(logEntry);
      }),
      queryLogs: jest.fn().mockImplementation((filters, limit, offset) => {
        let filteredLogs = [...logEntries];
        
        if (filters.operation_type) {
          filteredLogs = filteredLogs.filter(log => log.operation_type === filters.operation_type);
        }
        if (filters.admin_id) {
          filteredLogs = filteredLogs.filter(log => log.admin_id === filters.admin_id);
        }
        if (filters.entity_type) {
          filteredLogs = filteredLogs.filter(log => log.entity_type === filters.entity_type);
        }
        
        const total = filteredLogs.length;
        const paginatedLogs = filteredLogs.slice(offset, offset + limit);
        
        return Promise.resolve({ logs: paginatedLogs, total });
      }),
      getLogById: jest.fn().mockImplementation((id) => {
        const log = logEntries.find(entry => entry.id === id);
        return Promise.resolve(log || null);
      }),
      getStatistics: jest.fn().mockImplementation(() => {
        const stats = {
          totalOperations: logEntries.length,
          operationsByType: {},
          operationsByAdmin: {},
          recentActivity: logEntries.slice(-10)
        };
        
        logEntries.forEach(log => {
          stats.operationsByType[log.operation_type] = 
            (stats.operationsByType[log.operation_type] || 0) + 1;
          stats.operationsByAdmin[log.admin_id] = 
            (stats.operationsByAdmin[log.admin_id] || 0) + 1;
        });
        
        return Promise.resolve(stats);
      }),
      cleanupExpiredLogs: jest.fn().mockResolvedValue(0),
      getAdminOperationHistory: jest.fn().mockImplementation((adminId) => {
        const adminLogs = logEntries.filter(log => log.admin_id === adminId);
        return Promise.resolve(adminLogs);
      }),
      getSensitiveOperations: jest.fn().mockImplementation((limit, offset) => {
        const sensitiveOps = ['DELETE', 'BATCH_UPDATE', 'ADMIN_CREATE'];
        const sensitiveLogs = logEntries.filter(log => 
          sensitiveOps.includes(log.operation_type)
        );
        const total = sensitiveLogs.length;
        const paginatedLogs = sensitiveLogs.slice(offset, offset + limit);
        
        return Promise.resolve({ logs: paginatedLogs, total });
      })
    };

    module = await Test.createTestingModule({
      imports: [
        ConfigModule.forRoot({
          isGlobal: true,
          envFilePath: ['.env.test', '.env']
        })
      ],
      controllers: [AdminDatabaseController, AdminOperationLogController],
      providers: [
        DatabaseManagementService,
        {
          provide: AdminOperationLogService,
          useValue: mockLogService
        },
        {
          provide: AdminOperationLogInterceptor,
          useValue: {
            intercept: jest.fn().mockImplementation((context, next) => next.handle())
          }
        },
        {
          provide: 'UsersService',
          useValue: {
            findAll: jest.fn().mockResolvedValue([]),
            findOne: jest.fn().mockImplementation(() => {
              const user = PropertyTestGenerators.generateUser();
              return Promise.resolve({ ...user, id: BigInt(1) });
            }),
            create: jest.fn().mockImplementation((userData) => {
              return Promise.resolve({ ...userData, id: BigInt(1) });
            }),
            update: jest.fn().mockImplementation((id, updateData) => {
              const user = PropertyTestGenerators.generateUser();
              return Promise.resolve({ ...user, ...updateData, id });
            }),
            remove: jest.fn().mockResolvedValue(undefined),
            search: jest.fn().mockResolvedValue([]),
            count: jest.fn().mockResolvedValue(0)
          }
        },
        {
          provide: 'IUserProfilesService',
          useValue: {
            findAll: jest.fn().mockResolvedValue([]),
            findOne: jest.fn().mockResolvedValue({ id: BigInt(1) }),
            create: jest.fn().mockResolvedValue({ id: BigInt(1) }),
            update: jest.fn().mockResolvedValue({ id: BigInt(1) }),
            remove: jest.fn().mockResolvedValue(undefined),
            findByMap: jest.fn().mockResolvedValue([]),
            count: jest.fn().mockResolvedValue(0)
          }
        },
        {
          provide: 'ZulipAccountsService',
          useValue: {
            findMany: jest.fn().mockResolvedValue({ accounts: [] }),
            findById: jest.fn().mockResolvedValue({ id: '1' }),
            create: jest.fn().mockResolvedValue({ id: '1' }),
            update: jest.fn().mockResolvedValue({ id: '1' }),
            delete: jest.fn().mockResolvedValue(undefined),
            batchUpdateStatus: jest.fn().mockResolvedValue({ success: true, updatedCount: 0 }),
            getStatusStatistics: jest.fn().mockResolvedValue({
              active: 0, inactive: 0, suspended: 0, error: 0, total: 0
            })
          }
        }
      ]
    })
    .overrideGuard(AdminGuard)
    .useValue({ canActivate: () => true })
    .compile();

    app = module.createNestApplication();
    app.useGlobalFilters(new AdminDatabaseExceptionFilter());
    await app.init();

    databaseController = module.get<AdminDatabaseController>(AdminDatabaseController);
    logController = module.get<AdminOperationLogController>(AdminOperationLogController);
  });

  afterAll(async () => {
    await app.close();
  });

  beforeEach(() => {
    logEntries.length = 0; // 清空日志记录
  });

  describe('Property 11: 操作日志完整性', () => {
    it('所有CRUD操作都应该生成日志记录', async () => {
      await PropertyTestRunner.runPropertyTest(
        'CRUD操作日志记录完整性',
        () => PropertyTestGenerators.generateUser(),
        async (userData) => {
          const userWithStatus = { ...userData, status: UserStatus.ACTIVE };
          
          // 执行创建操作
          await databaseController.createUser(userWithStatus);
          
          // 执行读取操作
          await databaseController.getUserById('1');
          
          // 执行更新操作
          await databaseController.updateUser('1', { nickname: 'Updated Name' });
          
          // 执行删除操作
          await databaseController.deleteUser('1');
          
          // 验证日志记录
          expect(mockLogService.createLog).toHaveBeenCalledTimes(4);
          
          // 验证日志内容包含必要信息
          const createLogCall = mockLogService.createLog.mock.calls.find(call => 
            call[0].operation_type === 'CREATE'
          );
          const updateLogCall = mockLogService.createLog.mock.calls.find(call => 
            call[0].operation_type === 'UPDATE'
          );
          const deleteLogCall = mockLogService.createLog.mock.calls.find(call => 
            call[0].operation_type === 'DELETE'
          );
          
          expect(createLogCall).toBeDefined();
          expect(updateLogCall).toBeDefined();
          expect(deleteLogCall).toBeDefined();
          
          // 验证日志包含实体信息
          expect(createLogCall[0].entity_type).toBe('User');
          expect(updateLogCall[0].entity_type).toBe('User');
          expect(deleteLogCall[0].entity_type).toBe('User');
        },
        { ...DEFAULT_PROPERTY_CONFIG, iterations: 20 }
      );
    });

    it('日志记录应该包含完整的操作上下文', async () => {
      await PropertyTestRunner.runPropertyTest(
        '日志上下文完整性',
        () => ({
          user: PropertyTestGenerators.generateUser(),
          adminId: `admin_${Math.floor(Math.random() * 1000)}`,
          ipAddress: `192.168.1.${Math.floor(Math.random() * 255)}`,
          userAgent: 'Test-Agent/1.0'
        }),
        async ({ user, adminId, ipAddress, userAgent }) => {
          const userWithStatus = { ...user, status: UserStatus.ACTIVE };
          
          // 模拟带上下文的操作
          await databaseController.createUser(userWithStatus);
          
          // 验证日志记录包含上下文信息
          expect(mockLogService.createLog).toHaveBeenCalled();
          const logCall = mockLogService.createLog.mock.calls[0][0];
          
          expect(logCall).toHaveProperty('operation_type');
          expect(logCall).toHaveProperty('entity_type');
          expect(logCall).toHaveProperty('entity_id');
          expect(logCall).toHaveProperty('admin_id');
          expect(logCall).toHaveProperty('operation_details');
          expect(logCall).toHaveProperty('timestamp');
          
          // 验证时间戳格式
          expect(new Date(logCall.timestamp)).toBeInstanceOf(Date);
        },
        { ...DEFAULT_PROPERTY_CONFIG, iterations: 25 }
      );
    });

    it('敏感操作应该记录详细的前后状态', async () => {
      await PropertyTestRunner.runPropertyTest(
        '敏感操作详细日志',
        () => ({
          accounts: Array.from({ length: Math.floor(Math.random() * 5) + 2 }, 
            () => PropertyTestGenerators.generateZulipAccount()),
          targetStatus: ['active', 'inactive', 'suspended'][Math.floor(Math.random() * 3)]
        }),
        async ({ accounts, targetStatus }) => {
          const accountIds = accounts.map((_, i) => `account_${i + 1}`);
          
          // 执行批量更新操作（敏感操作）
          await databaseController.batchUpdateZulipAccountStatus({
            ids: accountIds,
            status: targetStatus as any,
            reason: '测试批量更新'
          });
          
          // 验证敏感操作日志
          expect(mockLogService.createLog).toHaveBeenCalled();
          const logCall = mockLogService.createLog.mock.calls[0][0];
          
          expect(logCall.operation_type).toBe('BATCH_UPDATE');
          expect(logCall.entity_type).toBe('ZulipAccount');
          expect(logCall.operation_details).toContain('reason');
          expect(logCall.operation_details).toContain(targetStatus);
        },
        { ...DEFAULT_PROPERTY_CONFIG, iterations: 15 }
      );
    });

    it('日志查询应该支持多种过滤条件', async () => {
      await PropertyTestRunner.runPropertyTest(
        '日志查询过滤功能',
        () => {
          // 预先创建一些日志记录
          const operations = ['CREATE', 'UPDATE', 'DELETE', 'BATCH_UPDATE'];
          const entities = ['User', 'UserProfile', 'ZulipAccount'];
          const adminIds = ['admin1', 'admin2', 'admin3'];
          
          return {
            operation_type: operations[Math.floor(Math.random() * operations.length)],
            entity_type: entities[Math.floor(Math.random() * entities.length)],
            admin_id: adminIds[Math.floor(Math.random() * adminIds.length)]
          };
        },
        async (filters) => {
          // 预先添加一些测试日志
          await mockLogService.createLog({
            operation_type: filters.operation_type,
            entity_type: filters.entity_type,
            admin_id: filters.admin_id,
            entity_id: '1',
            operation_details: JSON.stringify({ test: true }),
            timestamp: new Date().toISOString()
          });
          
          // 查询日志
          const response = await logController.getOperationLogs(
            20, // limit
            0,  // offset
            filters.admin_id,
            filters.operation_type,
            filters.entity_type,
            undefined, // operation_result
            undefined, // start_date
            undefined, // end_date
            undefined  // is_sensitive
          );
          
          expect(response.success).toBe(true);
          PropertyTestAssertions.assertListResponseFormat(response);
          
          // 验证过滤结果
          response.data.items.forEach((log: any) => {
            expect(log.operation_type).toBe(filters.operation_type);
            expect(log.entity_type).toBe(filters.entity_type);
            expect(log.admin_id).toBe(filters.admin_id);
          });
        },
        { ...DEFAULT_PROPERTY_CONFIG, iterations: 20 }
      );
    });

    it('日志统计应该准确反映操作情况', async () => {
      await PropertyTestRunner.runPropertyTest(
        '日志统计准确性',
        () => {
          const operations = Array.from({ length: Math.floor(Math.random() * 10) + 5 }, () => ({
            operation_type: ['CREATE', 'UPDATE', 'DELETE'][Math.floor(Math.random() * 3)],
            entity_type: ['User', 'UserProfile'][Math.floor(Math.random() * 2)],
            admin_id: `admin_${Math.floor(Math.random() * 3) + 1}`
          }));
          
          return { operations };
        },
        async ({ operations }) => {
          // 创建测试日志
          for (const op of operations) {
            await mockLogService.createLog({
              ...op,
              entity_id: '1',
              operation_details: JSON.stringify({}),
              timestamp: new Date().toISOString()
            });
          }
          
          // 获取统计信息
          const response = await logController.getOperationStatistics();
          
          expect(response.success).toBe(true);
          expect(response.data.totalOperations).toBe(operations.length);
          expect(response.data.operationsByType).toBeDefined();
          expect(response.data.operationsByAdmin).toBeDefined();
          
          // 验证统计数据准确性
          const expectedByType = {};
          const expectedByAdmin = {};
          
          operations.forEach(op => {
            expectedByType[op.operation_type] = (expectedByType[op.operation_type] || 0) + 1;
            expectedByAdmin[op.admin_id] = (expectedByAdmin[op.admin_id] || 0) + 1;
          });
          
          expect(response.data.operationsByType).toEqual(expectedByType);
          expect(response.data.operationsByAdmin).toEqual(expectedByAdmin);
        },
        { ...DEFAULT_PROPERTY_CONFIG, iterations: 15 }
      );
    });

    it('敏感操作查询应该正确识别和过滤', async () => {
      await PropertyTestRunner.runPropertyTest(
        '敏感操作识别准确性',
        () => {
          const allOperations = ['CREATE', 'READ', 'UPDATE', 'DELETE', 'BATCH_UPDATE', 'ADMIN_CREATE'];
          const operations = Array.from({ length: Math.floor(Math.random() * 8) + 3 }, () => 
            allOperations[Math.floor(Math.random() * allOperations.length)]
          );
          
          return { operations };
        },
        async ({ operations }) => {
          // 创建测试日志
          for (const op of operations) {
            await mockLogService.createLog({
              operation_type: op,
              entity_type: 'User',
              admin_id: 'admin1',
              entity_id: '1',
              operation_details: JSON.stringify({}),
              timestamp: new Date().toISOString()
            });
          }
          
          // 查询敏感操作
          const response = await logController.getSensitiveOperations(20, 0);
          
          expect(response.success).toBe(true);
          PropertyTestAssertions.assertListResponseFormat(response);
          
          // 验证只返回敏感操作
          const sensitiveOps = ['DELETE', 'BATCH_UPDATE', 'ADMIN_CREATE'];
          const expectedSensitiveCount = operations.filter(op => 
            sensitiveOps.includes(op)
          ).length;
          
          expect(response.data.total).toBe(expectedSensitiveCount);
          
          response.data.items.forEach((log: any) => {
            expect(sensitiveOps).toContain(log.operation_type);
          });
        },
        { ...DEFAULT_PROPERTY_CONFIG, iterations: 20 }
      );
    });

    it('管理员操作历史应该完整记录', async () => {
      await PropertyTestRunner.runPropertyTest(
        '管理员操作历史完整性',
        () => {
          const adminId = `admin_${Math.floor(Math.random() * 100)}`;
          const operations = Array.from({ length: Math.floor(Math.random() * 5) + 2 }, () => ({
            operation_type: ['CREATE', 'UPDATE', 'DELETE'][Math.floor(Math.random() * 3)],
            entity_type: 'User',
            admin_id: adminId
          }));
          
          return { adminId, operations };
        },
        async ({ adminId, operations }) => {
          // 创建该管理员的操作日志
          for (const op of operations) {
            await mockLogService.createLog({
              ...op,
              entity_id: '1',
              operation_details: JSON.stringify({}),
              timestamp: new Date().toISOString()
            });
          }
          
          // 创建其他管理员的操作日志（干扰数据）
          await mockLogService.createLog({
            operation_type: 'CREATE',
            entity_type: 'User',
            admin_id: 'other_admin',
            entity_id: '2',
            operation_details: JSON.stringify({}),
            timestamp: new Date().toISOString()
          });
          
          // 查询特定管理员的操作历史
          const response = await logController.getOperationLogs(
            50, // limit
            0,  // offset
            adminId, // adminUserId
            undefined, // operationType
            undefined, // targetType
            undefined, // operationResult
            undefined, // startDate
            undefined, // endDate
            undefined  // isSensitive
          );
          
          expect(response.success).toBe(true);
          expect(response.data.items).toHaveLength(operations.length);
          
          // 验证所有返回的日志都属于指定管理员
          response.data.items.forEach((log: any) => {
            expect(log.admin_id).toBe(adminId);
          });
        },
        { ...DEFAULT_PROPERTY_CONFIG, iterations: 15 }
      );
    });
  });
});