<?php

declare(strict_types=1);

require_once __DIR__ . \DIRECTORY_SEPARATOR . 'ExportProduct.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'Clinique.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . 'Service' . \DIRECTORY_SEPARATOR . 'CsvExporter.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . 'Service' . \DIRECTORY_SEPARATOR . 'ExportAppVeto' . \DIRECTORY_SEPARATOR . 'ExportGenerator.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . 'Service' . \DIRECTORY_SEPARATOR . 'ExportAppVeto' . \DIRECTORY_SEPARATOR . 'ExportSender.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . 'Service' . \DIRECTORY_SEPARATOR . 'RecommandationManagerApi' . \DIRECTORY_SEPARATOR . 'RecommandationController.php';

class WebserviceSpecificManagementRecommandations implements WebserviceSpecificManagementInterface
{
    /**
     * @var WebserviceOutputBuilderCore|null
     */
    protected $objOutput;

    /**
     * @var mixed
     */
    protected $output;

    /** @var WebserviceRequestCore|null */
    protected $wsObject;

    /**
     * @var int
     */
    private $idEmployee;

    /**
     * @var array<string, bool|float|int|string|null>|null
     */
    private $requestBody = [];

    /**
     * @var int|null
     */
    private $statusCodeResponse;

    private \Symfony\Component\DependencyInjection\ContainerInterface $container;

    private \Myvetshop\Module\Clinique\Repository\MyvetshopCliniqueRepository $cliniqueRepository;

    private \Myvetshop\Module\Clinique\Security\CliniquePermissionChecker $permissionChecker;

    public function __construct()
    {
        $container = PrestaShop\PrestaShop\Adapter\SymfonyContainer::getInstance();

        \assert(null !== $container);

        $this->container = $container;
        $cliniqueRepository = $this->container->get(
            \Myvetshop\Module\Clinique\Repository\MyvetshopCliniqueRepository::class
        );
        $permissionChecker = $this->container->get(
            \Myvetshop\Module\Clinique\Security\CliniquePermissionChecker::class
        );

        \assert($cliniqueRepository instanceof \Myvetshop\Module\Clinique\Repository\MyvetshopCliniqueRepository);
        \assert($permissionChecker instanceof \Myvetshop\Module\Clinique\Security\CliniquePermissionChecker);

        $this->cliniqueRepository = $cliniqueRepository;
        $this->permissionChecker = $permissionChecker;
    }

    public function setObjectOutput(WebserviceOutputBuilderCore $obj): ?WebserviceSpecificManagementInterface
    {
        $this->objOutput = $obj;

        return $this;
    }

    public function getObjectOutput(): ?WebserviceOutputBuilderCore
    {
        return $this->objOutput;
    }

    public function setWsObject(WebserviceRequestCore $obj): ?WebserviceSpecificManagementInterface
    {
        $this->wsObject = $obj;

        return $this;
    }

    public function getWsObject(): ?WebserviceRequestCore
    {
        return $this->wsObject;
    }

