<?php

declare(strict_types=1);

namespace FleekDash\Admin\Menu;

/**
 * Resolves menu slugs to route data (URL, target_path, target_hash, etc.)
 *
 * Extracted from MenuItemFormatter to reduce method count and complexity.
 *
 * @package FleekDash\Admin\Menu
 */
class MenuRouteResolver
{
    /** Option name for new tab URLs configuration */
    public const NEW_TAB_URLS_OPTION = '_fleekdash_menu_new_tab_urls';

    private const FLEEKDASH_ADMIN_PAGE = 'admin.php?page=fleekdash';

    /** Default URLs that should open in new tab */
    private $defaultNewTabUrls = [
        'customize.php',
        'site-editor.php',
        'admin.php?page=elementor-connect-account',
    ];

    private RouteConverter $routeConverter;

    public function __construct(RouteConverter $routeConverter)
    {
        $this->routeConverter = $routeConverter;
    }

    /**
     * Resolve route data for a menu slug
     *
     * @return array{target_path: string, target_hash: string, url: string, is_wp_native: bool, should_open_new_tab: bool, route_info: array}
     */
    public function resolveRouteData(string $rawSlug, string $decodedSlug, string $adminBaseUrl, string $adminBasePath): array
    {
        $voxel = $this->routeConverter->handleVoxelThemeRoute($rawSlug);
        $isFleekdashReact = strpos($rawSlug, 'fleekdash-react-') === 0;
        $isFleekdash = !$isFleekdashReact && strpos($rawSlug, 'fleekdash') === 0;

        $result = null;
        if ($voxel) {
            $result = $this->buildRouteDataFromVoxel($voxel);
        } elseif ($isFleekdashReact) {
            $result = $this->buildRouteDataFromFleekdashReact($rawSlug);
        } elseif ($isFleekdash) {
            $result = $this->buildRouteDataFromFleekdash($rawSlug);
        } else {
            $result = $this->buildRouteDataFromWordPress($decodedSlug, $adminBaseUrl, $adminBasePath);
        }

        return $result;
    }

    /** @param array<string, mixed> $voxel */
    private function buildRouteDataFromVoxel(array $voxel): array
    {
        return [
            'target_path' => $voxel['target_path'],
            'target_hash' => $voxel['target_hash'],
            'url' => $voxel['url'],
            'is_wp_native' => $voxel['is_wp_native'],
            'should_open_new_tab' => $voxel['should_open_new_tab'],
            'route_info' => [],
        ];
    }

    private function buildRouteDataFromFleekdash(string $rawSlug): array
    {
        [$targetPath, $url] = $this->processFleekDashRoute($rawSlug);
        return [
            'target_path' => $targetPath,
            'target_hash' => '',
            'url' => $url,
            'is_wp_native' => false,
            'should_open_new_tab' => false,
            'route_info' => [],
        ];
    }

    private function buildRouteDataFromFleekdashReact(string $rawSlug): array
    {
        $targetPath = $this->routeConverter->convertToCleanRoute($rawSlug);
        return [
            'target_path' => $targetPath,
            'target_hash' => '',
            'url' => \admin_url(self::FLEEKDASH_ADMIN_PAGE) . '#/' . $targetPath,
            'is_wp_native' => false,
            'should_open_new_tab' => false,
            'route_info' => [],
        ];
    }

    private function buildRouteDataFromWordPress(string $decodedSlug, string $adminBaseUrl, string $adminBasePath): array
    {
        [$targetPath, $targetHash] = $this->processWordPressRoute($decodedSlug, $adminBaseUrl, $adminBasePath);
        $routeInfo = $this->convertWordPressRoute($targetPath, $targetHash, $adminBaseUrl);
        return [
            'target_path' => $targetPath,
            'target_hash' => $targetHash,
            'url' => $routeInfo['url'],
            'is_wp_native' => $routeInfo['is_wp_native'],
            'should_open_new_tab' => ($routeInfo['target'] ?? '_self') === '_blank',
            'route_info' => $routeInfo,
        ];
    }

    private function processFleekDashRoute(string $raw_slug): array
    {
        $target_path = $raw_slug === 'fleekdash' ? 'dashboard' : str_replace('fleekdash-', '', $raw_slug);
        $url = \admin_url(self::FLEEKDASH_ADMIN_PAGE) . '#/' . $target_path;
        return [$target_path, $url];
    }

    private function processWordPressRoute(string $decoded_slug, string $admin_base_url, string $admin_base_path): array
    {
        $is_absolute_admin_url = strpos($decoded_slug, $admin_base_url) === 0;

        if ($is_absolute_admin_url) {
            $parsed_url = parse_url($decoded_slug);
            $path_from_root = ltrim($parsed_url['path'] ?? '', '/');
            $target_path = (strpos($path_from_root, ltrim($admin_base_path, '/')) === 0)
                ? substr($path_from_root, strlen(ltrim($admin_base_path, '/')))
                : $path_from_root;
            if (!empty($parsed_url['query'])) {
                $target_path .= '?' . $parsed_url['query'];
            }
            $target_hash = $parsed_url['fragment'] ?? '';
        } else {
            $slug_parts = explode('#', $decoded_slug, 2);
            $relative_slug_main = $slug_parts[0];
            $target_hash = $slug_parts[1] ?? '';
            // WordPress $menu[2] / $submenu[][2] slugs:
            // - Bare capability pages: "woocommerce" → admin.php?page=woocommerce
            // - Core scripts in wp-admin root: "plugins.php", "edit.php?post_type=x" → load as that path
            // - Plugin-style slugs: "mailerpress/campaigns.php" → WordPress uses admin.php?page=...,
            //   not wp-admin/mailerpress/campaigns.php (file is not under wp-admin).
            $target_path = $this->mapWordPressMenuSlugToTargetPath($relative_slug_main);
        }

        return [ltrim($target_path, '/'), $target_hash];
    }

