<?php

declare(strict_types=1);

namespace App\Support\Verification;

use App\Support\Providers\ProviderReasonCodes;

final class VerificationLinkBehavior
{
    /**
     * @return array{
     *   label:string,
     *   url:string,
     *   kind:'external'|'internal-diagnostic'|'internal-inline-safe',
     *   opens_in_new_tab:bool,
     *   show_new_tab_hint:bool
     * }
     */
    public function describe(?string $label, ?string $url): array
    {
        $normalizedLabel = is_string($label) && trim($label) !== ''
            ? trim($label)
            : 'Open link';

        $normalizedUrl = is_string($url) && trim($url) !== ''
            ? trim($url)
            : '';

        $kind = $this->classify($normalizedUrl);
        $opensInNewTab = $kind !== 'internal-inline-safe' && $normalizedUrl !== '';

        return [
            'label' => $normalizedLabel,
            'url' => $normalizedUrl,
            'kind' => $kind,
            'opens_in_new_tab' => $opensInNewTab,
            'show_new_tab_hint' => $opensInNewTab,
        ];
    }

    /**
     * @param  array<string, mixed>  $check
     */
    public function shouldRouteThroughAssist(array $check, bool $assistVisible): bool
    {
        if (! $assistVisible) {
            return false;
        }

        $key = $check['key'] ?? null;
        $key = is_string($key) ? trim($key) : '';

        if ($key !== '' && str_starts_with($key, 'permissions.')) {
            return true;
        }

        $reasonCode = $check['reason_code'] ?? null;
        $reasonCode = is_string($reasonCode) ? trim($reasonCode) : '';

        return in_array($reasonCode, [
            ProviderReasonCodes::ProviderConsentMissing,
            ProviderReasonCodes::ProviderConsentFailed,
            ProviderReasonCodes::ProviderConsentRevoked,
            ProviderReasonCodes::ProviderPermissionMissing,
            ProviderReasonCodes::ProviderPermissionDenied,
            ProviderReasonCodes::ProviderPermissionRefreshFailed,
            ProviderReasonCodes::IntuneRbacPermissionMissing,
        ], true);
    }

    /**
     * @return 'external'|'internal-diagnostic'|'internal-inline-safe'
     */
    private function classify(string $url): string
    {
        if ($url === '') {
            return 'internal-inline-safe';
        }

        $path = $this->extractPath($url);

        if ($path !== null && $this->isInternalDiagnosticPath($path)) {
            return 'internal-diagnostic';
        }

        if ($this->isExternalUrl($url)) {
            return 'external';
        }

        return 'internal-inline-safe';
    }

    private function extractPath(string $url): ?string
    {
        $path = parse_url($url, PHP_URL_PATH);

        if (is_string($path) && $path !== '') {
            return '/'.ltrim($path, '/');
        }

        if (str_starts_with($url, '/')) {
            return '/'.ltrim($url, '/');
        }

        return null;
    }

    private function isExternalUrl(string $url): bool
    {
        $scheme = parse_url($url, PHP_URL_SCHEME);

        if (! is_string($scheme) || ! in_array(strtolower($scheme), ['http', 'https'], true)) {
            return false;
        }

        $host = parse_url($url, PHP_URL_HOST);

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

        $applicationHost = parse_url(url('/'), PHP_URL_HOST);
        $applicationPort = parse_url(url('/'), PHP_URL_PORT);
        $urlPort = parse_url($url, PHP_URL_PORT);

        if (! is_string($applicationHost) || $applicationHost === '') {
            return true;
        }

        if (strcasecmp($host, $applicationHost) !== 0) {
            return true;
        }

        if ($applicationPort === null || $urlPort === null) {
            return false;
        }

        return (int) $urlPort !== (int) $applicationPort;
    }

    private function isInternalDiagnosticPath(string $path): bool
    {
        return (bool) preg_match(
            '/^\/admin\/(?:workspaces\/[^\/]+\/environments\/[^\/]+\/required-permissions|provider-connections(?:\/create|\/[^\/]+(?:\/edit)?)?)$/',
            $path,
        );
    }
}