    public function manage(): void
    {
        $ret = [];
        try {
            /** @var WebserviceRequestCore $wsObject */
            $wsObject = $this->getWsObject();
            $this->idEmployee = method_exists($wsObject, 'getIdEmployee') ? $wsObject->getIdEmployee() : 0;

            switch ($_SERVER['REQUEST_METHOD']) {
                case 'GET':
                    if (($this->wsObject->urlSegment[0] ?? '') !== 'recommandations') {
                        throw new \Exception('Segment error 0');
                    }

                    if (
                        isset(
                            $this->wsObject->urlSegment[1],
                            $this->wsObject->urlSegment[2],
                            $this->wsObject->urlSegment[3]
                        )
                        && 'recommandations' === $this->wsObject->urlSegment[0]
                        && 'validity' === $this->wsObject->urlSegment[2]
                    ) {
                        // /modules/myvetshopclinique/api/recommandations/<code>/validity/<customer_id>?access_token=
                        // Vérification que le code reco est valide pour un client donné
                        $customerId = (int) $this->wsObject->urlSegment[3];
                        $ret = $this->checkCodeReco($this->wsObject->urlSegment[1], $customerId);
                    } elseif (
                        isset(
                            $this->wsObject->urlSegment[1],
                            $this->wsObject->urlSegment[2]
                        )
                        && 'recommandations' === $this->wsObject->urlSegment[0]
                        && 'pdf' === $this->wsObject->urlSegment[2]
                    ) {
                        // /modules/myvetshopclinique/api/recommandations/<id>/pdf?access_token=
                        // PDF de la reco
                        $this->pdf((int) $this->wsObject->urlSegment[1]);
                    } elseif (
                        isset($this->wsObject->urlSegment[1])
                        && 'recommandations' === $this->wsObject->urlSegment[0]
                    ) {
                        /** @var \Myvetshop\Module\Clinique\Repository\RecommandationRepository $recommandationRepository */
                        $recommandationRepository = $this->container->get(
                            \Myvetshop\Module\Clinique\Repository\RecommandationRepository::class
                        );

                        $clinique = $this->cliniqueRepository->findByEmployeeId($this->idEmployee);
                        if (!$clinique) {
                            $this->statusCodeResponse = 401;
                            throw new Exception('Clinique inconnue');
                        }

                        /** @var \Myvetshop\Module\Clinique\Entity\Recommandation|null $ret */
                        $ret = $recommandationRepository->find((int) $this->wsObject->urlSegment[1]);

                        if (!$ret) {
                            $this->statusCodeResponse = 404;
                            throw new Exception('Recommandation non trouvée');
                        }

                        $customer = new \Customer($ret->animal->idCustomer);
                        if (!$this->permissionChecker->isCustomerOf($clinique, $customer)) {
                            $this->statusCodeResponse = 401;
                            throw new Exception('Le client n\'appartient pas à la clinique');
                        }
                    } elseif (
                        // Listing des recommandations
                        // /modules/myvetshopclinique/api/recommandations?access_token=
                        isset($this->wsObject->urlSegment[0])
                        && 'recommandations' === $this->wsObject->urlSegment[0]
                    ) {
                        $filters = Tools::getValue('filter');
                        if (is_array($filters) && isset($filters['id_customer'])) {
                            $idCustomer = (int) $filters['id_customer'];

                            $ret = $this->listRecoByCustomer($idCustomer);
                        }
                    }

                    break;

                case 'POST':
                    // Import reco
                    $this->requestBody = $this->extractBodyRequest();

                    if (isset(
                        $this->requestBody['code_reco'],
                        $this->requestBody['id_animal'],
                        $this->requestBody['id_customer'])
                    ) {
                        $ret = $this->importReco();
                    }

                    break;

                case 'PUT':
                    // Maj reco
                    if (isset($this->wsObject->urlSegment[1])) {
                        $this->requestBody = $this->extractBodyRequest();
                        if (isset($this->requestBody['id_animal'])) {
                            $ret = $this->updateReco((int) $this->wsObject->urlSegment[1]);
                        }
                    }
                    break;

                case 'DELETE':
                    // Suppression reco
                    // /modules/myvetshopclinique/api/recommandations/<id>?access_token=
                    if (isset($this->wsObject->urlSegment[1])) {
                        $ret = $this->deleteReco((int) $this->wsObject->urlSegment[1]);
                    }

                    break;
            }

            /** @var \Myvetshop\Module\Clinique\Serializer\Normalizer\Admin\RecommandationNormalizer $normalizer */
            $normalizer = $this->container->get(\Myvetshop\Module\Clinique\Serializer\Normalizer\Admin\RecommandationNormalizer::class);

            if ($ret instanceof \Myvetshop\Module\Clinique\Entity\Recommandation) {
                $ret = $normalizer->normalize($ret);
            }

            echo \json_encode($ret, \JSON_PRETTY_PRINT);
            exit;
        } catch (\Exception $exception) {
            \header('Content-Type: application/json');
            $this->statusCodeResponse ? \http_response_code($this->statusCodeResponse) : \http_response_code(500);
            echo \json_encode(['error' => $exception->getMessage()]);
            exit;
        }
    }

    /**
     * @throws Exception
     */
    private function deleteReco(int $idReco): Myvetshop\Module\Clinique\Entity\Recommandation
    {
        $clinique = $this->cliniqueRepository->findByEmployeeId($this->idEmployee);
        if (!$clinique) {
            $this->statusCodeResponse = 401;
            throw new Exception('Clinique inconnue');
        }

        /** @var \Myvetshop\Module\Clinique\Repository\RecommandationRepository $recommandationRepository */
        $recommandationRepository = $this->container->get(
            \Myvetshop\Module\Clinique\Repository\RecommandationRepository::class
        );

        /** @var \Myvetshop\Module\Clinique\Entity\Recommandation|null $recommandation */
        $recommandation = $recommandationRepository->find($idReco);

        if (!$recommandation) {
            $this->statusCodeResponse = 404;
            throw new Exception('La recommandation n\'existe pas');
        }

        $customer = new \Customer($recommandation->animal->idCustomer);

        if (!$this->permissionChecker->isCustomerOf($clinique, $customer)) {
            $this->statusCodeResponse = 401;
            throw new Exception('Le client n\'appartient pas à la clinique');
        }

        $recommandation->deleted = true;

        /** @var \Doctrine\ORM\EntityManagerInterface $entityManager */
        $entityManager = $this->container->get('doctrine.orm.entity_manager');
        $entityManager->flush();

        return $recommandation;
    }

