<?php

namespace App\Tests\A_Unit\Adapter\Controller\Front;

use Myvetshop\Module\Clinique\Adapter\Controller\Front\MyRecoImportControllerAdapter;
use Myvetshop\Module\Clinique\Api\Crokit\CrokitApiClient;
use Myvetshop\Module\Clinique\Api\Crokit\Dto\CrokitRecommandation;
use Myvetshop\Module\Clinique\Api\Crokit\RecommandationImporter;
use Myvetshop\Module\Clinique\Entity\Animal;
use Myvetshop\Module\Clinique\Entity\Recommandation;
use Myvetshop\Module\Clinique\Presenter\RecommandationPresenter;
use Myvetshop\Module\Clinique\Repository\AnimalRepository;
use Myvetshop\Module\Clinique\Repository\RecommandationRepository;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;

class MyRecoImportControllerAdapterTest extends TestCase
{
    /**
     * @var AnimalRepository&MockObject
     */
    private AnimalRepository $animalRepository;

    /**
     * @var CrokitApiClient&MockObject
     */
    private CrokitApiClient $crokitApiClient;

    /**
     * @var RecommandationImporter&MockObject
     */
    private RecommandationImporter $recommandationImporter;

    /**
     * @var RecommandationPresenter&MockObject
     */
    private RecommandationPresenter $recommandationPresenter;

    /**
     * @var RecommandationRepository&MockObject
     */
    private RecommandationRepository $recommandationRepository;

    /**
     * @var \Context&MockObject
     */
    private \Context $context;

    private MyRecoImportControllerAdapter $myRecoControllerAdapter;

    public function setUp(): void
    {
        $this->animalRepository = $this->createMock(AnimalRepository::class);
        $this->crokitApiClient = $this->createMock(CrokitApiClient::class);
        $this->recommandationImporter = $this->createMock(RecommandationImporter::class);
        $this->recommandationPresenter = $this->createMock(RecommandationPresenter::class);
        $this->recommandationRepository = $this->createMock(RecommandationRepository::class);

        $this->context = $this->createMock(\Context::class);
        $this->context->customer = $this->createMock(\Customer::class);

        $this->myRecoControllerAdapter = new MyRecoImportControllerAdapter(
            $this->animalRepository,
            $this->crokitApiClient,
            $this->recommandationImporter,
            $this->recommandationPresenter,
            $this->recommandationRepository,
        );
    }

    /**
     * Test 1 : Affichage de la page sans aucune information
     */
    public function testHandleEmptyRequest(): void
    {
        $this->recommandationRepository
            ->expects($this->once())
            ->method('findBy')
            ->with(['idCustomer' => null, 'deleted' => false])
            ->willReturn([]);

        $request = new Request();

        $result = $this->myRecoControllerAdapter->handleRequest($this->context, $request);

        $this->assertEquals(
            [
                'template' => 'module:myvetshopclinique/views/templates/front/myreco/recommandations.tpl',
                'parameters' => [
                    'animals' => null,
                    'code' => null,
                    'recommandations' => [],
                ],
            ],
            $result,
        );
    }

    /**
     * Test 2 : Affichage d'un code invalide
     */
    public function testHandleRequestUnknownCode(): void
    {
        $this->recommandationRepository
            ->expects($this->once())
            ->method('findBy')
            ->with(['idCustomer' => null, 'deleted' => false])
            ->willReturn([]);

        $request = new Request();
        $request->query->set('code', '123456');

        $this->crokitApiClient
            ->expects($this->once())
            ->method('getRecommandation')
            ->with('123456')
            ->willReturn(null);

        $result = $this->myRecoControllerAdapter->handleRequest($this->context, $request);

        $this->assertEquals(
            [
                'template' => 'module:myvetshopclinique/views/templates/front/myreco/recommandations.tpl',
                'parameters' => [
                    'animals' => null,
                    'code' => '123456',
                    'recommandations' => [],
                ],
                'errors' => [
                    'Le code recommandation n\'existe pas',
                ],
            ],
            $result,
        );
    }

    /**
     * Test 3 : Code OK, mais déjà importé
     */
    public function testHandleAlreadyImported(): void
    {
        $this->recommandationRepository
            ->expects($this->once())
            ->method('findBy')
            ->with(['idCustomer' => null, 'deleted' => false])
            ->willReturn([]);

        $request = new Request();
        $request->query->set('code', '123456');

        $recommandationDto = new CrokitRecommandation();
        $recommandationDto->codeReco = '123456';

        $this->crokitApiClient
            ->expects($this->once())
            ->method('getRecommandation')
            ->with('123456')
            ->willReturn($recommandationDto);

        $recommandation = new Recommandation();

        $this->recommandationRepository
            ->expects($this->once())
            ->method('findOneBy')
            ->with(['codeReco' => '123456', 'deleted' => false])
            ->willReturn($recommandation);

        $result = $this->myRecoControllerAdapter->handleRequest($this->context, $request);

        $this->assertEquals(
            [
                'template' => 'module:myvetshopclinique/views/templates/front/myreco/recommandations.tpl',
                'parameters' => [
                    'animals' => null,
                    'code' => '123456',
                    'recommandations' => [],
                ],
                'errors' => [
                    'Le code fourni a déjà été importé',
                ],
            ],
            $result,
        );
    }