    /**
     * Map a relative admin menu slug to the iframe target path (under wp-admin/).
     *
     * WordPress encodes slashes in the "page" query value as %2F (e.g. admin.php?page=plugin%2Ffile.php).
     * We rawurldecode before branching, then rawurlencode only the page slug so output matches core.
     * Extra query after the first "?" or "&" on the slug is appended with "&" on admin.php.
     */
    private function mapWordPressMenuSlugToTargetPath(string $relativeSlugMain): string
    {
        $normalized = rawurldecode($relativeSlugMain);
        [$basePart, $querySuffix] = $this->splitDecodedMenuSlugBaseAndQuerySuffix($normalized);

        $useAdminPage = !str_starts_with($basePart, 'admin.php')
            && (
                strpos($basePart, '.php') === false
                || strpos($basePart, '/') !== false
            );

        if ($useAdminPage) {
            $path = 'admin.php?page=' . rawurlencode($basePart);

            return $querySuffix !== '' ? $path . '&' . $querySuffix : $path;
        }

        return $querySuffix !== '' ? $basePart . '?' . $querySuffix : $basePart;
    }

    /**
     * Split menu slug string into hook file/slug vs extra query.
     * Plugins sometimes pass "plugin/file.php&path=/x" (ampersand, no "?"); if that whole string
     * is rawurlencoded into page=, WordPress treats it as one slug and returns 403.
     *
     * @return array{0: string, 1: string} Base slug / script, then query string (no leading ? or &).
     */
    private function splitDecodedMenuSlugBaseAndQuerySuffix(string $normalized): array
    {
        $questionPos = strpos($normalized, '?');
        if ($questionPos !== false) {
            return [
                substr($normalized, 0, $questionPos),
                substr($normalized, $questionPos + 1),
            ];
        }
        $ampersandPos = strpos($normalized, '&');
        if ($ampersandPos !== false) {
            return [
                substr($normalized, 0, $ampersandPos),
                substr($normalized, $ampersandPos + 1),
            ];
        }

        return [$normalized, ''];
    }

    private function convertWordPressRoute(string $target_path, string $target_hash, string $admin_base_url): array
    {
        $route = $this->routeConverter->convertWordPressRoute($target_path, $target_hash);

        if (($route['route_type'] ?? '') !== 'wordpress_iframe') {
            return $route;
        }

        if ($this->shouldOpenInNewTab($target_path)) {
            return $this->buildExternalTabRoute($target_path, $target_hash, $admin_base_url);
        }

        return $route;
    }

    private function shouldOpenInNewTab(string $targetPath): bool
    {
        if (strpos($targetPath, 'redirect_to=') !== false || strpos($targetPath, 'redirect=') !== false) {
            return true;
        }

        return $this->matchesNewTabUrlList($targetPath);
    }

    private function matchesNewTabUrlList(string $targetPath): bool
    {
        foreach ($this->getNewTabUrls() as $newTabUrl) {
            $matches = (strpos($newTabUrl, '=') !== false && strpos($targetPath, $newTabUrl) !== false)
                || strpos($targetPath, $newTabUrl) === 0;
            if ($matches) {
                return true;
            }
        }

        return false;
    }

    private function buildExternalTabRoute(string $targetPath, string $targetHash, string $adminBaseUrl): array
    {
        if (empty($adminBaseUrl)) {
            $adminBaseUrl = \admin_url();
        }
        $directUrl = $adminBaseUrl . $targetPath . ($targetHash !== '' ? '#' . $targetHash : '');
        $queryArgs = ['fleekdash-frame' => '1'];
        if ($this->matchesNewTabUrlList($targetPath)) {
            $queryArgs['protected'] = 'true';
        }

        return [
            'url' => add_query_arg($queryArgs, $directUrl),
            'route_type' => 'external_tab',
            'is_wp_native' => true,
            'target' => '_blank',
        ];
    }

    public function resolveRouteType(array $routeInfo, bool $isWpNative): string
    {
        if (isset($routeInfo['route_type'])) {
            return $routeInfo['route_type'];
        }
        return $isWpNative ? 'wordpress_iframe' : 'react';
    }

    public function constructHref(string $target_path, string $target_hash): string
    {
        $href_base = $target_path;

        if ($target_hash !== '' && $target_hash !== '0') {
            if (strpos($target_hash, '=') !== false && strpos($target_hash, '#') === false) {
                $href_base .= (strpos($href_base, '?') !== false ? '&' : '?') . $target_hash;
            } else {
                $href_base .= '#' . $target_hash;
            }
        }

        return $href_base;
    }

    private function getNewTabUrls(): array
    {
        $configured = \get_option(self::NEW_TAB_URLS_OPTION, null);

        $urls = ($configured !== null && is_array($configured))
            ? array_filter(array_map('trim', $configured))
            : $this->defaultNewTabUrls;

        return (array) \apply_filters('fleekdash_menu_new_tab_urls', $urls);
    }

    /** Default new tab URLs (for API exposure) */
    public function getDefaultNewTabUrls(): array
    {
        return $this->defaultNewTabUrls;
    }
}
