// src/services/logger.server.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

// Unmock the module we are testing to override the global mock from setupFiles.
vi.unmock('./logger.server');

// Mock fs to prevent actual file system operations
vi.mock('fs', () => ({
  default: {
    existsSync: vi.fn(() => true),
    mkdirSync: vi.fn(),
  },
  existsSync: vi.fn(() => true),
  mkdirSync: vi.fn(),
}));

// Create mock objects for pino's multistream functionality
const mockDestinationStream = { write: vi.fn() };
const mockMultistream = { write: vi.fn() };

// Mock pino before importing the logger
// The new logger uses pino.destination() and pino.multistream() for production/test
const mockLoggerInstance = {
  info: vi.fn(),
  warn: vi.fn(),
  error: vi.fn(),
  debug: vi.fn(),
  level: 'info',
  child: vi.fn(() => mockLoggerInstance),
};

// Create a properly typed mock that includes pino's static methods
const mockDestination = vi.fn(() => mockDestinationStream);
const mockMultistreamFn = vi.fn(() => mockMultistream);

const pinoMock = Object.assign(
  vi.fn(() => mockLoggerInstance),
  {
    destination: mockDestination,
    multistream: mockMultistreamFn,
  },
);

vi.mock('pino', () => ({ default: pinoMock }));

describe('Server Logger', () => {
  beforeEach(() => {
    // Reset modules to ensure process.env changes are applied to new module instances
    vi.resetModules();
    vi.clearAllMocks();
    vi.unstubAllEnvs();
  });

  afterEach(() => {
    vi.unstubAllEnvs();
  });

  it('should initialize pino with multistream for production (stdout + file)', async () => {
    vi.stubEnv('NODE_ENV', 'production');
    await import('./logger.server');

    // Production uses pino.destination for file output
    expect(mockDestination).toHaveBeenCalledWith(
      expect.objectContaining({
        dest: expect.stringContaining('app.log'),
        sync: false,
        mkdir: true,
      }),
    );

    // Production uses pino.multistream to combine stdout and file streams
    expect(mockMultistreamFn).toHaveBeenCalledWith(
      expect.arrayContaining([
        expect.objectContaining({ stream: process.stdout }),
        expect.objectContaining({ stream: mockDestinationStream }),
      ]),
    );

    // pino is called with level 'info' for production
    expect(pinoMock).toHaveBeenCalledWith(
      expect.objectContaining({ level: 'info' }),
      mockMultistream,
    );
  });

  it('should initialize pino with pretty-print transport for development', async () => {
    vi.stubEnv('NODE_ENV', 'development');
    await import('./logger.server');

    // Development does NOT use destination or multistream
    expect(mockDestination).not.toHaveBeenCalled();
    expect(mockMultistreamFn).not.toHaveBeenCalled();

    // Development uses pino-pretty transport
    expect(pinoMock).toHaveBeenCalledWith(
      expect.objectContaining({
        level: 'debug',
        transport: expect.objectContaining({
          target: 'pino-pretty',
        }),
      }),
    );
  });

  it('should initialize pino with multistream for test (stdout + file)', async () => {
    // This is the default for vitest, but we stub it for clarity.
    vi.stubEnv('NODE_ENV', 'test');
    await import('./logger.server');

    // Test env also uses file logging like production
    expect(mockDestination).toHaveBeenCalledWith(
      expect.objectContaining({
        dest: expect.stringContaining('app.log'),
        sync: false,
        mkdir: true,
      }),
    );

    expect(mockMultistreamFn).toHaveBeenCalled();

    // Test uses debug level
    expect(pinoMock).toHaveBeenCalledWith(
      expect.objectContaining({ level: 'debug' }),
      mockMultistream,
    );
  });
});
