import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
} from '@nestjs/common';
import type { FastifyReply, FastifyRequest } from 'fastify';

import { createProblemDetails, type ProblemDetails } from '@hexahost/contracts';

@Catch()
export class Rfc7807ExceptionFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<FastifyReply>();
    const request = ctx.getRequest<FastifyRequest>();

    const status = this.resolveStatus(exception);
    const problem = this.toProblemDetails(exception, status, request.url);

    void response
      .status(status)
      .header('Content-Type', 'application/problem+json')
      .send(problem);
  }

  private resolveStatus(exception: unknown): number {
    if (exception instanceof HttpException) {
      return exception.getStatus();
    }

    return HttpStatus.INTERNAL_SERVER_ERROR;
  }

  private toProblemDetails(
    exception: unknown,
    status: number,
    instance: string,
  ): ProblemDetails {
    if (exception instanceof HttpException) {
      const httpException = exception;
      const response = httpException.getResponse();

      if (typeof response === 'object' && response !== null) {
        const body = response as Record<string, unknown>;

        if (typeof body['status'] === 'string' && Array.isArray(body['checks'])) {
          return createProblemDetails({
            title: 'Service Unavailable',
            status,
            detail: 'One or more readiness checks failed',
            instance,
          });
        }

        if (typeof body['message'] === 'string') {
          return createProblemDetails({
            title: httpException.name,
            status,
            detail: body['message'],
            instance,
          });
        }

        if (Array.isArray(body['message'])) {
          return createProblemDetails({
            title: 'Validation Error',
            status,
            detail: 'Request validation failed',
            instance,
            errors: body['message'].map((message: unknown) => ({
              message: String(message),
            })),
          });
        }
      }

      return createProblemDetails({
        title: httpException.name,
        status,
        detail: httpException.message,
        instance,
      });
    }

    if (exception instanceof Error) {
      return createProblemDetails({
        title: 'Internal Server Error',
        status,
        detail: exception.message,
        instance,
      });
    }

    return createProblemDetails({
      title: 'Internal Server Error',
      status,
      detail: 'An unexpected error occurred',
      instance,
    });
  }
}
