<?php

namespace MintyPHP\Http;

use RuntimeException;

/**
 * Loads and validates core route declarations from config/routes.php.
 */
final class RouteCatalog
{
    /** @var list<array{path: string, target: string, public: bool}> */
    private array $coreRoutes;

    /** @var list<string> */
    private array $corePublicPaths;

    /**
     * @param list<array{path: string, target: string, public: bool}> $coreRoutes
     * @param list<string> $corePublicPaths
     */
    private function __construct(array $coreRoutes, array $corePublicPaths)
    {
        $this->coreRoutes = $coreRoutes;
        $this->corePublicPaths = $corePublicPaths;
    }

    public static function fromConfigFile(string $routesFile): self
    {
        if (!is_file($routesFile)) {
            throw new RuntimeException("Core route config not found: {$routesFile}");
        }

        $loaded = include $routesFile;
        if (!is_array($loaded)) {
            throw new RuntimeException("Core route config '{$routesFile}' must return an array.");
        }

        return self::fromArray($loaded, $routesFile);
    }

    /**
     * @param array<mixed> $routes
     */
    public static function fromArray(array $routes, string $source = 'config/routes.php'): self
    {
        $normalized = [];
        $publicPaths = [];
        $seenPaths = [];

        foreach (array_values($routes) as $index => $route) {
            if (!is_array($route)) {
                throw new RuntimeException("Invalid route definition at {$source}[{$index}]: expected array.");
            }

            $path = trim((string) ($route['path'] ?? ''));
            $target = trim((string) ($route['target'] ?? ''));
            if ($path === '' || $target === '') {
                throw new RuntimeException("Invalid route definition at {$source}[{$index}]: non-empty path and target are required.");
            }

            if (isset($seenPaths[$path])) {
                throw new RuntimeException("Core route path conflict: '{$path}' is defined multiple times in {$source}.");
            }
            $seenPaths[$path] = true;

            $isPublic = (bool) ($route['public'] ?? false);
            $normalized[] = [
                'path' => $path,
                'target' => $target,
                'public' => $isPublic,
            ];

            if ($isPublic) {
                $publicPaths[] = $path;
                $publicPaths[] = $target;
            }
        }

        $publicPaths = array_values(array_unique($publicPaths));
        sort($publicPaths);

        return new self($normalized, $publicPaths);
    }

    /**
     * @return list<array{path: string, target: string, public: bool}>
     */
    public function coreRoutes(): array
    {
        return $this->coreRoutes;
    }

    /**
     * @return list<string>
     */
    public function corePublicPaths(): array
    {
        return $this->corePublicPaths;
    }
}
