<?php

declare(strict_types=1);

namespace ApiModel\Middleware;

use ApiModel\Service\OrganizationService;
use ApiModel\Shared\Exceptions\BadRequestException;
use ApiModel\Shared\Exceptions\ForbiddenException;
use ApiModel\Shared\Exceptions\UnauthorizedException;
use ApiModel\Shared\Support\AuthContext;
use ApiModel\Shared\Support\CompanyContext;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

final class CompanyContextMiddleware implements MiddlewareInterface
{
    /** @var list<string> */
    private const SKIP_PREFIXES = [
        '/auth',
        '/health',
        '/metrics',
        '/docs',
        '/organization',
        '/users/me',
        '/security',
        '/platform',
        '/notifications',
        '/currencies',
    ];

    public function __construct(private readonly OrganizationService $organization) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $path = $request->getUri()->getPath();
        $basePath = (string) str_replace('/index.php', '', $_SERVER['SCRIPT_NAME'] ?? '');
        if ($basePath !== '' && str_starts_with($path, $basePath)) {
            $path = substr($path, strlen($basePath));
        }

        if ($this->shouldSkip($path)) {
            return $handler->handle($request);
        }

        $requiresCompany = str_starts_with($path, '/accounting');

        $companyId = trim($request->getHeaderLine('X-Company-Id'));
        $branchId = trim($request->getHeaderLine('X-Branch-Id'));

        if ($companyId === '' && ! $requiresCompany) {
            return $handler->handle($request);
        }

        if ($companyId === '' && $requiresCompany) {
            throw new BadRequestException('Se requiere el encabezado X-Company-Id');
        }

        if ($request->getHeaderLine('Authorization') === '') {
            throw new UnauthorizedException('No autorizado');
        }

        $user = AuthContext::jwtUser($request);

        if (! $this->organization->userHasAccess($user['userId'], $companyId, $branchId !== '' ? $branchId : null)) {
            throw new ForbiddenException('Sin acceso a la empresa o sucursal indicada');
        }

        $request = $request->withAttribute(CompanyContext::REQUEST_ATTR, [
            'company_id' => $companyId,
            'branch_id' => $branchId !== '' ? $branchId : null,
        ]);

        return $handler->handle($request);
    }

    private function shouldSkip(string $path): bool
    {
        foreach (self::SKIP_PREFIXES as $prefix) {
            if ($path === $prefix || str_starts_with($path, $prefix . '/')) {
                return true;
            }
        }

        return false;
    }
}
