<?php
/**
 * RouteCollection Class
 *
 * @package Licenzo\Framework\Http\Router
 */

namespace Licenzo\Framework\Http\Router;

use Licenzo\Framework\Contracts\Router\Route;
use Licenzo\Framework\Http\Request;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Collection class for managing routes
 */
class RouteCollection
{
    /**
     * All registered routes
     *
     * @var array
     */
    protected array $routes = [];

    /**
     * Routes indexed by method
     *
     * @var array
     */
    protected array $routesByMethod = [];

    /**
     * Add a route to the collection
     *
     * Routes are automatically sorted by specificity to ensure
     * static routes are matched before dynamic routes.
     *
     * @param Route $route
     * @return void
     */
    public function add(Route $route): void
    {
        $this->routes[] = $route;

        // Index by method for faster lookup
        $method = $route->getMethod();

        if (!isset($this->routesByMethod[$method])) {
            $this->routesByMethod[$method] = [];
        }

        $this->routesByMethod[$method][] = $route;

        // Sort routes by specificity (the highest score first)
        // This ensures static routes are matched before dynamic routes

        usort($this->routesByMethod[$method], function($a, $b) {
            return $b->getSpecificity() <=> $a->getSpecificity();
        });
    }

    /**
     * Get all routes
     *
     * @return array
     */
    public function all(): array
    {
        return $this->routes;
    }

    /**
     * Get routes by HTTP method
     *
     * @param string $method
     * @return array
     */
    public function getByMethod(string $method): array
    {
        return $this->routesByMethod[strtoupper($method)] ?? [];
    }

    /**
     * Match a request to a route
     *
     * @param Request $request
     * @return Route|null
     */
    public function match(Request $request): ?Route
    {
        $method = $request->getMethod();
        $routes = $this->getByMethod($method);

        foreach ($routes as $route) {

            if ($route->matches($request)) {
                return $route;
            }
        }

        return null;
    }

    /**
     * Clear all routes
     *
     * @return void
     */
    public function clear(): void
    {
        $this->routes = [];
        $this->routesByMethod = [];
    }

    /**
     * Check if collection is empty
     *
     * @return bool
     */
    public function isEmpty(): bool
    {
        return empty($this->routes);
    }

    /**
     * Get routes by name pattern
     *
     * @param string $pattern
     * @return array
     */
    public function getByNamePattern($pattern): array
    {
        $matchingRoutes = [];

        foreach ($this->routes as $route) {
            $name = $route->getName();
            if ($name && fnmatch($pattern, $name)) {
                $matchingRoutes[] = $route;
            }
        }

        return $matchingRoutes;
    }

    /**
     * Get routes with specific middleware
     *
     * @param string $middleware
     * @return array
     */
    public function getByMiddleware($middleware): array
    {
        $matchingRoutes = [];

        foreach ($this->routes as $route) {
            if (in_array($middleware, $route->getMiddleware())) {
                $matchingRoutes[] = $route;
            }
        }

        return $matchingRoutes;
    }

    /**
     * Get routes with a specific policy
     *
     * @param string $policy
     * @return array
     */
    public function getByPolicy($policy): array
    {
        $matchingRoutes = [];

        foreach ($this->routes as $route) {
            if ($route->getPolicy() === $policy) {
                $matchingRoutes[] = $route;
            }
        }

        return $matchingRoutes;
    }
}
