<?php

namespace NoahVet\Reef\Test\A_Unit\Domain\Http\Multipart;

use NoahVet\Reef\Domain\Http\Multipart\HeaderParser;
use PHPUnit\Framework\TestCase;

class HeaderParserTest extends TestCase
{
    private HeaderParser $parser;

    protected function setUp(): void
    {
        $this->parser = new HeaderParser();
    }

    public function testSimpleHeaders(): void
    {
        $stream = $this->streamFromString("Content-Type: text/plain\r\nAccept: */*\r\n\r\nbody");
        $result = $this->parser->parse($stream);

        self::assertEquals('text/plain', $result['headers']['content-type']);
        self::assertEquals('*/*', $result['headers']['accept']);
    }

    public function testCaseInsensitiveKeys(): void
    {
        $stream = $this->streamFromString("CONTENT-TYPE: text/plain\r\n\r\n");
        $result = $this->parser->parse($stream);

        self::assertArrayHasKey('content-type', $result['headers']);
    }

    public function testBodyOffsetIsCorrect(): void
    {
        $stream = $this->streamFromString("Content-Type: text/plain\r\n\r\nbody content");
        $result = $this->parser->parse($stream);

        $body = \stream_get_contents($stream, -1, $result['bodyOffset']);
        self::assertEquals('body content', $body);
    }

    public function testMultiLineHeaderWithSpace(): void
    {
        $stream = $this->streamFromString("X-Custom: line one\r\n line two\r\n\r\n");
        $result = $this->parser->parse($stream);

        self::assertEquals('line one line two', $result['headers']['x-custom']);
    }

    public function testMultiLineHeaderWithTab(): void
    {
        $stream = $this->streamFromString("X-Custom: line one\r\n\tline two\r\n\r\n");
        $result = $this->parser->parse($stream);

        self::assertEquals('line one line two', $result['headers']['x-custom']);
    }

    public function testDuplicateHeaderBecomesArray(): void
    {
        $stream = $this->streamFromString("X-Foo: first\r\nX-Foo: second\r\n\r\n");
        $result = $this->parser->parse($stream);

        self::assertIsArray($result['headers']['x-foo']);
        self::assertEquals(['first', 'second'], $result['headers']['x-foo']);
    }

    public function testInvalidContentThrowsException(): void
    {
        $stream = $this->streamFromString('No empty line here');

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Content is not valid');
        $this->parser->parse($stream);
    }

    public function testTooLongHeaderThrowsException(): void
    {
        $stream = $this->streamFromString('X-Long: '.\str_repeat('a', 8193)."\r\n\r\n");

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Malformed header: header value is too long');
        $this->parser->parse($stream);
    }

    public function testMimeEncodedHeaderDecoded(): void
    {
        $stream = $this->streamFromString("Subject: =?UTF-8?B?dGjDqSBzdWJqZWN0?=\r\n\r\n");
        $result = $this->parser->parse($stream);

        self::assertEquals('thé subject', $result['headers']['subject']);
    }

    private function streamFromString(string $content): mixed
    {
        $stream = \fopen('php://temp', 'rw');
        \fwrite($stream, $content);

        return $stream;
    }
}
