<?php

declare(strict_types=1);

namespace App\Tests\Api\Entity;

use App\Entity\Document;
use App\Entity\Machine;
use App\Entity\Site;
use App\Tests\AbstractApiTestCase;

/**
 * @internal
 */
class DocumentTest extends AbstractApiTestCase
{
    public function testGetCollection(): void
    {
        $this->createDocumentInDb();

        $client = $this->createViewerClient();
        $client->request('GET', '/api/documents');

        $this->assertResponseIsSuccessful();
        $this->assertJsonContainsHydraCollection();
        $this->assertJsonContains(['totalItems' => 1]);
    }

    public function testGetItem(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createViewerClient();
        $client->request('GET', self::iri('documents', $doc->getId()));

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains([
            'name'     => 'test-doc',
            'filename' => 'test.pdf',
            'mimeType' => 'application/pdf',
            'size'     => 1024,
        ]);
    }

    public function testPut(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createGestionnaireClient();
        $client->request('PUT', self::iri('documents', $doc->getId()), [
            'headers' => ['Content-Type' => 'application/ld+json'],
            'json'    => [
                'name'     => 'renamed-doc',
                'filename' => 'test.pdf',
                'path'     => '/uploads/test.pdf',
                'mimeType' => 'application/pdf',
                'size'     => 1024,
            ],
        ]);

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['name' => 'renamed-doc']);
    }

    public function testDelete(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createGestionnaireClient();
        $client->request('DELETE', self::iri('documents', $doc->getId()));

        $this->assertResponseStatusCodeSame(204);
    }

    public function testUnauthenticatedAccess(): void
    {
        $client = $this->createUnauthenticatedClient();
        $client->request('GET', '/api/documents');

        $this->assertResponseStatusCodeSame(401);
    }

    public function testViewerCannotDelete(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createViewerClient();
        $client->request('DELETE', self::iri('documents', $doc->getId()));

        $this->assertResponseStatusCodeSame(403);
    }

    public function testDocumentLinkedToMachine(): void
    {
        $machine = $this->createMachine('Machine A');
        $doc     = $this->createDocumentInDb($machine->getId());

        $client = $this->createViewerClient();
        $client->request('GET', '/api/documents?machine[exists]=true');

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['totalItems' => 1]);
    }

    public function testDocumentLinkedToSite(): void
    {
        $site = $this->createSite('Site A');
        $doc  = $this->createDocumentInDb(siteId: $site->getId());

        $client = $this->createViewerClient();
        $client->request('GET', '/api/documents?site[exists]=true');

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['totalItems' => 1]);
    }

    public function testPatchType(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createGestionnaireClient();
        $client->request('PATCH', self::iri('documents', $doc->getId()), [
            'headers' => ['Content-Type' => 'application/merge-patch+json'],
            'json'    => ['type' => 'devis'],
        ]);

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['type' => 'devis']);
    }

    public function testPatchNameAndType(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createGestionnaireClient();
        $client->request('PATCH', self::iri('documents', $doc->getId()), [
            'headers' => ['Content-Type' => 'application/merge-patch+json'],
            'json'    => ['name' => 'new-name', 'type' => 'facture'],
        ]);

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['name' => 'new-name', 'type' => 'facture']);
    }

    public function testGetItemIncludesType(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createViewerClient();
        $client->request('GET', self::iri('documents', $doc->getId()));

        $this->assertResponseIsSuccessful();
        $this->assertJsonContains(['type' => 'documentation']);
    }

    public function testViewerCannotPatch(): void
    {
        $doc = $this->createDocumentInDb();

        $client = $this->createViewerClient();
        $client->request('PATCH', self::iri('documents', $doc->getId()), [
            'headers' => ['Content-Type' => 'application/merge-patch+json'],
            'json'    => ['type' => 'devis'],
        ]);

        $this->assertResponseStatusCodeSame(403);
    }

    private function createDocumentInDb(?string $machineId = null, ?string $siteId = null): Document
    {
        $doc = new Document();
        $doc->setName('test-doc');
        $doc->setFilename('test.pdf');
        $doc->setPath('/uploads/test.pdf');
        $doc->setMimeType('application/pdf');
        $doc->setSize(1024);

        if ($machineId) {
            $machine = $this->getEntityManager()->getRepository(Machine::class)->find($machineId);
            if ($machine) {
                $doc->setMachine($machine);
            }
        }

        if ($siteId) {
            $site = $this->getEntityManager()->getRepository(Site::class)->find($siteId);
            if ($site) {
                $doc->setSite($site);
            }
        }

        $em = $this->getEntityManager();
        $em->persist($doc);
        $em->flush();

        return $doc;
    }
}
