<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
use App\Models\ErrorLog;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Request;

class Handler extends ExceptionHandler
{
    /**
     * The 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',
    ];

    /**
     * Exceptions that should not be logged to database
     *
     * @var array<int, string>
     */
    protected $dontLogToDatabase = [
        \Illuminate\Auth\AuthenticationException::class,
        \Illuminate\Validation\ValidationException::class,
        \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class,
        \Illuminate\Session\TokenMismatchException::class,
    ];

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            // Log to database if not in dontLogToDatabase list
            if (!$this->shouldntLogToDatabase($e)) {
                $this->logToDatabase($e);
            }
        });
    }

    /**
     * Determine if the exception should not be logged to database
     */
    protected function shouldntLogToDatabase(Throwable $e): bool
    {
        foreach ($this->dontLogToDatabase as $type) {
            if ($e instanceof $type) {
                return true;
            }
        }

        return false;
    }

    /**
     * Log exception to database
     */
    protected function logToDatabase(Throwable $e): void
    {
        try {
            $level = $this->determineLogLevel($e);
            
            ErrorLog::create([
                'level' => $level,
                'type' => get_class($e),
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'url' => Request::fullUrl(),
                'method' => Request::method(),
                'ip' => Request::ip(),
                'user_agent' => Request::userAgent(),
                'user_id' => Auth::id(),
                'context' => [
                    'request_data' => $this->getSafeRequestData(),
                    'server' => [
                        'php_version' => PHP_VERSION,
                        'laravel_version' => app()->version(),
                    ],
                ],
            ]);
        } catch (\Exception $loggingException) {
            // If logging fails, just continue without breaking the app
            \Log::error('Failed to log exception to database: ' . $loggingException->getMessage());
        }
    }

    /**
     * Determine log level based on exception type
     */
    protected function determineLogLevel(Throwable $e): string
    {
        // Critical errors
        if ($e instanceof \ErrorException && in_array($e->getSeverity(), [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR])) {
            return 'critical';
        }

        // Database errors are often critical
        if ($e instanceof \Illuminate\Database\QueryException) {
            return 'critical';
        }

        // Warning level
        if ($e instanceof \ErrorException && in_array($e->getSeverity(), [E_WARNING, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING])) {
            return 'warning';
        }

        // Default to error
        return 'error';
    }

    /**
     * Get safe request data (excluding sensitive fields)
     */
    protected function getSafeRequestData(): array
    {
        $data = Request::all();
        
        // Remove sensitive fields
        $sensitiveFields = [
            'token',
            'api_token',
            'secret',
            'api_key',
            'api_secret',
        ];

        foreach ($sensitiveFields as $field) {
            if (isset($data[$field])) {
                $data[$field] = '[REDACTED]';
            }
        }

        return $data;
    }
}