<?php

declare(strict_types=1);

namespace Webapp\Core\Worker;

use ApiPlatform\Core\Api\IriConverterInterface;
use Doctrine\ORM\EntityManagerInterface;
use Dtc\QueueBundle\Model\Worker;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Shared\Authentication\Entity\RelUserSite;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Throwable;
use Webapp\Core\Entity\AbstractVariable;
use Webapp\Core\Entity\CloneSite\CloneSite;
use Webapp\Core\Entity\StateCode;
use Webapp\Core\Entity\VariableConnection;
use Webapp\Core\Enumeration\SpreadingEnum;
use Webapp\Core\Service\CloneService;

/**
 * Class ParsingWorker
 * @package Webapp\FileManagement\Worker
 */
class SiteWorker extends Worker implements LoggerAwareInterface
{
    use LoggerAwareTrait;

    private CloneService $cloneService;

    private EntityManagerInterface $entityManager;

    private IriConverterInterface $iriConverter;

    private Mailer $mailer;

    private string $mailSender;

    private TranslatorInterface $translator;


    public function __construct(
        EntityManagerInterface $entityManager,
        CloneService           $cloneService,
        IriConverterInterface  $iriConverter,
        MailerInterface        $mailer,
        string                 $mailSender,
        TranslatorInterface    $translator
    )
    {
        $this->cloneService = $cloneService;
        $this->entityManager = $entityManager;
        $this->iriConverter = $iriConverter;
        $this->mailer = $mailer;
        $this->mailSender = $mailSender;
        $this->translator = $translator;
    }

