<?php

declare(strict_types=1);

namespace App\Framework\Core;

use App\Framework\Router\CompiledPattern;
use App\Framework\Router\CompiledRoutes;

final readonly class RouteCompiler implements AttributeCompiler
{
    private array $named;
    /**
     * @param array<int, array{method: string, path: string, controller: class-string, handler: string}> $routes
     * @return array<string, array{static: array<string, array>, dynamic: array<int, array{regex: string, params: array, handler: array}>}>
     */
    public function compile(array $routes): array
    {
        $compiled = [];
        $named = [];

        foreach ($routes as $route) {

            $method = is_string($route['http_method']) ? strtoupper($route['http_method']) : $route['http_method']->value;
            $path = $route['path'];
            $routeName = $route['name'] ?? '';

            $compiled[$method] ??= ['static' => [], 'dynamic' => []];

            if (! str_contains($path, '{')) {
                // Statische Route
                $staticRoute =  new StaticRoute(
                    $route['class'],
                    $route['method'],
                    $route['parameters'] ?? [],
                    $routeName,
                    $path,
                    $route['attributes']
                );

                $compiled[$method]['static'][$path] = $staticRoute;

                if($routeName) {
                    $named[$routeName] = $staticRoute;
                }

            } else {
                // Dynamische Route
                $paramNames = [];
                $regex = $this->convertPathToRegex($path, $paramNames);

                $dynamicRoute = new DynamicRoute(
                    $regex,
                    $paramNames,
                    $route['class'],
                    $route['method'],
                    $route['parameters'],
                    [],
                    $routeName,
                    $path,
                    $route['attributes']
                );

                $compiled[$method]['dynamic'][] = $dynamicRoute;

                if($routeName) {
                    $named[$routeName] = $dynamicRoute;
                }
            }
        }

        if(!isset($this->named)) {
            $this->named = $named;
        }

        return $compiled;
    }

    public function compileNamedRoutes(array $routes): array
    {
        return $this->named;
    }

    /**
     * Konvertiert zB. /user/{id}/edit → ~^/user/([^/]+)/edit$~ und gibt ['id'] als Parameternamen zurück.
     *
     * @param string $path
     * @param array<string> &$paramNames
     * @return string
     */
    private function convertPathToRegex(string $path, array &$paramNames): string
    {
        $paramNames = [];
        $regex = preg_replace_callback('#\{(\w+)(\*)?}#', function ($matches) use (&$paramNames) {
            $paramNames[] = $matches[1];

            // Wenn {id*} dann erlaube Slashes, aber mache es non-greedy
            if (isset($matches[2]) && $matches[2] === '*') {
                return '(.+?)'; // Non-greedy: matcht so wenig wie möglich
            }

            return '([^/]+)'; // Keine Slashes
        }, $path);

        return '~^' . $regex . '$~';
    }

    public function getAttributeClass(): string
    {
        return Route::class;
    }

    public function compileOptimized(array $routes): CompiledRoutes
    {
        $compiled = $this->compile($routes);
        $optimizedStatic = [];
        $optimizedDynamic = [];

        foreach($compiled as $method => $routes) {
            $optimizedStatic[$method] = $routes['static'];

            if(!empty($routes['dynamic'])) {
                $optimizedDynamic[$method] = $this->createCompiledPattern($routes['dynamic']);
            }
        }

        return new CompiledRoutes($optimizedStatic, $optimizedDynamic, $this->named);
    }

    private function createCompiledPattern(mixed $dynamicRoutes): CompiledPattern
    {
        $patterns = [];
        $routeData = [];
        $currentIndex = 1; // Nach Full Match (Index 0)

        foreach($dynamicRoutes as $index => $route) {
            $pattern = $this->stripAnchors($route->regex);
            $patterns[] = "({$pattern})";

            // Route-Gruppe Index merken
            $routeGroupIndex = $currentIndex++;

            // Parameter-Mapping KORREKT berechnen
            $paramMap = [];
            foreach($route->paramNames as $paramName) {
                $paramMap[$paramName] = $currentIndex++;
            }

            $routeData[$index] = [
                'route' => $route,
                'paramMap' => $paramMap,
                'routeGroupIndex' => $routeGroupIndex
            ];
        }

        $combinedRegex = '~^(?:' . implode('|', $patterns) . ')$~';
        return new CompiledPattern($combinedRegex, $routeData);
    }

    private function stripAnchors($regex): string
    {
        if (preg_match('/^~\^(.+)\$~$/', $regex, $matches)) {
            return $matches[1];
        }
        return $regex;
    }
}
