<?php

namespace App\Tests\A_Unit\Controller\Api;

use Myvetshop\Module\Clinique\Controller\Api\EventsGetAllController;
use Myvetshop\Module\Clinique\Entity\EstablishmentEvent;
use Myvetshop\Module\Clinique\Entity\MyvetshopClinique;
use Myvetshop\Module\Clinique\Repository\EstablishmentEventRepository;
use Myvetshop\Module\Clinique\Repository\MyvetshopCliniqueRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;

class EventsGetAllControllerTest extends TestCase
{
    private EventsGetAllController $controller;
    /**
     * @var EstablishmentEventRepository&MockObject
     */
    private EstablishmentEventRepository $eventRepositoryMock;
    /**
     * @var MyvetshopCliniqueRepository&MockObject
     */
    private $clinicRepositoryMock;
    private MyvetshopClinique $clinique;

    public function setUp(): void
    {
        parent::setUp();
        $this->eventRepositoryMock = $this->createMock(EstablishmentEventRepository::class);
        $this->clinicRepositoryMock = $this->createMock(MyvetshopCliniqueRepository::class);

        $this->controller = new EventsGetAllController($this->eventRepositoryMock, $this->clinicRepositoryMock);

        $this->clinique = new MyvetshopClinique();
        $this->clinique->apiKey = 'testpass';
        $this->clinique->deleted = false;
    }

    public function testInvokeWithInvalidUser(): void
    {
        $request = new Request();

        try {
            $this->controller->__invoke($request);

            $this->fail('No exception thrown');
        } catch (\Exception $e) {
            $this->assertInstanceOf(HttpException::class, $e);
            $this->assertEquals(Response::HTTP_UNAUTHORIZED, $e->getStatusCode());
        }
    }

    public function testInvokeWithInvalidApiKey(): void
    {
        $this->clinicRepositoryMock
            ->expects($this->any())
            ->method('findByCodePrivilege')
            ->willReturn($this->clinique);

        $request = new Request(
            [],
            ['after' => 0, 'limit' => 1],
            [],
            [],
            [],
            ['PHP_AUTH_USER' => 'testuser', 'PHP_AUTH_PW' => 'wrongpass']
        );

        try {
            $this->controller->__invoke($request);
        } catch (\Exception $e) {
            $this->assertInstanceOf(HttpException::class, $e);
            $this->assertEquals(Response::HTTP_UNAUTHORIZED, $e->getStatusCode());
        }
    }

    public function testInvokeWithSuccessfulEmptyResponse(): void
    {
        $this->clinicRepositoryMock
            ->expects($this->any())
            ->method('findByCodePrivilege')
            ->willReturn($this->clinique);

        $this->eventRepositoryMock
            ->method('getLastEventIdByClinique')
            ->willReturn(null);

        $this->eventRepositoryMock
            ->method('findEventsByClinique')
            ->willReturn([]);

        $request = new Request(
            [],
            ['after' => 5001, 'limit' => 5000],
            [],
            [],
            [],
            ['PHP_AUTH_USER' => 'testuser', 'PHP_AUTH_PW' => 'testpass']
        );
        $response = $this->controller->__invoke($request);

        $this->assertInstanceOf(Response::class, $response);
        $this->assertEquals(Response::HTTP_REQUESTED_RANGE_NOT_SATISFIABLE, $response->getStatusCode());
    }

    public function testInvokeWithSuccessfulPaginatedResponse(): void
    {
        $this->clinicRepositoryMock
            ->expects($this->any())
            ->method('findByCodePrivilege')
            ->willReturn($this->clinique);

        $this->eventRepositoryMock
            ->method('getLastEventIdByClinique')
            ->willReturn(10);

        $event = $this->createMock(EstablishmentEvent::class);
        $event->clinique = $this->clinique;
        $event->method('getId')->willReturn(1);
        $event->event = EstablishmentEvent::EVENT_CUSTOMER_JOIN;
        $event->idCustomer = 5;
        $event->customerData = [''];
        $event->dateAdd = new \DateTimeImmutable('2024-01-16 10:00:00');

        $this->eventRepositoryMock
            ->method('findEventsByClinique')
            ->with($this->clinique, 0, 1)
            ->willReturn([$event]);

        $request = new Request(
            ['after' => 0, 'limit' => 1],
            [],
            [],
            [],
            [],
            ['PHP_AUTH_USER' => 'testuser', 'PHP_AUTH_PW' => 'testpass']
        );
        $response = $this->controller->__invoke($request);

        $this->assertInstanceOf(Response::class, $response);
        $this->assertEquals(Response::HTTP_PARTIAL_CONTENT, $response->getStatusCode());

        $rawJson = $response->getContent();
        \assert(\is_string($rawJson));
        $this->assertJson($rawJson);

        $json = \json_decode($rawJson, true);
        $this->assertEquals(
            [
                ['id' => $event->getId(), 'event' => $event->event, 'customer' => $event->customerData, 'order' => null],
            ],
            $json
        );
        $this->assertEquals('events 1-1/10', $response->headers->get('Content-Range'));
    }
}