    /**
     * @throws Exception
     */
    private function updateReco(int $idReco): Myvetshop\Module\Clinique\Entity\Recommandation
    {
        throw new \Exception('Not implemented');
    }

    private function importReco(): Myvetshop\Module\Clinique\Entity\Recommandation
    {
        $clinique = $this->cliniqueRepository->findByEmployeeId($this->idEmployee);
        if (!$clinique) {
            $this->statusCodeResponse = 401;
            throw new Exception('Clinique inconnue');
        }

        /** @var \Myvetshop\Module\Clinique\Api\Crokit\CrokitApiClient $crokitApiClient; */
        $crokitApiClient = $this->container->get(
            \Myvetshop\Module\Clinique\Api\Crokit\CrokitApiClient::class
        );

        /** @var \Myvetshop\Module\Clinique\Api\Crokit\RecommandationImporter $recommandationImporter */
        $recommandationImporter = $this->container->get(
            \Myvetshop\Module\Clinique\Api\Crokit\RecommandationImporter::class
        );

        $customer = new Customer(
            \is_numeric($this->requestBody['id_customer'] ?? null)
                ? \intval($this->requestBody['id_customer'] ?? null)
                : 0
        );

        if (!$customer->id) {
            $this->statusCodeResponse = 404;
            throw new Exception('Le client n\'existe pas');
        }

        if (!$this->permissionChecker->isCustomerOf($clinique, $customer)) {
            $this->statusCodeResponse = 401;
            throw new Exception('Le client n\'appartient pas à la clinique');
        }

        $recommandationDto = $crokitApiClient->getRecommandation(\strval($this->requestBody['code_reco'] ?? ''));

        if (!$recommandationDto) {
            $this->statusCodeResponse = 404;
            throw new Exception('Recommandation non trouvée');
        }

        $animal = $recommandationImporter->getRecommandationAnimal(
            \strval($this->requestBody['id_animal'] ?? ''),
            $customer,
            $recommandationDto
        );

        return $recommandationImporter->import($recommandationDto, $animal);
    }

    /**
     * @throws Exception
     */
    private function checkCodeReco(string $codeReco, ?int $customerId): ?Myvetshop\Module\Clinique\Api\Crokit\Dto\CrokitRecommandation
    {
        $clinique = $this->cliniqueRepository->findByEmployeeId($this->idEmployee);
        if (!$clinique) {
            $this->statusCodeResponse = 401;
            throw new \Exception('Clinique inconnue');
        }

        $customer = new \Customer($customerId);

        if (!$customer->id) {
            $this->statusCodeResponse = 404;
            throw new \Exception('Le client n\'existe pas');
        }

        if (!$this->permissionChecker->isCustomerOf($clinique, $customer)) {
            $this->statusCodeResponse = 401;
            throw new \Exception('Le client n\'appartient pas à la clinique');
        }

        /** @var \Myvetshop\Module\Clinique\Api\Crokit\CrokitApiClient $crokitApiClient */
        $crokitApiClient = $this->container->get(\Myvetshop\Module\Clinique\Api\Crokit\CrokitApiClient::class);

        return $crokitApiClient->getRecommandation($codeReco);
    }

    /**
     * @return array<string, mixed>
     *
     * @throws Exception
     */
    private function listRecoByCustomer(int $idCustomer): array
    {
        $customer = new Customer($idCustomer);
        if (!$customer->id) {
            $this->statusCodeResponse = 404;
            throw new Exception('Le client n\'existe pas');
        }

        \assert(\method_exists($customer, 'checkGroup'));

        if (!$customer->checkGroup($this->idEmployee)) {
            $this->statusCodeResponse = 401;
            throw new Exception('Le client n\'appartient pas à la clinique');
        }

        $recommandationManagerApi = new RecommandationController($customer);

        return $recommandationManagerApi->actionCricCrocList();
    }

    /**
     * @throws Exception
     */
    private function pdf(int $idReco): void
    {
        throw new \Exception('Not implemented');
    }

    /**
     * @return array<string, bool|float|int|string|null>
     *
     * @throws Exception
     */
    private function extractBodyRequest(): array
    {
        $json = \file_get_contents('php://input');

        if (!$json) {
            throw new Exception('Une erreur s\'est produite lors de l\'extraction du contenu de la requête');
        }

        /** @var array<string, bool|float|int|string|null> $parsedJson */
        $parsedJson = \json_decode($json, true);
        if (!\is_array($parsedJson)) {
            throw new Exception('Une erreur s\'est produite lors de l\'extraction du contenu de la requête');
        }

        return $parsedJson;
    }

    /**
     * This must be return an array with specific values as WebserviceRequest expects.
     *
     * @return array<mixed>|string
     */
    public function getContent()
    {
        if (!$this->objOutput) {
            return [];
        }

        return $this->objOutput->getObjectRender()->overrideContent($this->output);
    }
}
