<?php

declare(strict_types=1);

namespace apexl\Io\factories;

use apexl\Io\exceptions\ValidationException;
use apexl\Io\Request\IoRequest;
use DI\Container;
use DI\DependencyException;
use DI\NotFoundException;
use Psr\Http\Message\ServerRequestInterface;
use ReflectionClass;
use ReflectionException;
use function apexl\Io\assertInstanceOf;

final readonly class IoRequestFactory
{
    public function __construct(
        private Container $container,
    ) {}

    /**
     * @param class-string<IoRequest> $requestClass
     * @throws ValidationException
     * @throws NotFoundException
     * @throws DependencyException
     * @throws ReflectionException
     */
    public function fromRequest(string $requestClass, ServerRequestInterface $request): IoRequest
    {
        assertInstanceOf($requestClass, IoRequest::class);

        $reflected = new ReflectionClass($request);

        /** @var IoRequest $ioRequest */
        $ioRequest = $this->container->make($requestClass, [
            'method' => $reflected->getProperty('method')->getValue($request),
            'uri' => clone $reflected->getProperty('uri')->getValue($request),
            'headers' => clone $reflected->getProperty('headers')->getValue($request),
            'cookies' => $reflected->getProperty('cookies')->getValue($request),
            'serverParams' => $reflected->getProperty('serverParams')->getValue($request),
            'body' => clone $reflected->getProperty('body')->getValue($request),
            'uploadedFiles' => $reflected->getProperty('uploadedFiles')->getValue($request),
        ]);

        foreach (['attributes', 'parsedBody', 'requestTarget', 'queryParams'] as $property) {
            $reflectedProperty = $reflected->getProperty($property);
            $reflectedProperty->setValue($ioRequest, $reflectedProperty->getValue($request));
        }

        return $ioRequest;
    }
}