<?php

namespace App\Domains\Tenant\Support;

class TenantDomainNormalizer
{
    public static function hasValue(mixed $domain): bool
    {
        return is_string($domain) && trim(urldecode($domain)) !== '';
    }

    public static function normalize(mixed $domain): ?string
    {
        if (! is_string($domain)) {
            return null;
        }

        $decodedDomain = trim(urldecode($domain));

        if ($decodedDomain === '') {
            return null;
        }

        $candidate = str_contains($decodedDomain, '://')
            ? $decodedDomain
            : "//{$decodedDomain}";

        $host = parse_url($candidate, PHP_URL_HOST);

        if (! is_string($host) || $host === '') {
            return null;
        }

        return strtolower($host);
    }

    public static function normalizeTenantKey(mixed $domain, mixed $path = null): ?string
    {
        $host = self::normalize($domain);

        if ($host === null) {
            return null;
        }

        $path ??= self::pathFromDomain($domain);

        $normalizedPath = self::normalizePath($path);

        if ($normalizedPath === null) {
            return null;
        }

        return $host.($normalizedPath === '/' ? '' : $normalizedPath);
    }

    public static function pathFromDomain(mixed $domain): ?string
    {
        if (! is_string($domain)) {
            return null;
        }

        $decodedDomain = trim(urldecode($domain));

        if ($decodedDomain === '') {
            return null;
        }

        $candidate = str_contains($decodedDomain, '://')
            ? $decodedDomain
            : "//{$decodedDomain}";

        return self::normalizePath(parse_url($candidate, PHP_URL_PATH) ?: '/');
    }

    public static function normalizePath(mixed $path): ?string
    {
        if (! is_string($path)) {
            return null;
        }

        $path = trim($path);

        if ($path === '' || $path === '/') {
            return '/';
        }

        $path = parse_url(str_starts_with($path, '/') ? $path : "/{$path}", PHP_URL_PATH);

        if (! is_string($path)) {
            return null;
        }

        $path = preg_replace('#/+#', '/', $path);

        if (! is_string($path)) {
            return null;
        }

        $segments = array_filter(explode('/', $path), static fn (string $segment): bool => $segment !== '');

        foreach ($segments as $segment) {
            if ($segment === '.' || $segment === '..') {
                return null;
            }
        }

        return '/'.implode('/', $segments);
    }

    /** @return list<string> */
    public static function tenantKeyCandidates(mixed $domain, mixed $path): array
    {
        $host = self::normalize($domain);

        if ($host === null) {
            return [];
        }

        return array_map(
            static fn (string $basePath): string => $host.($basePath === '/' ? '' : $basePath),
            self::basePathCandidates($path),
        );
    }

    /** @return list<string> */
    public static function basePathCandidates(mixed $path): array
    {
        $normalizedPath = self::normalizePath($path);

        if ($normalizedPath === null) {
            return [];
        }

        $segments = array_values(array_filter(
            explode('/', $normalizedPath),
            static fn (string $segment): bool => $segment !== '',
        ));
        $candidates = [];

        while ($segments !== []) {
            $candidates[] = '/'.implode('/', $segments);
            array_pop($segments);
        }

        $candidates[] = '/';

        return $candidates;
    }
}
