<?php
/**
 * Resolves admin bar node href when WordPress/plugins leave it empty (JS-driven items).
 *
 * Strategy (cheap → expensive):
 * 1. Keep non-empty real hrefs as-is
 * 2. Parse first href from title HTML (admin bar often stores HTML in title)
 * 3. Scan meta recursively for hrefs / absolute URLs
 * 4. Known node-id → admin path map (filterable)
 * 5. Match first sidebar menu URL by derived slug (uses AdminMenuContextBuilder)
 *
 * @package FleekDash\API
 * @since V2.2
 */

declare(strict_types=1);

namespace FleekDash\API;

use FleekDash\Admin\Menu\AdminMenuContextBuilder;

/**
 * Toolbar href resolution for REST toolbar JSON.
 */
final class ToolbarHrefResolver
{
    private const KNOWN_NODE_HREF_FALLBACKS = [
        'itsec_admin_bar_menu' => 'admin.php?page=itsec-dashboard',
    ];

    /**
     * @param array<string, mixed> $meta
     */
    public static function resolve(string $rawHref, string $nodeId, array $meta, string $titleHtml): string
    {
        $candidate = \trim($rawHref);
        $out = !self::isEffectivelyEmpty($candidate)
            ? $rawHref
            : self::resolveEmptyHref($rawHref, $nodeId, $meta, $titleHtml);

        return (string) \apply_filters('fleekdash_toolbar_resolved_href', $out, $nodeId, $meta, $titleHtml);
    }

    /**
     * @param array<string, mixed> $meta
     */
    private static function resolveEmptyHref(string $rawHref, string $nodeId, array $meta, string $titleHtml): string
    {
        $out = self::extractHrefFromHtml($titleHtml);
        if ($out === '') {
            $out = self::extractHrefFromMeta($meta);
        }
        if ($out === '') {
            $out = self::resolveKnownNodeId($nodeId);
        }
        if ($out === '') {
            $out = self::guessFirstPluginMenuUrl($nodeId);
        }
        if ($out === '') {
            $out = $rawHref;
        }

        return $out;
    }

    private static function isEffectivelyEmpty(string $href): bool
    {
        $empty = false;
        $t = \trim($href);
        if ($t === '' || $t === '#') {
            $empty = true;
        } else {
            $lower = \strtolower($t);
            if (\str_starts_with($lower, 'javascript:')) {
                $empty = true;
            }
        }

        return $empty;
    }

    private static function extractHrefFromHtml(string $html): string
    {
        $out = '';
        if ($html !== '') {
            $decoded = \html_entity_decode($html, \ENT_QUOTES | \ENT_HTML5, 'UTF-8');
            if (\preg_match('/href\s*=\s*["\']([^"\']+)["\']/i', $decoded, $m)) {
                $h = \trim((string) ($m[1] ?? ''));
                if ($h !== '' && !self::isEffectivelyEmpty($h)) {
                    $out = $h;
                }
            }
        }

        return $out;
    }

    /**
     * @param array<string, mixed> $meta
     */
    private static function extractHrefFromMeta(array $meta): string
    {
        $found = '';
        foreach ($meta as $value) {
            if (\is_string($value)) {
                $fromHtml = self::extractHrefFromHtml($value);
                if ($fromHtml !== '') {
                    $found = $fromHtml;
                    break;
                }
                if (\preg_match('#^\s*https?://#i', \trim($value))) {
                    $found = \trim($value);
                    break;
                }
            } elseif (\is_array($value)) {
                $nested = self::extractHrefFromMeta($value);
                if ($nested !== '') {
                    $found = $nested;
                    break;
                }
            }
        }

        return $found;
    }

    private static function resolveKnownNodeId(string $nodeId): string
    {
        $out = '';
        if ($nodeId !== '') {
            $map = \apply_filters(
                'fleekdash_toolbar_node_href_fallback_map',
                self::KNOWN_NODE_HREF_FALLBACKS,
            );
            if (!\is_array($map)) {
                $map = self::KNOWN_NODE_HREF_FALLBACKS;
            }
            if (isset($map[$nodeId]) && \is_string($map[$nodeId])) {
                $out = \trim($map[$nodeId]);
            }
        }

        return $out;
    }

