<?php

namespace App\Exceptions;

use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Throwable;

class ApiExceptionHandler
{
    /**
     * Handle exception untuk API routes
     * 
     * @param Throwable $e
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse|null
     */
    public static function handle(Throwable $e, Request $request): ?\Illuminate\Http\JsonResponse
    {
        // Jika request adalah API route, return JSON response
        if ($request->is('api/*') || $request->expectsJson()) {
            // Get HTTP code dari exception
            $httpCode = self::getHttpCode($e);

            // Get message berdasarkan HTTP code dan exception type
            $message = self::getErrorMessage($e, $httpCode);

            $response = [
                'success' => false,
                'message' => $message,
            ];

            if ($e instanceof ValidationException) {
                $response['errors'] = $e->errors();

                $meta = self::getValidationMeta($e);
                if (! empty($meta)) {
                    $response['data'] = $meta;
                }
            }

            // Tambahkan detail error jika dalam debug mode
            if (config('app.debug')) {
                $response['error'] = [
                    'path' => $request->path(),
                    'type' => get_class($e),
                    'message' => $e->getMessage(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                ];
            }

            return response()->json($response, $httpCode);
        }

        return null;
    }

    /**
     * Get HTTP code dari exception
     */
    protected static function getHttpCode(Throwable $e): int
    {
        if ($e instanceof ValidationException) {
            return $e->status ?? 422;
        }

        if ($e instanceof AuthenticationException) {
            return 401;
        }

        if ($e instanceof AuthorizationException) {
            return 403;
        }

        if ($e instanceof ModelNotFoundException) {
            return 404;
        }

        // Check untuk HttpException
        if ($e instanceof HttpException) {
            return $e->getStatusCode();
        }

        // Check untuk HttpResponseException
        if ($e instanceof HttpResponseException) {
            return $e->getResponse()->getStatusCode();
        }

        // Jika exception memiliki method getCode() dan return valid HTTP code
        if (method_exists($e, 'getCode')) {
            $code = $e->getCode();
            if ($code >= 400 && $code < 600) {
                return $code;
            }
        }

        // Default untuk runtime errors
        return 500;
    }

    /**
     * Get error message berdasarkan exception dan HTTP code
     */
    protected static function getErrorMessage(Throwable $e, int $httpCode): string
    {
        return match ($httpCode) {
            404 => 'Resource not found',
            403 => 'Forbidden',
            401 => 'Unauthorized',
            422 => self::getValidationMessage($e),
            500 => config('app.debug') ? $e->getMessage() : 'Internal server error',
            default => config('app.debug') ? $e->getMessage() : 'An error occurred',
        };
    }

    protected static function getValidationMessage(Throwable $e): string
    {
        if (! $e instanceof ValidationException) {
            return 'Unprocessable entity';
        }

        $errors = $e->errors();

        if (empty($errors)) {
            return 'Validation failed';
        }

        $firstErrorGroup = reset($errors);

        return is_array($firstErrorGroup) && isset($firstErrorGroup[0])
            ? $firstErrorGroup[0]
            : 'Validation failed';
    }

    protected static function getValidationMeta(ValidationException $e): array
    {
        $errors = $e->errors();
        $meta = [];

        if (array_key_exists('remaining', $errors)) {
            $remaining = data_get($errors, 'remaining.0');
            $meta['remaining_attempts'] = is_numeric($remaining) ? (int) $remaining : $remaining;
        }

        if (array_key_exists('retry_after', $errors)) {
            $retryAfter = data_get($errors, 'retry_after.0');
            $meta['retry_after'] = is_numeric($retryAfter) ? (int) $retryAfter : $retryAfter;
        }

        return $meta;
    }
}
