<?php
// components/Router.php
namespace Components;

class Router
{
    /**
     * Routes registry
     * Structure: [routeName => ['path' => string, 'options' => array]]
     */
    private static array $routes = [];

    private static string $baseUrl = '';

    /** Common URL parameters to sync across all routes (e.g., ['tab']) */
    private static array $urlSyncCommon = [];

    // Defaults for route parameter names
    private const DEFAULT_TAB_PARAM = 'nav';      // main navigation only
    private const DEFAULT_SUBTAB_PARAM = 'subtab';      // main navigation only
    private const DEFAULT_PAGE_PARAM = 'action';    // partial/page loads

    // Optional: set a base URL if absolute URLs are required
    public static function setBaseUrl(string $baseUrl): void
    {
        self::$baseUrl = rtrim($baseUrl, '/');
    }

    /** Get the configured main navigation parameter name (defaults to 'tab'). */
    public static function subtabParam(): string
    {
        // Allow override via global constant
        if (defined('MB_PARAM_SUBTAB')) {
            $v = (string)constant('MB_PARAM_SUBTAB');
            if ($v !== '') return $v;
        }
        return self::DEFAULT_SUBTAB_PARAM;
    }    /** Get the configured main navigation parameter name (defaults to 'tab'). */
    public static function tabParam(): string
    {
        // Allow override via global constant
        if (defined('MB_PARAM_TAB')) {
            $v = (string)constant('MB_PARAM_TAB');
            if ($v !== '') return $v;
        }
        return self::DEFAULT_TAB_PARAM;
    }

    /** Get the configured partial/page load parameter name (defaults to 'page'). */
    public static function pageParam(): string
    {
        // Allow override via global constant
        if (defined('MB_PARAM_PAGE')) {
            $v = (string)constant('MB_PARAM_PAGE');
            if ($v !== '') return $v;
        }
        return self::DEFAULT_PAGE_PARAM;
    }

    /**
     * Register a named route
     *
     * @param string $name  Logical route name
     * @param string $path  Relative path to file (e.g., 'pages/dashboard.php')
     * @param array  $options Optional options, supported keys:
     *   - 'urlSyncParams' => array of query parameter names that should be synced to keep the browser URL in sync with the iframe URL (preferred)
     *   - 'urlParams' => same as above (deprecated; kept for backwards compatibility)
     *   - 'capability' => capability definition checked via Components\Capabilities::isSupported()
     *   - 'capabilities' => list of capability definitions, all must pass
     *   - 'requires' => alias for 'capability'
     *   - 'fallbackPath' => route file path to use when capability check fails
     *   - 'unsupportedPath' => alias for 'fallbackPath'
     */
    public static function add(string $name, string $path, array $options = []): void
    {
        self::$routes[$name] = [
            'path' => ltrim($path, '/'),
            'options' => $options,
        ];
    }

    // Generate a URL by route name with optional query params
    // Refactored: all routes go through the configured entry point (BEEZUI_ENTRYPOINT)
    public static function url(string $name, array $query = [], bool $absolute = false): string
    {
        // Choose the correct route parameter based on partial intent
        $routeParam = (isset($query['partial']) && (string)$query['partial'] === '1')
            ? self::pageParam()
            : self::tabParam();

        // Base query with <routeParam>=<route>
        $params = array_merge([$routeParam => $name], $query);

        // Determine entrypoint and merge any predefined query parameters from it
        $entry = defined('BEEZUI_ENTRYPOINT') ? (string)constant('BEEZUI_ENTRYPOINT') : 'app.php';
        $entryPath = $entry;
        $entryQsParams = [];
        if (strpos($entry, '?') !== false) {
            [$entryPath, $entryQs] = explode('?', $entry, 2);
            if ($entryQs !== '') {
                parse_str($entryQs, $entryQsParams);
                if (!is_array($entryQsParams)) {
                    $entryQsParams = [];
                }
            }
        }
        // Merge entrypoint query params first so explicit $query overrides them
        if ($entryQsParams) {
            $params = array_merge($entryQsParams, $params);
        }

        // Optionally enforce a required entry parameter (e.g., 'main') if defined
        if (defined('BEEZUI_ENTRYPOINT_REQUIRED_PARAM')) {
            $req = (string)constant('BEEZUI_ENTRYPOINT_REQUIRED_PARAM');
            if ($req !== '' && !array_key_exists($req, $params)) {
                $params[$req] = 1;
            }
        }

        $q = $params ? ('?' . http_build_query($params)) : '';
        $base = ($absolute && self::$baseUrl) ? self::$baseUrl . '/' : '';
        return $base . $entryPath . $q;
    }

    // Reverse lookup by path (useful for active navigation)
    public static function nameForPath(string $path): ?string
    {
        $path = ltrim($path, '/');
        foreach (self::$routes as $name => $def) {
            $p = is_array($def) ? ($def['path'] ?? '') : (string)$def;
            if ($p === $path) return $name;
        }
        return null;
    }

    /**
     * Resolve a route name to its registered relative file path, or null if not found.
     */
    public static function path(string $name): ?string
    {
        $def = self::$routes[$name] ?? null;
        if (!$def) return null;

        if (is_array($def)) {
            $primaryPath = isset($def['path']) ? (string)$def['path'] : null;
            if ($primaryPath === null || $primaryPath === '') {
                return null;
            }

            $options = isset($def['options']) && is_array($def['options']) ? $def['options'] : [];
            if (!self::isRouteSupported($options)) {
                $fallbackPath = self::unsupportedPath($options);
                if ($fallbackPath !== null) {
                    return $fallbackPath;
                }
            }

            return $primaryPath;
        }

        return is_string($def) ? $def : null;
    }

    /**
     * @param array<string,mixed> $options
     */
    private static function isRouteSupported(array $options): bool
    {
        if (array_key_exists('capabilities', $options)) {
            return Capabilities::isSupported(['all' => $options['capabilities']]);
        }
        if (array_key_exists('capability', $options)) {
            return Capabilities::isSupported($options['capability']);
        }
        if (array_key_exists('requires', $options)) {
            return Capabilities::isSupported($options['requires']);
        }
        return true;
    }

    /**
     * @param array<string,mixed> $options
     */
    private static function unsupportedPath(array $options): ?string
    {
        $value = $options['fallbackPath'] ?? $options['unsupportedPath'] ?? null;
        if (!is_string($value) || $value === '') {
            return null;
        }
        return ltrim($value, '/');
    }

    /**
     * Set common URL parameters that are always synced across routes
     */
    public static function setUrlSyncCommon(array $common): void
    {
        // normalize to unique, string-only values
        self::$urlSyncCommon = array_values(array_unique(array_map('strval', $common)));
    }

    /**
     * Get current URL sync configuration combining common and per-route parameters
     * Returns: ['common' => [...], 'routeParams' => ['routeName' => [...], ...]]
     */
    public static function getUrlSyncConfig(): array
    {
        $routeParams = [];
        foreach (self::$routes as $name => $def) {
            $opts = is_array($def) ? ($def['options'] ?? []) : [];
            $params = [];
            $params = isset($opts['urlSyncParams']) && is_array($opts['urlSyncParams']) ? array_values(array_unique(array_map('strval', $opts['urlSyncParams']))) : [];

            if ($params) {
                $routeParams[$name] = $params;
            }
        }
        return [
            'common' => self::$urlSyncCommon,
            'routeParams' => $routeParams,
        ];
    }
}
