<?php

declare(strict_types=1);

namespace Apexl\Io\Validation\Middleware;

use Apexl\Io\Validation\Factories\ValidatorFactory;
use Apexl\Io\Validation\Interfaces\HasValidationRulesInterface;
use Apexl\Io\Validation\Interfaces\ValidatesDataInterface;
use Apexl\Io\Validation\Services\ValidationRuleCollector;
use DI\Container;
use JsonException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Psr7\Factory\ResponseFactory;

final readonly class ValidationMiddleware implements MiddlewareInterface
{
    public function __construct(
        private Container $container,
        private ValidatorFactory $validatorFactory,
        private ValidationRuleCollector $validationRuleCollector
    ) {}

    /**
     * @throws JsonException
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        if ($request instanceof ValidatesDataInterface) {
            $rules = $this->rules($request);
            if (!$rules) {
                return $handler->handle($request);
            }
            
            $data = $request->dataToValidate();
            $messages = $request->validationMessages();
            $lang = $request->validationLang();

            $validator = $this->validatorFactory->make($data, $rules, $messages, $lang);

            if (!$validator->isValid()) {
                $response = new ResponseFactory()->createResponse(422, 'Validation failed');
                $response->getBody()->write(json_encode($validator->getErrors(), JSON_THROW_ON_ERROR));

                return $response;
            }
        }

        return $handler->handle($request);
    }

    private function rules(ValidatesDataInterface|ServerRequestInterface $request): array
    {
        $rules = $request->validationRules();

        if (is_array($rules)) {
            return $rules;
        }

        if (is_string($rules)) {
            \Apexl\Io\assertInstanceOf($rules, HasValidationRulesInterface::class);

            $rules = $this->container->get($rules);
        }


        return $this->validationRuleCollector->collect($rules)->forValidator();
    }
}
