<?php

declare(strict_types=1);

namespace App\Service\Tenant;

use App\Entity\Ecole;
use App\Entity\TenantDomain;
use App\Repository\EcoleRepository;
use Symfony\Component\HttpFoundation\Request;

final class TenantResolver
{
    public function __construct(
        private readonly EcoleRepository $ecoleRepository,
        private readonly string $publicDomain = 'localhost',
        private readonly string $platformHost = 'localhost',
    ) {
    }

    public function resolveEcole(Request $request): ?Ecole
    {
        $tenant = trim((string) $request->query->get('tenant', ''));
        if ($tenant !== '') {
            $identifier = $this->normalizeIdentifier($tenant);

            return $identifier ? $this->ecoleRepository->findActiveBySlugOrSubdomain($identifier) : null;
        }

        $host = $this->normalizeHost($request->getHost());
        if ($this->isIgnoredHost($host)) {
            return null;
        }

        return $this->ecoleRepository->findActiveByHost($host, $this->publicDomain);
    }

    public function resolveSlug(Request $request): ?string
    {
        $tenant = trim((string) $request->query->get('tenant', ''));
        if ($tenant !== '') {
            return $this->normalizeIdentifier($tenant);
        }

        $host = $this->normalizeHost($request->getHost());
        if ($this->isIgnoredHost($host)) {
            return null;
        }

        $publicDomain = $this->normalizeHost($this->publicDomain);
        if ($publicDomain !== '' && $host !== $publicDomain && str_ends_with($host, '.'.$publicDomain)) {
            return $this->normalizeIdentifier(substr($host, 0, -mb_strlen('.'.$publicDomain)));
        }

        $parts = explode('.', $host);

        if (count($parts) >= 2 && end($parts) === 'localhost' && $parts[0] !== 'www') {
            return $this->normalizeIdentifier($parts[0]);
        }

        if (count($parts) > 2 && $parts[0] !== 'www') {
            return $this->normalizeIdentifier($parts[0]);
        }

        return null;
    }

    public function isTenantHost(Request $request): bool
    {
        return $this->resolveSlug($request) !== null;
    }

    public function canonicalHost(Ecole $ecole): ?string
    {
        $canonicalDomain = $ecole->getCanonicalTenantDomain();
        if ($canonicalDomain?->getHost()) {
            return $canonicalDomain->getHost();
        }

        $subdomain = $this->normalizeIdentifier((string) $ecole->getSousDomaine());
        $publicDomain = $this->normalizeHost($this->publicDomain);

        if ($subdomain !== null && $publicDomain !== '') {
            return sprintf('%s.%s', $subdomain, $publicDomain);
        }

        return null;
    }

    public function canonicalRedirectHost(Request $request): ?string
    {
        $host = $this->normalizeHost($request->getHost());
        if ($this->isIgnoredHost($host)) {
            return null;
        }

        $ecole = $this->ecoleRepository->findActiveByHost($host, $this->publicDomain);
        if (!$ecole instanceof Ecole) {
            return null;
        }

        // A tenant can answer on several hosts, but public traffic must settle on the canonical host for SEO and stable links.
        $canonicalHost = $this->canonicalHost($ecole);

        return $canonicalHost !== null && $canonicalHost !== $host ? $canonicalHost : null;
    }

    private function isIgnoredHost(string $host): bool
    {
        return $host === ''
            || $host === $this->normalizeHost($this->platformHost)
            || $host === 'localhost'
            || $host === '127.0.0.1'
            || $host === '::1'
            || str_starts_with($host, 'www.');
    }

    private function normalizeHost(string $host): string
    {
        return TenantDomain::normalizeHost($host);
    }

    private function normalizeIdentifier(string $identifier): ?string
    {
        $identifier = mb_strtolower(trim($identifier));
        $identifier = trim($identifier, '.');

        return $identifier !== '' ? $identifier : null;
    }
}
