/**
 * 应用日志服务测试
 * 
 * 功能描述：
 * - 测试日志服务的核心功能
 * - 验证不同日志级别的正确性
 * - 测试敏感信息过滤功能
 * - 验证请求上下文绑定功能
 * 
 * 职责分离：
 * - 功能测试：测试日志服务的各项核心功能
 * - 安全测试：验证敏感信息过滤机制
 * - 集成测试：测试与其他组件的集成效果
 * - 边界测试：验证各种边界条件和异常情况
 * 
 * 最近修改：
 * - 2026-01-07: 代码规范优化 - 完善测试注释文档，新增完整的测试用例覆盖
 * 
 * 测试覆盖：
 * - 服务实例化
 * - 日志方法调用
 * - 敏感数据过滤
 * - 请求上下文绑定
 * 
 * @author moyin
 * @version 1.0.1
 * @since 2025-12-13
 * @lastModified 2026-01-07
 */

import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { AppLoggerService } from './logger.service';

describe('AppLoggerService', () => {
  let service: AppLoggerService;
  let mockConfigService: jest.Mocked<ConfigService>;

  beforeEach(async () => {
    mockConfigService = {
      get: jest.fn((key: string, defaultValue?: any) => {
        const config: Record<string, any> = {
          NODE_ENV: 'test',
          APP_NAME: 'test-app',
        };
        return config[key] || defaultValue;
      }),
    } as any;

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

    service = module.get<AppLoggerService>(AppLoggerService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  /**
   * 测试所有日志级别方法
   * 
   * 验证点：
   * - 所有日志方法能够正确调用内部 log 方法
   * - 传递的参数格式正确
   * - 日志级别设置正确
   */
  describe('logging methods', () => {
    let logSpy: jest.SpyInstance;

    beforeEach(() => {
      logSpy = jest.spyOn(service as any, 'log').mockImplementation();
    });

    afterEach(() => {
      logSpy.mockRestore();
    });

    it('should log debug messages', () => {
      service.debug('Debug message', { module: 'TestModule' });
      
      expect(logSpy).toHaveBeenCalledWith('debug', {
        message: 'Debug message',
        context: { module: 'TestModule' }
      });
    });

    it('should log info messages', () => {
      service.info('Info message', { module: 'TestModule' });
      
      expect(logSpy).toHaveBeenCalledWith('info', {
        message: 'Info message',
        context: { module: 'TestModule' }
      });
    });

    it('should log warn messages', () => {
      service.warn('Warning message', { module: 'TestModule' });
      
      expect(logSpy).toHaveBeenCalledWith('warn', {
        message: 'Warning message',
        context: { module: 'TestModule' }
      });
    });

    it('should log error messages', () => {
      const stack = 'Error stack trace';
      service.error('Error message', { module: 'TestModule' }, stack);
      
      expect(logSpy).toHaveBeenCalledWith('error', {
        message: 'Error message',
        context: { module: 'TestModule' },
        stack
      });
    });

    it('should log fatal messages', () => {
      const stack = 'Fatal error stack trace';
      service.fatal('Fatal message', { module: 'TestModule' }, stack);
      
      expect(logSpy).toHaveBeenCalledWith('fatal', {
        message: 'Fatal message',
        context: { module: 'TestModule' },
        stack
      });
    });

    it('should log trace messages', () => {
      service.trace('Trace message', { module: 'TestModule' });
      
      expect(logSpy).toHaveBeenCalledWith('trace', {
        message: 'Trace message',
        context: { module: 'TestModule' }
      });
    });

    it('should handle messages without context', () => {
      service.info('Simple message');
      
      expect(logSpy).toHaveBeenCalledWith('info', {
        message: 'Simple message',
        context: undefined
      });
    });
  });

  /**
   * 测试敏感信息过滤功能
   * 
   * 验证点：
   * - 敏感信息过滤方法被正确调用
   * - 包含敏感字段的日志会触发过滤逻辑
   * - 过滤功能不影响正常的日志记录流程
   * - 各种敏感字段都能被正确识别
   */
  describe('sensitive data filtering', () => {
    let redactSpy: jest.SpyInstance;

    beforeEach(() => {
      redactSpy = jest.spyOn(service as any, 'redactSensitiveData');
    });

    afterEach(() => {
      redactSpy.mockRestore();
    });

    it('should filter password fields', () => {
      service.info('Login attempt', { 
        module: 'AuthModule',
        password: 'secret123',
        userPassword: 'another-secret'
      });
      
      expect(redactSpy).toHaveBeenCalled();
    });

    it('should filter token fields', () => {
      service.info('API call', { 
        module: 'ApiModule',
        token: 'jwt-token',
        accessToken: 'access-token'
      });
      
      expect(redactSpy).toHaveBeenCalled();
    });

    it('should filter authorization fields', () => {
      service.info('Request', { 
        module: 'HttpModule',
        authorization: 'Bearer token',
        authHeader: 'Basic auth'
      });
      
      expect(redactSpy).toHaveBeenCalled();
    });

    it('should filter nested sensitive data', () => {
      service.info('Complex data', { 
        module: 'TestModule',
        user: {
          name: 'John',
          password: 'secret',
          profile: {
            token: 'nested-token'
          }
        }
      });
      
      expect(redactSpy).toHaveBeenCalled();
    });
  });

  /**
   * 测试请求上下文绑定功能
   * 
   * 验证点：
   * - bindRequest 方法返回正确的日志方法对象
   * - 返回的对象包含所有必要的日志方法
   * - 绑定的上下文信息能够正确传递
   * - 处理缺失请求信息的情况
   */
  describe('request context binding', () => {
    it('should bind request context with complete request object', () => {
      const mockReq = {
        id: 'req-123',
        headers: {
          'x-request-id': 'custom-req-id',
          'x-user-id': 'user-456'
        },
        ip: '127.0.0.1'
      };

      const boundLogger = service.bindRequest(mockReq, 'TestController');
      
      expect(boundLogger).toHaveProperty('debug');
      expect(boundLogger).toHaveProperty('info');
      expect(boundLogger).toHaveProperty('warn');
      expect(boundLogger).toHaveProperty('error');
      expect(boundLogger).toHaveProperty('fatal');
      expect(boundLogger).toHaveProperty('trace');
      expect(typeof boundLogger.info).toBe('function');
    });

    it('should handle missing request properties', () => {
      const mockReq = {
        headers: {}
      };

      const boundLogger = service.bindRequest(mockReq, 'TestController');
      
      expect(boundLogger).toHaveProperty('info');
      expect(typeof boundLogger.info).toBe('function');
    });

    it('should merge base context with extra context', () => {
      const logSpy = jest.spyOn(service as any, 'log').mockImplementation();
      const mockReq = {
        id: 'req-123',
        headers: { 'x-user-id': 'user-456' },
        ip: '127.0.0.1'
      };

      const boundLogger = service.bindRequest(mockReq, 'TestController');
      boundLogger.info('Test message', { operation: 'testOp' });
      
      expect(logSpy).toHaveBeenCalledWith('info', {
        message: 'Test message',
        context: expect.objectContaining({
          reqId: 'req-123',
          userId: 'user-456',
          ip: '127.0.0.1',
          module: 'TestController',
          operation: 'testOp'
        })
      });
      
      logSpy.mockRestore();
    });
  });

  /**
   * 测试日志级别控制功能
   * 
   * 验证点：
   * - 不同环境下的日志级别控制正确
   * - 禁用的日志级别不会输出
   * - 启用的日志级别正常输出
   */
  describe('log level control', () => {
    it('should respect log level settings in test environment', () => {
      const buildLogDataSpy = jest.spyOn(service as any, 'buildLogData');
      const outputLogSpy = jest.spyOn(service as any, 'outputLog').mockImplementation();

      // In test environment, info should be enabled
      service.info('Test message');
      expect(buildLogDataSpy).toHaveBeenCalled();
      expect(outputLogSpy).toHaveBeenCalled();

      buildLogDataSpy.mockRestore();
      outputLogSpy.mockRestore();
    });
  });

  /**
   * 测试边界情况和异常处理
   * 
   * 验证点：
   * - 处理空消息
   * - 处理null/undefined上下文
   * - 处理循环引用对象
   */
  describe('edge cases', () => {
    it('should handle empty messages', () => {
      const logSpy = jest.spyOn(service as any, 'log').mockImplementation();
      
      service.info('');
      
      expect(logSpy).toHaveBeenCalledWith('info', {
        message: '',
        context: undefined
      });
      
      logSpy.mockRestore();
    });

    it('should handle null context', () => {
      const logSpy = jest.spyOn(service as any, 'log').mockImplementation();
      
      service.info('Test message', null as any);
      
      expect(logSpy).toHaveBeenCalledWith('info', {
        message: 'Test message',
        context: null
      });
      
      logSpy.mockRestore();
    });
  });
});
