/**
 * AdminOperationLogController 单元测试
 * 
 * 功能描述：
 * - 测试管理员操作日志控制器的所有HTTP端点
 * - 验证请求参数处理和响应格式
 * - 测试权限验证和异常处理
 * 
 * 职责分离：
 * - HTTP层测试，不涉及业务逻辑实现
 * - Mock业务服务，专注控制器逻辑
 * - 验证请求响应的正确性
 * 
 * 最近修改：
 * - 2026-01-09: 功能新增 - 创建AdminOperationLogController单元测试 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.0.0
 * @since 2026-01-09
 * @lastModified 2026-01-09
 */

import { Test, TestingModule } from '@nestjs/testing';
import { AdminOperationLogController } from './admin_operation_log.controller';
import { AdminOperationLogService } from './admin_operation_log.service';
import { AdminGuard } from './admin.guard';
import { AdminOperationLog } from './admin_operation_log.entity';

describe('AdminOperationLogController', () => {
  let controller: AdminOperationLogController;
  let logService: jest.Mocked<AdminOperationLogService>;

  const mockLogService = {
    queryLogs: jest.fn(),
    getLogById: jest.fn(),
    getStatistics: jest.fn(),
    getSensitiveOperations: jest.fn(),
    getAdminOperationHistory: jest.fn(),
    cleanupExpiredLogs: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [AdminOperationLogController],
      providers: [
        {
          provide: AdminOperationLogService,
          useValue: mockLogService,
        },
      ],
    })
      .overrideGuard(AdminGuard)
      .useValue({ canActivate: () => true })
      .compile();

    controller = module.get<AdminOperationLogController>(AdminOperationLogController);
    logService = module.get(AdminOperationLogService);
  });

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

  describe('getOperationLogs', () => {
    it('should query logs with default parameters', async () => {
      const mockLogs = [
        { id: 'log1', operation_type: 'CREATE' },
        { id: 'log2', operation_type: 'UPDATE' },
      ] as AdminOperationLog[];

      logService.queryLogs.mockResolvedValue({ logs: mockLogs, total: 2 });

      const result = await controller.getOperationLogs(50, 0);

      expect(logService.queryLogs).toHaveBeenCalledWith({
        limit: 50,
        offset: 0
      });
      expect(result.success).toBe(true);
      expect(result.data.items).toEqual(mockLogs);
      expect(result.data.total).toBe(2);
    });

    it('should query logs with custom parameters', async () => {
      const mockLogs = [] as AdminOperationLog[];

      logService.queryLogs.mockResolvedValue({ logs: mockLogs, total: 0 });

      const result = await controller.getOperationLogs(
        20,
        10,
        'admin1',
        'CREATE',
        'users',
        'SUCCESS',
        '2026-01-01',
        '2026-01-31',
        'true'
      );

      expect(logService.queryLogs).toHaveBeenCalledWith({
        adminUserId: 'admin1',
        operationType: 'CREATE',
        targetType: 'users',
        operationResult: 'SUCCESS',
        startDate: new Date('2026-01-01'),
        endDate: new Date('2026-01-31'),
        isSensitive: true,
        limit: 20,
        offset: 10
      });
      expect(result.success).toBe(true);
    });

    it('should handle invalid date parameters', async () => {
      await expect(controller.getOperationLogs(
        50,
        0,
        undefined,
        undefined,
        undefined,
        undefined,
        'invalid',
        'invalid'
      )).rejects.toThrow('日期格式无效，请使用ISO格式');
    });

    it('should handle service error', async () => {
      logService.queryLogs.mockRejectedValue(new Error('Database error'));

      await expect(controller.getOperationLogs(50, 0)).rejects.toThrow('Database error');
    });
  });

  describe('getOperationLogById', () => {
    it('should get log by id successfully', async () => {
      const mockLog = {
        id: 'log1',
        operation_type: 'CREATE',
        target_type: 'users'
      } as AdminOperationLog;

      logService.getLogById.mockResolvedValue(mockLog);

      const result = await controller.getOperationLogById('log1');

      expect(logService.getLogById).toHaveBeenCalledWith('log1');
      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockLog);
    });

    it('should handle log not found', async () => {
      logService.getLogById.mockResolvedValue(null);

      await expect(controller.getOperationLogById('nonexistent')).rejects.toThrow('操作日志不存在');
    });

    it('should handle service error', async () => {
      logService.getLogById.mockRejectedValue(new Error('Database error'));

      await expect(controller.getOperationLogById('log1')).rejects.toThrow('Database error');
    });
  });

  describe('getOperationStatistics', () => {
    it('should get statistics successfully', async () => {
      const mockStats = {
        totalOperations: 100,
        successfulOperations: 80,
        failedOperations: 20,
        operationsByType: { CREATE: 50, UPDATE: 30, DELETE: 20 },
        operationsByTarget: { users: 60, profiles: 40 },
        operationsByAdmin: { admin1: 60, admin2: 40 },
        averageDuration: 150.5,
        sensitiveOperations: 10,
        uniqueAdmins: 5
      };

      logService.getStatistics.mockResolvedValue(mockStats);

      const result = await controller.getOperationStatistics();

      expect(logService.getStatistics).toHaveBeenCalledWith(undefined, undefined);
      expect(result.success).toBe(true);
      expect(result.data).toEqual(mockStats);
    });

    it('should get statistics with date range', async () => {
      const mockStats = {
        totalOperations: 50,
        successfulOperations: 40,
        failedOperations: 10,
        operationsByType: {},
        operationsByTarget: {},
        operationsByAdmin: {},
        averageDuration: 100,
        sensitiveOperations: 5,
        uniqueAdmins: 3
      };

      logService.getStatistics.mockResolvedValue(mockStats);

      const result = await controller.getOperationStatistics('2026-01-01', '2026-01-31');

      expect(logService.getStatistics).toHaveBeenCalledWith(
        new Date('2026-01-01'),
        new Date('2026-01-31')
      );
      expect(result.success).toBe(true);
    });

    it('should handle invalid dates', async () => {
      await expect(controller.getOperationStatistics('invalid', 'invalid')).rejects.toThrow('日期格式无效，请使用ISO格式');
    });

    it('should handle service error', async () => {
      logService.getStatistics.mockRejectedValue(new Error('Statistics error'));

      await expect(controller.getOperationStatistics()).rejects.toThrow('Statistics error');
    });
  });

  describe('getSensitiveOperations', () => {
    it('should get sensitive operations successfully', async () => {
      const mockLogs = [
        { id: 'log1', is_sensitive: true }
      ] as AdminOperationLog[];

      logService.getSensitiveOperations.mockResolvedValue({ logs: mockLogs, total: 1 });

      const result = await controller.getSensitiveOperations(50, 0);

      expect(logService.getSensitiveOperations).toHaveBeenCalledWith(50, 0);
      expect(result.success).toBe(true);
      expect(result.data.items).toEqual(mockLogs);
      expect(result.data.total).toBe(1);
    });

    it('should get sensitive operations with pagination', async () => {
      logService.getSensitiveOperations.mockResolvedValue({ logs: [], total: 0 });

      const result = await controller.getSensitiveOperations(20, 10);

      expect(logService.getSensitiveOperations).toHaveBeenCalledWith(20, 10);
    });

    it('should handle service error', async () => {
      logService.getSensitiveOperations.mockRejectedValue(new Error('Query error'));

      await expect(controller.getSensitiveOperations(50, 0)).rejects.toThrow('Query error');
    });
  });

  describe('cleanupExpiredLogs', () => {
    it('should cleanup logs successfully', async () => {
      logService.cleanupExpiredLogs.mockResolvedValue(25);

      const result = await controller.cleanupExpiredLogs(90);

      expect(logService.cleanupExpiredLogs).toHaveBeenCalledWith(90);
      expect(result.success).toBe(true);
      expect(result.data.deleted_count).toBe(25);
    });

    it('should cleanup logs with custom retention days', async () => {
      logService.cleanupExpiredLogs.mockResolvedValue(10);

      const result = await controller.cleanupExpiredLogs(30);

      expect(logService.cleanupExpiredLogs).toHaveBeenCalledWith(30);
      expect(result.data.deleted_count).toBe(10);
    });

    it('should handle invalid retention days', async () => {
      await expect(controller.cleanupExpiredLogs(5)).rejects.toThrow('保留天数必须在7-365天之间');
    });

    it('should handle service error', async () => {
      logService.cleanupExpiredLogs.mockRejectedValue(new Error('Cleanup error'));

      await expect(controller.cleanupExpiredLogs(90)).rejects.toThrow('Cleanup error');
    });
  });
});