<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Tenant;

use function in_array;
use function strlen;

readonly class TenantResolver
{
    private const array RESERVED_SUBDOMAINS = ['www', 'api', 'admin', 'static', 'cdn', 'mail'];

    public function __construct(
        private TenantRegistry $registry,
        private string $baseDomain,
    ) {
    }

    /**
     * Resolves a tenant from a host header value.
     *
     * @throws TenantNotFoundException if tenant cannot be resolved
     */
    public function resolve(string $host): TenantConfig
    {
        $subdomain = $this->extractSubdomain($host);

        if ($subdomain === null) {
            throw TenantNotFoundException::withSubdomain('');
        }

        return $this->registry->getBySubdomain($subdomain);
    }

    /**
     * Extracts the subdomain from a host header.
     * Returns null if no tenant subdomain is present (main domain or reserved subdomain).
     */
    public function extractSubdomain(string $host): ?string
    {
        // Remove port if present
        $host = explode(':', $host)[0];

        // Check if host ends with base domain
        $baseDomain = '.' . $this->baseDomain;
        if (!str_ends_with($host, $baseDomain)) {
            // Host doesn't match our domain - could be the base domain itself
            if ($host === $this->baseDomain) {
                return null;
            }

            return null;
        }

        // Extract subdomain
        $subdomain = substr($host, 0, -strlen($baseDomain));

        // Empty subdomain or reserved
        if ($subdomain === '' || in_array($subdomain, self::RESERVED_SUBDOMAINS, true)) {
            return null;
        }

        return $subdomain;
    }
}
