<?php

declare(strict_types=1);

namespace App\Framework\Router;

use App\Framework\DI\DefaultContainer;
use App\Framework\Http\ControllerRequest;
use App\Framework\Http\Request;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Router\Exceptions\ParameterResolutionException;
use ReflectionClass;
use ReflectionException;

final readonly class ParameterProcessor
{
    public function __construct(
        private DefaultContainer         $container,
        private ControllerRequestFactory $controllerRequestFactory,
        private DefaultLogger            $logger
    ) {}

    public function prepareParameters(array $params, array $queryParams): array
    {
        $parameters = [];

        foreach ($params as $param) {
            try {
                if(in_array($param['name'], array_keys($queryParams))) {
                    $parameters[] = $queryParams[$param['name']];
                    continue;
                }

                if ($param['isBuiltin'] === true) {
                    $parameters[] = $param['default'] ?? null;
                    continue;
                }

                $reflectionClass = new ReflectionClass($param['type']);

                if ($reflectionClass->implementsInterface(ControllerRequest::class)) {
                    $request = $this->container->get(Request::class);
                    $parsedBody = $request->parsedBody;

                    $controllerRequest = $this->controllerRequestFactory
                        ->createAndValidate($reflectionClass, $parsedBody);

                    $this->container->bind($param['type'], $controllerRequest);
                    $parameters[] = $controllerRequest;
                    continue;
                }

                $parameters[] = $this->container->get($param['type']);

            } catch (ReflectionException $e) {
                $this->logger?->error("Parameter reflection failed", [
                    'type' => $param['type'],
                    'error' => $e->getMessage()
                ]);
                throw new ParameterResolutionException(
                    "Cannot resolve parameter of type '{$param['type']}'",
                    0,
                    $e
                );
            } catch (\Throwable $e) {
                $this->logger?->error("Parameter preparation failed", [
                    'param' => $param,
                    'error' => $e->getMessage()
                ]);
                throw $e;
            }
        }

        return $parameters;
    }
}
