<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * A list of exception types with their corresponding custom log levels.
     *
     * @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
     */
    protected $levels = [
        //
    ];

    /**
     * A list of the exception types that are not reported.
     *
     * @var array<int, class-string<\Throwable>>
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    /**
     * Render an exception into an HTTP response.
     */
    public function render($request, Throwable $exception)
    {
        // Handle custom validation exceptions
        if ($exception instanceof \App\Exceptions\ValidationException) {
            if ($request->is('api/*')) {
                return response()->json([
                    'success' => false,
                    'message' => $exception->getMessage(),
                    'error_code' => $exception->getErrorCode(),
                    'errors' => $exception->getErrors(),
                ], 422);
            }

            return redirect()->back()
                ->withInput()
                ->withErrors($exception->getErrors())
                ->with('error', $exception->getMessage());
        }

        // Handle API requests
        if ($request->is('api/*')) {
            return $this->handleApiException($request, $exception);
        }

        // Handle web requests
        return $this->handleWebException($request, $exception);
    }

    /**
     * Handle API exceptions
     */
    protected function handleApiException($request, Throwable $exception)
    {
        $statusCode = $this->getStatusCode($exception);
        $errorCode = $this->getErrorCode($exception);
        $message = $this->getErrorMessage($exception);

        // Log the error with context
        $this->logException($exception, $request, 'api');

        return response()->json([
            'success' => false,
            'error' => [
                'code' => $errorCode,
                'message' => $message,
                'details' => config('app.debug') ? $exception->getMessage() : null,
            ],
        ], $statusCode);
    }

    /**
     * Handle web exceptions
     */
    protected function handleWebException($request, Throwable $exception)
    {
        $statusCode = $this->getStatusCode($exception);

        // For 500 errors, show custom error page
        if ($statusCode >= 500) {
            // Log the error with context
            $this->logException($exception, $request, 'web');

            return response()->view('errors.500', [
                'message' => config('app.debug') ? $exception->getMessage() : 'Something went wrong.',
                'trace' => config('app.debug') ? $exception->getTraceAsString() : null,
            ], 500);
        }

        // For other errors, let Laravel handle them
        return parent::render($request, $exception);
    }

    /**
     * Get appropriate status code for exception
     */
    protected function getStatusCode(Throwable $exception): int
    {
        if ($exception instanceof HttpException) {
            return $exception->getStatusCode();
        }

        // Custom business logic exceptions
        if ($exception instanceof \App\Exceptions\BusinessLogicException) {
            return 422; // Unprocessable Entity
        }

        if ($exception instanceof \App\Exceptions\ValidationException) {
            return 422;
        }

        if ($exception instanceof \App\Exceptions\AuthorizationException) {
            return 403;
        }

        if ($exception instanceof \App\Exceptions\NotFoundException) {
            return 404;
        }

        return 500;
    }

    /**
     * Get error code for exception
     */
    protected function getErrorCode(Throwable $exception): string
    {
        if ($exception instanceof \App\Exceptions\BusinessLogicException) {
            return $exception->getErrorCode();
        }

        return 'INTERNAL_ERROR';
    }

    /**
     * Get user-friendly error message
     */
    protected function getErrorMessage(Throwable $exception): string
    {
        if ($exception instanceof \App\Exceptions\BusinessLogicException) {
            return $exception->getMessage();
        }

        if ($exception instanceof \Illuminate\Validation\ValidationException) {
            return 'Validation failed. Please check your input.';
        }

        if ($exception instanceof \Illuminate\Auth\Access\AuthorizationException) {
            return 'You are not authorized to perform this action.';
        }

        if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
            return 'The requested resource was not found.';
        }

        return 'An unexpected error occurred. Please try again later.';
    }

    /**
     * Log exception with structured context
     */
    protected function logException(Throwable $exception, Request $request, string $context = 'web'): void
    {
        $logData = [
            'message' => $exception->getMessage(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'url' => $request->fullUrl(),
            'method' => $request->method(),
            'ip' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'user_id' => Auth::id(),
            'context' => $context,
            'trace' => $exception->getTraceAsString(),
        ];

        // Determine log level based on exception type
        $level = $this->getLogLevel($exception);

        Log::log($level, 'Exception occurred: ' . $exception->getMessage(), $logData);
    }

    /**
     * Get appropriate log level for exception
     */
    protected function getLogLevel(Throwable $exception): string
    {
        if ($exception instanceof \App\Exceptions\BusinessLogicException) {
            return 'info'; // Business logic errors are expected
        }

        if ($exception instanceof \Illuminate\Validation\ValidationException) {
            return 'info'; // Validation errors are user input issues
        }

        if ($exception instanceof \Illuminate\Auth\Access\AuthorizationException) {
            return 'warning'; // Authorization issues might be security related
        }

        if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
            return 'info'; // Not found errors are common
        }

        return 'error'; // Default to error for unexpected exceptions
    }
}
