<?php

namespace App\Exceptions;

use App\Helpers\ApiResponse;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Log;
use Symfony\Component\HttpKernel\Exception\{AccessDeniedHttpException,
    HttpException,
    MethodNotAllowedHttpException,
    NotFoundHttpException,
    UnauthorizedHttpException};
use Throwable;

class ApiExceptionHandler
{
    public static function handle(Throwable $exception, Request $request): ?JsonResponse {
        if (!$request->expectsJson() && !$request->is('api/*')) {
            return null;
        }

        return match (true) {
            $exception instanceof ValidationException => self::handleValidation($exception),
            $exception instanceof AuthenticationException => ApiResponse::unauthorized('Authentication required.'),
            $exception instanceof AuthorizationException => ApiResponse::forbidden('This action is unauthorized.'),
            $exception instanceof UnauthorizedHttpException => ApiResponse::unauthorized('Unauthorized.'),
            $exception instanceof AccessDeniedHttpException => ApiResponse::forbidden('Access denied.'),
            $exception instanceof ModelNotFoundException => self::handleModelNotFound($exception),
            $exception instanceof NotFoundHttpException => ApiResponse::notFound('The requested resource was not found.'),
            $exception instanceof MethodNotAllowedHttpException => ApiResponse::error('Method not allowed.', 405),
            $exception instanceof QueryException => self::handleQueryException($exception),
            $exception instanceof HttpException => ApiResponse::error($exception->getMessage(),
                $exception->getStatusCode()),
            default => self::handleGeneric($exception),
        };
    }

    private static function handleValidation(ValidationException $e): JsonResponse {
        return ApiResponse::validationError($e->errors(), 'Validation failed.');
    }

    private static function handleModelNotFound(ModelNotFoundException $e): JsonResponse {
        $model = class_basename($e->getModel());
        return ApiResponse::notFound("The requested {$model} was not found.");
    }

    private static function handleQueryException(QueryException $e): JsonResponse {
        Log::error('Database Query Exception: '.$e->getMessage(), [
            'sql' => $e->getSql(),
            'bindings' => $e->getBindings(),
            'trace' => $e->getTraceAsString(),
        ]);

        $errorCode = $e->errorInfo[1] ?? null;
        return match ($errorCode) {
            1062 => ApiResponse::error('Duplicate entry.', 422),
            1451 => ApiResponse::error('Cannot delete: used by other records.', 422),
            1452 => ApiResponse::error('Referenced record does not exist.', 422),
            default => ApiResponse::serverError('A database error occurred.'),
        };
    }

    private static function handleGeneric(Throwable $e): JsonResponse {
        Log::error('Unhandled Exception: '.$e->getMessage(), [
            'exception' => get_class($e),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTraceAsString(),
        ]);

        return app()->environment('production')
            ? ApiResponse::serverError('An unexpected error occurred. Please try again later.')
            : ApiResponse::serverError('Dev Error: '.$e->getMessage().' in '.$e->getFile().':'.$e->getLine());
    }
}