    /**
     * @param int $dataId
     */
    public function cloneSite(int $dataId)
    {
        $data = $this->entityManager->getRepository(CloneSite::class)->find($dataId);
        $site = $data->getOriginSite();

        $iriMap = [];

        $res = $this->cloneService->cloneSite($data->getOriginSite(), $data->getName());
        $data->setResult($res);

        $this->entityManager->persist($res);

        if ($data->isCloneLibraries()) {
            foreach ($site->getOezNatures() as $oezNature) {
                $resOezNature = $this->cloneService->cloneOEZNature($oezNature, $iriMap);
                $iriMap[$this->iriConverter->getIriFromItem($oezNature)] = $resOezNature;
                $res->addOezNature($resOezNature);
            }
            foreach ($site->getExperiments() as $experiment) {
                $res->addExperiment($this->cloneService->cloneExperiment($experiment, $iriMap));
            }
            foreach ($site->getProtocols() as $protocol) {
                $res->addProtocol($this->cloneService->cloneProtocol($protocol));
            }
            foreach ($site->getFactors() as $factor) {
                $res->addFactor($this->cloneService->cloneFactor($factor));
            }

            foreach ($site->getGeneratorVariables() as $variable) {
                $resVariable = $this->cloneService->cloneGeneratorVariable($variable, $iriMap);
                $res->addGeneratorVariable($resVariable);

            }
            foreach ($site->getSimpleVariables() as $variable) {
                $resVariable = $this->cloneService->cloneSimpleVariable($variable);
                $res->addSimpleVariable($resVariable);
            }
            foreach ($site->getDevices() as $device) {
                $res->addDevice($this->cloneService->cloneDevice($device, $iriMap));
            }
            foreach ($site->getStateCodes() as $stateCode) {
                $res->addStateCode($this->cloneService->cloneStateCode($stateCode));
            }
            foreach ($site->getValueLists() as $valueList) {
                $res->addValueList($this->cloneService->cloneValueList($valueList));
            }
        } else {
            $res->addStateCode(
                (new StateCode())
                    ->setCode(-9)
                    ->setMeaning('Code d\'état permanent : Individu Mort')
                    ->setTitle('Mort')
                    ->setSpreading(SpreadingEnum::INDIVIDUAL)
                    ->setColor(null)
                    ->setPermanent(true)
            );
            $res->addStateCode(
                (new StateCode())
                    ->setCode(-6)
                    ->setMeaning('Code d\'état permanent : Donnée manquante')
                    ->setTitle('Donnée Manquante')
                    ->setSpreading(null)
                    ->setColor(null)
                    ->setPermanent(true)
            );
        }

        foreach ($site->getUserRoles() as $role) {
            if ($data->isCloneUsers() || $role->getUser() === $data->getAdmin()) {
                $res->addUserRole((new RelUserSite())
                    ->setUser($role->getUser())
                    ->setRole($role->getRole())
                );
            }
        }


        if ($data->isCloneLibraries() && $data->isCloneUsers() && $data->isClonePlatforms()) {

            foreach ($site->getPlatforms() as $platform) {
                $resPlatform = $this->cloneService->clonePlatform($platform, $iriMap);
                $iriMap[$this->iriConverter->getIriFromItem($platform)] = $resPlatform;
                $res->addPlatform($resPlatform);

                $this->entityManager->persist($resPlatform);
                $this->entityManager->flush();

                if ($data->isCloneProjects()) {
                    foreach ($platform->getProjects() as $project) {
                        $resProject = $this->cloneService->cloneProject($project, null, $iriMap);
                        $iriMap[$this->iriConverter->getIriFromItem($project)] = $resProject;
                        $resProject->setPlatform(
                            $iriMap[$this->iriConverter->getIriFromItem($project->getPlatform())]
                        );
                        if ($resProject->getPathBase() !== null) {
                            $selectedIris = $this->getNewIris($resProject->getPathBase()->getSelectedIris(), $iriMap);
                            $resProject->getPathBase()->setSelectedIris($selectedIris);
                            $orderedIris = $this->getNewIris($resProject->getPathBase()->getOrderedIris(), $iriMap);
                            $resProject->getPathBase()->setOrderedIris($orderedIris);
                            foreach ($resProject->getPathBase()->getUserPaths() as $userPath) {
                                $workflowIris = $this->getNewIris($userPath->getWorkflow(), $iriMap);
                                $userPath->setWorkflow($workflowIris);
                            }
                        }
                        foreach ($project->getExperiments() as $experiment) {
                            $resProject->addExperiment(
                                $iriMap[$this->iriConverter->getIriFromItem($experiment)]
                            );
                        }
                        $resPlatform->addProject($resProject);

                        $this->entityManager->persist($resProject);

                        $projectDatasToClone = [];
                        if ($data->isCloneEntries()) {
                            $projectDatasToClone = $project->getProjectDatas();
                        } else {
                            /** @var AbstractVariable $variable */
                            foreach ($project->getVariables() as $variable) {
                                /** @var VariableConnection $connectedVariable */
                                foreach ($variable->getConnectedVariables() as $connectedVariable) {
                                    if (!in_array($connectedVariable->getDataEntryVariable()->getProjectData(), $projectDatasToClone)) {
                                        $projectDatasToClone[] = $connectedVariable->getDataEntryVariable()->getProjectData();
                                    }
                                }
                            }
                        }
                        foreach ($projectDatasToClone as $projectData) {
                            $resProjectData = $this->cloneService->cloneProjectData($projectData, $iriMap);
                            $iriMap[$this->iriConverter->getIriFromItem($project)]->addProjectData($resProjectData);
                        }
                        foreach ($resProject->getVariables() as $resVariable) {
                            /** @var VariableConnection $connectedVariable */
                            foreach ($resVariable->getConnectedVariables() as $connectedVariable) {
                                $connectedVariable->setDataEntryVariable($iriMap[$this->iriConverter->getIriFromItem($connectedVariable->getDataEntryVariable())]);
                            }
                            foreach ($resVariable->getTests() as $test) {
                                $resVariable->removeTest($test);
                            }
                        }
                        foreach ($project->getVariables() as $variable) {
                            foreach ($variable->getTests() as $test) {
                                $iriMap[$this->iriConverter->getIriFromItem($variable)]->addTest($this->cloneService->cloneTest($test, $iriMap));
                            }
                        }
                    }


                }
            }


        }
        $this->entityManager->flush();
        try {

            $message = (new TemplatedEmail())
                ->from($this->mailSender)
                ->to($data->getUser()->getEmail())
                ->subject("Copie de site terminée")
                ->htmlTemplate('emails/site-clone-finished.html.twig')
                ->context([
                    'siteName' => $res->getLabel()
                ]);

            $this->mailer->send($message);
        } catch (Throwable $exception) {
            $this->logger->critical('Error occured while sending email to user', [$exception]);
        }
    }


    public function getName()
    {
        return 'SiteWorker';
    }

    /**
     * @param string[] $originalIris
     * @param array<string, object> $iriMap
     *
     * @return string[]
     */
    private function getNewIris(array $originalIris, array $iriMap): array
    {
        return array_map(
            fn(string $iri) => $this->iriConverter->getIriFromItem($iriMap[$iri]),
            array_filter($originalIris, function (string $iri) use ($iriMap) {
                if (!array_key_exists($iri, $iriMap)) {
                    $this->logger->warning('IRI d\'un objet non lié au site. Cet objet sera ignoré dans le site cloné.', ['iri' => $iri]);

                    return false;
                }

                return true;
            })
        );
    }
}
