<?php

declare(strict_types=1);

namespace apexl\Io\middleware;

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function apexl\Io\config;

final readonly class addCorsHeaders implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $response = $handler->handle($request);

        return $this->maybeAddCorsHeaders($request, $response);
    }


    private function maybeAddCorsHeaders(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface
    {
        $referer = $this->refererFromRequest($request);

        if ($referer === null) {
            if (config('app.environment') === 'development') {
                return $this->addCorsHeaders($response);
            }

            return $response;
        }

        if ($domain = $this->matchRefererToAllowedDomains($referer)) {
            return $this->addCorsHeaders($response, $domain);
        }

        return $response;
    }

    private function refererFromRequest(RequestInterface $request): ?string
    {
        return $request->getHeader('HTTP_REFERER')[0] ?? null;
    }

    public function addCorsHeaders(ResponseInterface $response, string $domain = "*"): ResponseInterface
    {
        return $response
            ->withHeader('Access-Control-Allow-Origin', $domain)
            ->withHeader(
                'Access-Control-Allow-Headers',
                implode(', ', [
                    'X-Requested-With',
                    'Content-Type',
                    'Accept',
                    'Origin',
                    'Authorization',
                    ...config('app.allowed_headers'),
                ])
            )
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS')
            ->withHeader('Access-Control-Allow-Credentials', $domain === '*' ? 'false' : 'true');
    }

    private function matchRefererToAllowedDomains(string $referer): ?string
    {
        $allowedDomains = config('app.allowed_domains');

        $bits = parse_url($referer);
        $referer = sprintf(
            '%s://%s%s',
            $bits['scheme'],
            $bits['host'],
            ($bits['port'] ?? null) ? sprintf(":%s", $bits['port']) : ''
        );

        $ix = array_search(trim($referer, '/'), $allowedDomains);

        return $ix === false ? null : $allowedDomains[$ix];
    }

}
