<?php

declare(strict_types=1);

namespace App\Tests\Shared\Api;

use App\Shared\Domain\Entity\UploadedDocument;
use App\Tests\Module\Core\Api\AbstractApiTestCase;
use Symfony\Component\HttpFoundation\File\UploadedFile;

/**
 * Tests fonctionnels de l'endpoint d'upload generique (ERP-154).
 *
 * Couvre :
 *  - POST multipart d'un PDF valide -> 201, IRI renvoyee, ligne persistee,
 *    checksum sha256 calcule cote serveur ;
 *  - POST d'un MIME hors whitelist (text/plain) -> 422 ;
 *  - POST sans fichier -> 422 ;
 *  - POST anonyme -> 401 (acces /api protege globalement).
 *
 * @internal
 */
final class UploadedDocumentApiTest extends AbstractApiTestCase
{
    private const string ENDPOINT = '/api/uploaded_documents';

    /** @var list<string> */
    private array $tempFiles = [];

    protected function tearDown(): void
    {
        foreach ($this->tempFiles as $path) {
            if (is_file($path)) {
                @unlink($path);
            }
        }
        parent::tearDown();
    }

    public function testUploadValidPdfReturnsIriAndPersistsRowWithChecksum(): void
    {
        $client  = $this->authenticatedClient('admin', 'admin');
        $content = $this->minimalPdf();
        $file    = $this->makeUploadedFile($content, 'facture.pdf');

        $response = $client->request('POST', self::ENDPOINT, [
            'headers' => ['Accept' => 'application/ld+json'],
            'extra'   => ['files' => ['file' => $file]],
        ]);

        self::assertResponseStatusCodeSame(201);

        $data = $response->toArray();

        self::assertArrayHasKey('@id', $data);
        self::assertStringStartsWith(self::ENDPOINT.'/', $data['@id']);
        self::assertSame('facture.pdf', $data['originalFilename']);
        self::assertSame('application/pdf', $data['mimeType']);
        self::assertSame(\strlen($content), $data['sizeBytes']);
        self::assertSame(hash('sha256', $content), $data['checksum']);
        self::assertSame(64, \strlen($data['checksum']));

        // La ligne est bien persistee et relisible via le repository.
        $id       = $data['id'];
        $document = $this->getEm()->getRepository(UploadedDocument::class)->find($id);
        self::assertInstanceOf(UploadedDocument::class, $document);
        self::assertSame(hash('sha256', $content), $document->getChecksum());
    }

    public function testUploadDisallowedMimeTypeReturns422(): void
    {
        $client = $this->authenticatedClient('admin', 'admin');
        $file   = $this->makeUploadedFile('just some plain text content', 'note.txt');

        $client->request('POST', self::ENDPOINT, [
            'headers' => ['Accept' => 'application/ld+json'],
            'extra'   => ['files' => ['file' => $file]],
        ]);

        self::assertResponseStatusCodeSame(422);
    }

    public function testUploadWithoutFileReturns422(): void
    {
        $client = $this->authenticatedClient('admin', 'admin');

        $client->request('POST', self::ENDPOINT, [
            'headers' => ['Accept' => 'application/ld+json'],
            'extra'   => ['files' => []],
        ]);

        self::assertResponseStatusCodeSame(422);
    }

    public function testUploadAnonymousIsRejected(): void
    {
        $client = self::createClient();
        $file   = $this->makeUploadedFile($this->minimalPdf(), 'facture.pdf');

        $client->request('POST', self::ENDPOINT, [
            'headers' => ['Accept' => 'application/ld+json'],
            'extra'   => ['files' => ['file' => $file]],
        ]);

        self::assertResponseStatusCodeSame(401);
    }

    /**
     * Cree un UploadedFile en mode test (move() autorise hors contexte HTTP).
     */
    private function makeUploadedFile(string $content, string $clientName): UploadedFile
    {
        $path = sys_get_temp_dir().'/erp154-api-'.bin2hex(random_bytes(4));
        file_put_contents($path, $content);
        $this->tempFiles[] = $path;

        return new UploadedFile($path, $clientName, null, null, true);
    }

    /**
     * Contenu PDF minimal valide (entete `%PDF-1.4` -> finfo `application/pdf`).
     */
    private function minimalPdf(): string
    {
        return "%PDF-1.4\n"
            ."1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n"
            ."2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj\n"
            ."3 0 obj<</Type/Page/Parent 2 0 R/MediaBox[0 0 612 792]>>endobj\n"
            ."trailer<</Root 1 0 R/Size 4>>\n"
            ."%%EOF\n";
    }
}
