<?php

namespace NoahVet\Reef\Test\A_Unit\Infrastructure\Phpunit\Loader;

use NoahVet\Reef\Contract\Phpunit\JsonBody;
use NoahVet\Reef\Contract\Phpunit\MultipartBody;
use NoahVet\Reef\Infrastructure\Phpunit\Loader\OasExampleLoader;
use PHPUnit\Framework\TestCase;

class OasExampleLoaderTest extends TestCase
{
    private OasExampleLoader $loader;

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

    public function testLoadYamlHappyPath(): void
    {
        $file = $this->createTempFile('yaml', <<<YAML
            components:
              examples:
                MyExample:
                  name: test workspace
                  code: WS-001
            YAML);

        $result = $this->loader->load($file, 'MyExample');

        self::assertSame(['name' => 'test workspace', 'code' => 'WS-001'], $result);
    }

    public function testLoadYmlExtension(): void
    {
        $file = $this->createTempFile('yml', <<<YAML
            components:
              examples:
                MyExample:
                  id: 42
            YAML);

        $result = $this->loader->load($file, 'MyExample');

        self::assertSame(['id' => 42], $result);
    }

    public function testLoadJsonHappyPath(): void
    {
        $file = $this->createTempFile('json', \json_encode([
            'components' => [
                'examples' => [
                    'MyExample' => [
                        'name' => 'json workspace',
                    ],
                ],
            ],
        ], \JSON_THROW_ON_ERROR));

        $result = $this->loader->load($file, 'MyExample');

        self::assertSame(['name' => 'json workspace'], $result);
    }

    public function testThrowsRuntimeExceptionOnMissingComponent(): void
    {
        $file = $this->createTempFile('yaml', <<<YAML
            components:
              examples:
                OtherExample:
                  foo: bar
            YAML);

        $this->expectException(\RuntimeException::class);
        $this->expectExceptionMessageMatches('/MissingExample/');

        $this->loader->load($file, 'MissingExample');
    }

    public function testThrowsInvalidArgumentExceptionOnUnsupportedFormat(): void
    {
        $file = $this->createTempFile('txt', 'not a supported format');

        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessageMatches('/\.txt/');

        $this->loader->load($file, 'AnyComponent');
    }

    public function testLoadReturnsMultipartBodyWhenValueIsString(): void
    {
        $file = $this->createTempFile('yaml', <<<YAML
            components:
              examples:
                MyMultipart:
                  summary: A multipart example
                  value: |
                    --reef-boundary
                    Content-Type: application/json

                    {}
                    --reef-boundary--
            YAML);

        $result = $this->loader->load($file, 'MyMultipart');

        self::assertInstanceOf(MultipartBody::class, $result);
        self::assertSame('reef-boundary', $result->boundary);
        self::assertSame('related', $result->subtype);
        self::assertStringContainsString('--reef-boundary', $result->getRawBody());
    }

    public function testLoadReadsMultipartSubtypeFromXContentTypeFullMimeType(): void
    {
        $file = $this->createTempFile('yaml', <<<YAML
            components:
              examples:
                MyFormData:
                  summary: A form-data example
                  x-content-type: multipart/form-data
                  value: |
                    --reef-boundary
                    Content-Disposition: form-data; name="file"

                    binary
                    --reef-boundary--
            YAML);

        $result = $this->loader->load($file, 'MyFormData');

        self::assertInstanceOf(MultipartBody::class, $result);
        self::assertSame('form-data', $result->subtype);
        self::assertSame('multipart/form-data; boundary=reef-boundary', $result->contentType());
    }

    public function testLoadReadsMultipartSubtypeFromXContentTypeShortForm(): void
    {
        $file = $this->createTempFile('yaml', <<<YAML
            components:
              examples:
                MyFormData:
                  summary: A form-data example
                  x-content-type: form-data
                  value: |
                    --reef-boundary
                    Content-Disposition: form-data; name="file"

                    binary
                    --reef-boundary--
            YAML);

        $result = $this->loader->load($file, 'MyFormData');

        self::assertInstanceOf(MultipartBody::class, $result);
        self::assertSame('form-data', $result->subtype);
    }

    public function testLoadThrowsWhenMultipartStringHasNoBoundary(): void
    {
        $file = $this->createTempFile('yaml', <<<YAML
            components:
              examples:
                BadMultipart:
                  summary: A multipart example
                  value: "not a valid multipart body"
            YAML);

        $result = $this->loader->load($file, 'BadMultipart');

        self::assertInstanceOf(JsonBody::class, $result);
        self::assertStringContainsString('not a valid multipart body', $result->getRawBody());
    }

    private function createTempFile(string $extension, string $content): string
    {
        $path = \tempnam(\sys_get_temp_dir(), 'oas_test_').'.'.$extension;
        \file_put_contents($path, $content);
        \register_shutdown_function(static fn () => @\unlink($path));

        return $path;
    }
}
