<?php

declare(strict_types=1);

namespace RainbowDrive\Support;

use ErrorException;
use Throwable;

final class ErrorHandler
{
    public function register(): void
    {
        error_reporting(E_ALL);

        set_error_handler(static 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(static function (Throwable $e): void {
            $isDebug = getenv('APP_DEBUG') === '1';

            if (PHP_SAPI === 'cli') {
                fwrite(STDERR, ($isDebug ? $e : new ErrorException('Internal Server Error')) . PHP_EOL);
                return;
            }

            $accept = isset($_SERVER['HTTP_ACCEPT']) ? (string) $_SERVER['HTTP_ACCEPT'] : '';
            $requestedWith = isset($_SERVER['HTTP_X_REQUESTED_WITH']) ? (string) $_SERVER['HTTP_X_REQUESTED_WITH'] : '';
            $wantsJson = str_contains($accept, 'application/json')
                || strcasecmp($requestedWith, 'XMLHttpRequest') === 0;

            http_response_code(500);

            if ($wantsJson) {
                header('Content-Type: application/json; charset=utf-8');
                echo json_encode(
                    [
                        'error' => [
                            'type' => $isDebug ? $e::class : 'ServerError',
                            'message' => $isDebug ? $e->getMessage() : 'Internal Server Error',
                        ],
                    ],
                    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
                );
                return;
            }

            header('Content-Type: text/html; charset=utf-8');
            echo $isDebug ? nl2br(htmlspecialchars((string) $e, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')) : 'Internal Server Error';
        });
    }
}
