<?php
namespace Components\Legacy;

class LegacyRouter
{
    private static array $routes = [];

    /**
     * Register a legacy route by name.
     *
     * @param string               $name   Key to reference this route later
     * @param string|callable      $base   Base URL or callable(): string that returns the base URL
     * @param array{defaults?:array, allowed?:array|null} $options Defaults + optional allowed param names
     */
    public static function add(string $name,  $base, array $options = []): void
    {

        self::$routes[$name] = [
            'base'     => $base,
            'defaults' => $options['defaults'] ?? [],
            'allowed'  => $options['allowed']  ?? null, // null = allow all
        ];
    }

    /**
     * Build a URL for a registered legacy route.
     *
     * Behaviour overview
     * -------------------
     * - The registered $base may already contain a query string, e.g.
     *     mh_cfg('MAILBEEZ_MAILHIVE_URL') . 'view&format=html&realview=true'
     * - Any query parameters embedded in the base URL are considered *fixed*
     *   and are always preserved.
     * - The optional allow-list only applies to dynamic parameters supplied
     *   via $options['defaults'] and the $params argument, not to the fixed
     *   parameters coming from the base URL.
     * - Merge precedence (later wins):
     *     base-query ? filtered-defaults ? filtered-params
     */
    public static function url(string $name, array $params = []): string
    {
        if (!isset(self::$routes[$name])) {
            throw new \InvalidArgumentException("Unknown legacy route: {$name}");
        }

        $def  = self::$routes[$name];
        $base = is_callable($def['base']) ? (string) ($def['base'])($params) : (string) $def['base'];

        // Extract query already present in base (if any)
        $baseQuery = [];
        if (false !== ($qpos = strpos($base, '?'))) {
            parse_str(substr($base, $qpos + 1), $baseQuery);
            $base = substr($base, 0, $qpos);
        }

        // Dynamic (override-able) part comes from defaults + params
        $dynamic = array_merge($def['defaults'], $params);

        // Optional allow-list – only applied to the dynamic part so that
        // fixed parameters from the base URL (e.g. format=html) are
        // *always* kept.
        if (is_array($def['allowed'])) {
            $dynamic = array_intersect_key($dynamic, array_flip($def['allowed']));
        }

        // Merge: base-query ? filtered-defaults ? filtered-params
        $query = array_merge($baseQuery, $dynamic);

        // Normalize to scalar strings, drop arrays/objects to avoid malformed URLs
        foreach ($query as $k => $v) {
            if (is_array($v) || is_object($v)) {
                unset($query[$k]);
            } else {
                $query[$k] = (string) $v;
            }
        }

        $qs = http_build_query($query);
        return $qs ? ($base . '?' . $qs) : $base;
    }
}