import { HttpException, HttpStatus } from '@nestjs/common';
import { describe, expect, it, vi } from 'vitest';
import { DomainException } from '../../domain/domain-exception';
import { ErrorCode } from '../../domain/error-codes';
import { GlobalExceptionFilter } from '../filters/global-exception.filter';
import { type LoggerService } from '../logger.service';

function createMockHost(correlationId?: string) {
  const json = vi.fn();
  const status = vi.fn().mockReturnValue({ json });
  const request = {
    headers: { 'x-correlation-id': correlationId },
  };
  const response = { status };

  return {
    switchToHttp: () => ({
      getRequest: () => request,
      getResponse: () => response,
    }),
    json,
    status,
  };
}

describe('GlobalExceptionFilter', () => {
  const logger = {
    log: vi.fn(),
    error: vi.fn(),
    warn: vi.fn(),
    debug: vi.fn(),
    verbose: vi.fn(),
    child: vi.fn(),
  } as unknown as LoggerService;
  const filter = new GlobalExceptionFilter(logger);

  it('should handle DomainException with correct error code', () => {
    const exception = new DomainException(
      ErrorCode.USER_NOT_FOUND,
      'User not found',
      HttpStatus.NOT_FOUND,
    );
    const host = createMockHost('test-corr-id');

    filter.catch(exception, host as never);

    expect(host.status).toHaveBeenCalledWith(HttpStatus.NOT_FOUND);
    expect(host.json).toHaveBeenCalledWith(
      expect.objectContaining({
        statusCode: HttpStatus.NOT_FOUND,
        errorCode: ErrorCode.USER_NOT_FOUND,
        message: 'User not found',
        correlationId: 'test-corr-id',
      }),
    );
  });

  it('should handle standard HttpException', () => {
    const exception = new HttpException('Bad request', HttpStatus.BAD_REQUEST);
    const host = createMockHost();

    filter.catch(exception, host as never);

    expect(host.status).toHaveBeenCalledWith(HttpStatus.BAD_REQUEST);
    expect(host.json).toHaveBeenCalledWith(
      expect.objectContaining({
        statusCode: HttpStatus.BAD_REQUEST,
        errorCode: ErrorCode.BAD_REQUEST,
        message: 'Bad request',
      }),
    );
  });

  it('should handle unknown errors as 500', () => {
    const exception = new Error('Something broke');
    const host = createMockHost();

    filter.catch(exception, host as never);

    expect(host.status).toHaveBeenCalledWith(HttpStatus.INTERNAL_SERVER_ERROR);
    expect(host.json).toHaveBeenCalledWith(
      expect.objectContaining({
        statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
        errorCode: ErrorCode.INTERNAL_ERROR,
        message: 'Internal server error',
      }),
    );
  });

  it('should include timestamp in every response', () => {
    const exception = new Error('test');
    const host = createMockHost();

    filter.catch(exception, host as never);

    expect(host.json).toHaveBeenCalledWith(expect.objectContaining({ timestamp: expect.any(String) }));
  });
});
