<?php

declare(strict_types=1);

namespace App\Core;

use Throwable;
use ErrorException;

/**
 * ErrorHandler
 *
 * Converts PHP errors/warnings into exceptions (so a stray warning
 * can't silently corrupt a half-finished DB write), and gives every
 * uncaught exception exactly one place to be logged and turned into a
 * response. In debug mode the real error is shown; in production the
 * visitor only ever sees a generic message — the details go to
 * storage/logs instead.
 */
final class ErrorHandler
{
    public static function register(bool $debug): void
    {
        error_reporting(E_ALL);
        ini_set('display_errors', '0'); // we handle display ourselves

        set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
            if (!(error_reporting() & $severity)) {
                return false;
            }

            throw new ErrorException($message, 0, $severity, $file, $line);
        });

        set_exception_handler(function (Throwable $e) use ($debug): void {
            self::handle($e, $debug);
        });

        register_shutdown_function(function () use ($debug): void {
            $error = error_get_last();

            if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
                self::handle(
                    new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']),
                    $debug
                );
            }
        });
    }

    public static function handle(Throwable $e, bool $debug): void
    {
        Logger::error($e->getMessage(), [
            'exception' => get_class($e),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $debug ? $e->getTraceAsString() : null,
        ]);

        if (!headers_sent()) {
            http_response_code(500);
            header('Content-Type: text/html; charset=UTF-8');
        }

        $wantsJson = str_contains($_SERVER['HTTP_ACCEPT'] ?? '', 'application/json')
            || strtolower($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'xmlhttprequest';

        if ($wantsJson) {
            header('Content-Type: application/json; charset=UTF-8');
            echo json_encode([
                'success' => false,
                'message' => $debug ? $e->getMessage() : 'Something went wrong. Please try again.',
                'debug' => $debug ? [
                    'exception' => get_class($e),
                    'file' => $e->getFile(),
                    'line' => $e->getLine(),
                    'trace' => explode("\n", $e->getTraceAsString()),
                ] : null,
            ]);

            return;
        }

        if ($debug) {
            echo self::renderDebugPage($e);

            return;
        }

        $errorView = dirname(__DIR__) . '/views/errors/500.php';
        if (is_file($errorView)) {
            require $errorView;

            return;
        }

        echo '<h1>500 — Something went wrong</h1><p>Please try again, or contact support if this keeps happening.</p>';
    }

    private static function renderDebugPage(Throwable $e): string
    {
        $message = htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
        $file = htmlspecialchars($e->getFile(), ENT_QUOTES, 'UTF-8');
        $trace = htmlspecialchars($e->getTraceAsString(), ENT_QUOTES, 'UTF-8');
        $exceptionClass = htmlspecialchars(get_class($e), ENT_QUOTES, 'UTF-8');
        $line = $e->getLine();

        return <<<HTML
            <!DOCTYPE html>
            <html><head><title>Application Error</title>
            <style>
                body{font-family:ui-monospace,monospace;background:#0B0F19;color:#f4f4f5;padding:2rem;}
                h1{color:#D2242A;font-size:1.1rem;} .meta{color:#98A2B3;margin-bottom:1rem;}
                pre{background:#1B2333;padding:1rem;border-radius:8px;overflow:auto;white-space:pre-wrap;}
            </style></head><body>
                <h1>Uncaught {$exceptionClass}</h1>
                <p>{$message}</p>
                <p class="meta">{$file}:{$line}</p>
                <pre>{$trace}</pre>
            </body></html>
            HTML;
    }
}
