<?php

declare(strict_types=1);

namespace ApiModel\Shared\Support;

use ApiModel\Infrastructure\Logging\StructuredLog;
use ApiModel\Shared\Exceptions\ApiException;
use ApiModel\Shared\Http\ErrorResponse;
use ApiModel\Shared\Http\HttpUtil;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

final class HttpRunner
{
    public static function runJson(ServerRequestInterface $request, ResponseInterface $response, callable $fn): ResponseInterface
    {
        try {
            return HttpUtil::json($response, $fn());
        } catch (ApiException $e) {
            $c = (int) $e->getCode();
            $status = $c >= 400 && $c <= 599 ? $c : 500;

            return HttpUtil::error($e->getMessage(), $status, $e->getErrorCode(), $e->getDetails());
        } catch (\InvalidArgumentException $e) {
            return HttpUtil::error($e->getMessage(), 400, 'BAD_REQUEST');
        } catch (ExpiredException|SignatureInvalidException|\UnexpectedValueException) {
            return HttpUtil::error('No autorizado', 401, 'UNAUTHORIZED');
        } catch (\RuntimeException $e) {
            $c = (int) $e->getCode();
            $status = $c >= 400 && $c <= 599 ? $c : 500;

            return HttpUtil::error(
                $e->getMessage(),
                $status,
                ErrorResponse::defaultCodeForStatus($status),
            );
        } catch (\Throwable $e) {
            if (PHP_SAPI === 'cli') {
                fwrite(STDERR, "\nUNHANDLED EXCEPTION: " . $e->getMessage() . "\n" . $e->getTraceAsString() . "\n");
            }
            StructuredLog::exception($e, 'http.unhandled');

            return HttpUtil::error('Error interno', 500, 'INTERNAL_ERROR');
        }
    }
}