    /**
     * Test 4 : Code OK, mais utilisateur non connecté
     */
    public function testHandleNotConnected(): void
    {
        $this->recommandationRepository
            ->expects($this->once())
            ->method('findBy')
            ->with(['idCustomer' => null, 'deleted' => false])
            ->willReturn([]);

        $request = new Request();
        $request->query->set('code', '123456');

        $recommandationDto = new CrokitRecommandation();
        $recommandationDto->codeReco = '123456';

        $this->recommandationPresenter
            ->method('presentDto')
            ->with($recommandationDto)
            ->willReturn(['codeReco' => '123456']);

        $this->crokitApiClient
            ->expects($this->once())
            ->method('getRecommandation')
            ->with('123456')
            ->willReturn($recommandationDto);

        $result = $this->myRecoControllerAdapter->handleRequest($this->context, $request);

        $this->assertEquals(
            [
                'template' => 'module:myvetshopclinique/views/templates/front/myreco/recommandation_public.tpl',
                'parameters' => [
                    'animals' => null,
                    'code' => '123456',
                    'recommandations' => [],
                    'recommandation' => ['codeReco' => '123456'],
                ],
            ],
            $result,
        );
    }

    /**
     * Test 4 : Code OK, utilisateur connecté
     */
    public function testHandleConnected(): void
    {
        $this->recommandationRepository
            ->expects($this->once())
            ->method('findBy')
            ->with(['idCustomer' => 1, 'deleted' => false])
            ->willReturn([]);

        $request = new Request();
        $request->query->set('code', '123456');

        $recommandationDto = new CrokitRecommandation();
        $recommandationDto->codeReco = '123456';
        $recommandationDto->esp = 'chien';

        $this->recommandationPresenter
            ->method('presentDto')
            ->with($recommandationDto)
            ->willReturn(['codeReco' => '123456']);

        $this->crokitApiClient
            ->expects($this->once())
            ->method('getRecommandation')
            ->with('123456')
            ->willReturn($recommandationDto);

        $this->context->customer->id = 1;

        $this->animalRepository
            ->expects($this->once())
            ->method('findBy')
            ->with([
                'idCustomer' => 1,
                'deleted' => false,
            ])
            ->willReturn([]);

        $result = $this->myRecoControllerAdapter->handleRequest($this->context, $request);

        $this->assertEquals(
            [
                'template' => 'module:myvetshopclinique/views/templates/front/myreco/recommandations.tpl',
                'parameters' => [
                    'animals' => [],
                    'code' => '123456',
                    'recommandation' => ['codeReco' => '123456'],
                    'recommandations' => [],
                ],
            ],
            $result,
        );
    }

    /**
     * Test 4 : Code OK, l'utilisateur a sélectionné "auto" comme animal pour l'import
     */
    public function testHandleImportAuto(): void
    {
        $this->animalRepository
            ->expects($this->once())
            ->method('findBy')
            ->with([
                'idCustomer' => 1,
                'deleted' => false,
            ])
            ->willReturn([]);

        $this->recommandationRepository
            ->expects($this->once())
            ->method('findBy')
            ->with(['idCustomer' => 1, 'deleted' => false])
            ->willReturn([]);

        $request = new Request();
        $request->query->set('code', '123456');
        $request->query->set('id_animal', 'auto');

        $recommandationDto = new CrokitRecommandation();
        $recommandationDto->codeReco = '123456';
        $recommandationDto->esp = 'chien';
        $recommandationDto->petname = 'Medor';
        $recommandationDto->sex = 'Mâle';
        $recommandationDto->dob = '12 mai';

        $this->crokitApiClient
            ->expects($this->once())
            ->method('getRecommandation')
            ->with('123456')
            ->willReturn($recommandationDto);

        $this->context->customer->id = 1;

        $recommandation = new Recommandation();

        $this->recommandationImporter
            ->expects($this->once())
            ->method('import')
            ->with(
                $recommandationDto,
                $this->isInstanceOf(Animal::class),
            )
            ->willReturn($recommandation);

        $result = $this->myRecoControllerAdapter->handleRequest($this->context, $request);

        $this->assertEquals(
            'module:myvetshopclinique/views/templates/front/myreco/recommandations.tpl',
            $result['template']
        );
        $this->assertIsArray($result['parameters']['animals']);
        $this->assertCount(
            1,
            $result['parameters']['animals']
        );
        $this->assertNull(
            $result['parameters']['code']
        );
        $this->assertCount(
            1,
            $result['parameters']['recommandations']
        );
    }
}
