<?php

declare(strict_types=1);

namespace App\Framework\HttpClient;

use App\Framework\HttpClient\Exception\RequestValidationException;

final readonly class RequestValidator
{
    public function validate(ClientRequest $request): void
    {
        $this->validateUrl($request->url);
        $this->validateMethod($request->method);
        $this->validateHeaders($request->headers);
        $this->validateBody($request->body, $request->method);
        $this->validateOptions($request->options);
    }

    private function validateUrl(string $url): void
    {
        if (empty($url)) {
            throw new RequestValidationException('URL cannot be empty');
        }

        if (! filter_var($url, FILTER_VALIDATE_URL)) {
            throw new RequestValidationException("Invalid URL format: $url");
        }

        $scheme = parse_url($url, PHP_URL_SCHEME);
        if (! in_array($scheme, ['http', 'https'])) {
            throw new RequestValidationException("Unsupported URL scheme: $scheme");
        }
    }

    private function validateMethod(\App\Framework\Http\Method $method): void
    {
        // Bereits durch Enum validiert, aber zusätzliche Checks möglich
        if (in_array($method, [\App\Framework\Http\Method::TRACE, \App\Framework\Http\Method::CONNECT])) {
            throw new RequestValidationException("Method {$method->value} is not supported");
        }
    }

    private function validateHeaders(\App\Framework\Http\Headers $headers): void
    {
        foreach ($headers->all() as $name => $value) {
            if (empty($name)) {
                throw new RequestValidationException('Header name cannot be empty');
            }

            if (preg_match('/[^\x21-\x7E\x80-\xFF]/', $name)) {
                throw new RequestValidationException("Invalid header name: $name");
            }
        }
    }

    private function validateBody(string|array $body, \App\Framework\Http\Method $method): void
    {
        // GET und HEAD sollten keinen Body haben
        if (in_array($method, [\App\Framework\Http\Method::GET, \App\Framework\Http\Method::HEAD])) {
            if (! empty($body)) {
                throw new RequestValidationException("Method {$method->value} should not have a body");
            }
        }
    }

    private function validateOptions(ClientOptions $options): void
    {
        if ($options->timeout <= 0) {
            throw new RequestValidationException('Timeout must be positive');
        }

        if ($options->connectTimeout <= 0) {
            throw new RequestValidationException('Connect timeout must be positive');
        }

        if ($options->maxRedirects < 0) {
            throw new RequestValidationException('Max redirects cannot be negative');
        }
    }
}
