<?php

declare(strict_types=1);

namespace Apexl\Io\Factories;

use Apexl\Io\Requests\IoRequest;
use DI\Container;
use DI\DependencyException;
use DI\NotFoundException;
use Psr\Http\Message\ServerRequestInterface;
use ReflectionClass;
use ReflectionException;

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

    /**
     * @param class-string<IoRequest> $requestClass
     * @throws ReflectionException
     * @throws DependencyException
     * @throws NotFoundException
     */
    public function fromRequest(string $requestClass, ServerRequestInterface $request): IoRequest
    {
        \Apexl\Io\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 ($reflected->getProperties() as $property) {
            if (in_array($property->getName(), [
                'method',
                'uri',
                'headers',
                'cookies',
                'serverParams',
                'body',
                'uploadedFiles',
            ])) {
                continue;
            }

            $value = $property->getValue($request);
            $property->setValue($ioRequest, is_object($value) ? clone $value : $value);
        }

        return $ioRequest;
    }
}