    private static function guessFirstPluginMenuUrl(string $nodeId): string
    {
        $hints = self::collectMenuSlugHints($nodeId);
        if ($hints === []) {
            return '';
        }

        $builder = new AdminMenuContextBuilder();
        $builder->ensureLoaded();

        global $menu, $submenu;
        $urls = self::collectMenuUrls((array) $menu, (array) $submenu);
        $out = self::firstUrlMatchingPageParam($urls, $hints);
        if ($out === '') {
            $out = self::firstUrlContainingAnyHint($urls, $hints);
        }

        return $out;
    }

    /**
     * @param list<string> $urls
     * @param list<string> $hints
     */
    private static function firstUrlMatchingPageParam(array $urls, array $hints): string
    {
        $out = '';
        foreach ($hints as $hint) {
            $hintLower = \strtolower($hint);
            foreach ($urls as $url) {
                if ($url !== '' && \str_contains(\strtolower($url), 'page=' . $hintLower)) {
                    $out = $url;
                    break 2;
                }
            }
        }

        return $out;
    }

    /**
     * @param list<string> $urls
     * @param list<string> $hints
     */
    private static function firstUrlContainingAnyHint(array $urls, array $hints): string
    {
        $out = '';
        foreach ($hints as $hint) {
            $hintLower = \strtolower($hint);
            foreach ($urls as $url) {
                if ($url !== '' && \str_contains(\strtolower($url), $hintLower)) {
                    $out = $url;
                    break 2;
                }
            }
        }

        return $out;
    }

    /**
     * @return list<string>
     */
    private static function collectMenuSlugHints(string $nodeId): array
    {
        if ($nodeId === '') {
            return [];
        }
        $hints = self::collectRawSlugHints($nodeId);
        $filtered = \array_values(\array_unique(\array_filter(\array_map('strval', $hints))));
        $filtered = \apply_filters('fleekdash_toolbar_node_menu_slug_hints', $filtered, $nodeId);
        if (!\is_array($filtered)) {
            return [];
        }

        return self::normalizeStringList($filtered);
    }

    /**
     * @return list<string>
     */
    private static function collectRawSlugHints(string $nodeId): array
    {
        $hints = [];
        $stripped = \preg_replace('/_admin_bar.*$/i', '', $nodeId);
        if (\is_string($stripped) && $stripped !== '' && $stripped !== $nodeId) {
            $hints[] = $stripped;
        }
        $stripped2 = \preg_replace('/[-_]adminbar$/i', '', $nodeId);
        if (\is_string($stripped2) && $stripped2 !== '' && $stripped2 !== $nodeId) {
            $hints[] = $stripped2;
        }

        return $hints;
    }

    /**
     * @param array<int, mixed> $filtered
     * @return list<string>
     */
    private static function normalizeStringList(array $filtered): array
    {
        $out = [];
        foreach ($filtered as $h) {
            $s = \is_string($h) ? \trim($h) : '';
            if ($s !== '') {
                $out[] = $s;
            }
        }

        return $out;
    }

    /**
     * @param array<int|string, mixed> $menu
     * @param array<int|string, mixed> $submenu
     * @return list<string>
     */
    private static function collectMenuUrls(array $menu, array $submenu): array
    {
        $urls = [];
        foreach ($menu as $item) {
            self::pushUrlFromMenuRow($urls, $item);
        }
        foreach ($submenu as $items) {
            if (!\is_array($items)) {
                continue;
            }
            foreach ($items as $sub) {
                self::pushUrlFromMenuRow($urls, $sub);
            }
        }

        return $urls;
    }

    /**
     * @param list<string> $urls
     */
    private static function pushUrlFromMenuRow(array &$urls, mixed $item): void
    {
        if (!\is_array($item) || !isset($item[2])) {
            return;
        }
        $u = \trim((string) $item[2]);
        if ($u !== '') {
            $urls[] = $u;
        }
    }
}
