<?php

declare(strict_types=1);

use App\Framework\Http\Headers;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\HttpResponse;
use App\Framework\Http\Method;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\Middlewares\RequestIdMiddleware;
use App\Framework\Http\Next;
use App\Framework\Http\RequestIdGenerator;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\ResponseManipulator;
use App\Framework\Http\Status;

beforeEach(function () {
    $this->responseManipulator = new ResponseManipulator();

    // Create real request ID generator with test secret
    $this->requestIdGenerator = new RequestIdGenerator('test-secret-for-testing');

    $this->middleware = new RequestIdMiddleware(
        $this->requestIdGenerator,
        $this->responseManipulator
    );

    // Create test request
    $this->request = new HttpRequest(
        method: Method::GET,
        path: '/test'
    );

    $this->stateManager = new RequestStateManager(new WeakMap(), $this->request);
    $this->context = new MiddlewareContext($this->request);
});

it('adds request ID header to response', function () {
    // Create response
    $headers = new Headers([
        'Content-Type' => 'application/json',
    ]);

    $response = new HttpResponse(Status::OK, $headers, '{"test": true}');

    // Create next handler that returns context with response
    $next = new class ($response) implements Next {
        public function __construct(private HttpResponse $response)
        {
        }

        public function __invoke(MiddlewareContext $context): MiddlewareContext
        {
            return $context->withResponse($this->response);
        }
    };

    $result = $this->middleware->__invoke(
        $this->context,
        $next,
        $this->stateManager
    );

    expect($result->hasResponse())->toBeTrue();
    expect($result->response->headers->has('X-Request-ID'))->toBeTrue();
    expect($result->response->headers->getFirst('X-Request-ID'))->not->toBeEmpty();
    expect($result->response->headers->has('Content-Type'))->toBeTrue();
    expect($result->response->headers->getFirst('Content-Type'))->toBe('application/json');
});

it('preserves existing headers when adding request ID', function () {
    // Create response with multiple headers
    $headers = new Headers([
        'Content-Type' => 'text/html',
        'Cache-Control' => 'no-cache',
        'Server' => 'test-server',
    ]);

    $response = new HttpResponse(Status::OK, $headers, '<html>test</html>');

    $next = new class ($response) implements Next {
        public function __construct(private HttpResponse $response)
        {
        }

        public function __invoke(MiddlewareContext $context): MiddlewareContext
        {
            return $context->withResponse($this->response);
        }
    };

    $result = $this->middleware->__invoke(
        $this->context,
        $next,
        $this->stateManager
    );

    expect($result->hasResponse())->toBeTrue();
    expect($result->response->headers->has('X-Request-ID'))->toBeTrue();
    expect($result->response->headers->getFirst('X-Request-ID'))->not->toBeEmpty();
    expect($result->response->headers->has('Content-Type'))->toBeTrue();
    expect($result->response->headers->getFirst('Content-Type'))->toBe('text/html');
    expect($result->response->headers->has('Cache-Control'))->toBeTrue();
    expect($result->response->headers->getFirst('Cache-Control'))->toBe('no-cache');
    expect($result->response->headers->has('Server'))->toBeTrue();
    expect($result->response->headers->getFirst('Server'))->toBe('test-server');
});

it('passes through context when no response present', function () {
    // Next handler that doesn't set a response
    $next = new class () implements Next {
        public function __invoke(MiddlewareContext $context): MiddlewareContext
        {
            return $context;
        }
    };

    $result = $this->middleware->__invoke(
        $this->context,
        $next,
        $this->stateManager
    );

    expect($result->hasResponse())->toBeFalse();
});

it('preserves response body and status', function () {
    $headers = new Headers([
        'Content-Type' => 'application/json',
    ]);

    $response = new HttpResponse(Status::CREATED, $headers, '{"created": true}');

    $next = new class ($response) implements Next {
        public function __construct(private HttpResponse $response)
        {
        }

        public function __invoke(MiddlewareContext $context): MiddlewareContext
        {
            return $context->withResponse($this->response);
        }
    };

    $result = $this->middleware->__invoke(
        $this->context,
        $next,
        $this->stateManager
    );

    expect($result->hasResponse())->toBeTrue();
    expect($result->response->status)->toBe(Status::CREATED);
    expect($result->response->body)->toBe('{"created": true}');
    expect($result->response->headers->has('X-Request-ID'))->toBeTrue();
    expect($result->response->headers->getFirst('X-Request-ID'))->not->toBeEmpty();
});

it('uses correct header name', function () {
    $headers = new Headers(['Content-Type' => 'text/plain']);
    $response = new HttpResponse(Status::OK, $headers, 'test');

    $next = new class ($response) implements Next {
        public function __construct(private HttpResponse $response)
        {
        }

        public function __invoke(MiddlewareContext $context): MiddlewareContext
        {
            return $context->withResponse($this->response);
        }
    };

    $result = $this->middleware->__invoke($this->context, $next, $this->stateManager);

    expect($result->response->headers->has('X-Request-ID'))->toBeTrue();
    expect(RequestIdGenerator::getHeaderName())->toBe('X-Request-ID');
});
