<?php
/**
 * @author      Aurélien BERNARD - TRYDEA - 2021
 */

declare(strict_types=1);

namespace Shared\TransferSync\Worker;

use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Persistence\ObjectManager;
use Mobile\Device\Entity\Individual;
use Mobile\Device\Entity\UnitParcel;
use Mobile\Device\Repository\IndividualRepository;
use Mobile\Device\Repository\UnitParcelRepository;
use Shared\TransferSync\Entity\IndividualStructureChange;
use Shared\TransferSync\Entity\StatusDataEntry;
use UnexpectedValueException;

/**
 * Class ExperimentStructureCheckWorker
 * @package Shared\TransferSync\Worker
 */
class IndividualStructureCheckHelper
{
    private ObjectManager $entityManager;

    /**
     * ExperimentStructureCheckWorker constructor.
     * @param ManagerRegistry $managerRegistry
     */
    public function __construct(
        ManagerRegistry $managerRegistry
    )
    {
        $this->entityManager = $managerRegistry->getManager();
    }

    public function checkDataEntry(StatusDataEntry $projectStatus): void
    {
        /** @var $individualRepo IndividualRepository */
        $individualRepo = $this->entityManager->getRepository(Individual::class);
        /** @var $unitParcelRepo UnitParcelRepository */
        $unitParcelRepo = $this->entityManager->getRepository(UnitParcel::class);
        $requestProject = $projectStatus->getRequest();
        $responseProject = $projectStatus->getResponse();

        // Must never happen
        if (is_null($requestProject) || is_null($responseProject)) {
            throw new UnexpectedValueException('Either requestProject or responseProject is null. Worker has stop');
        }

        $isIndividualPu = $requestProject->getPlatform()->getDevices()->first()->isIndividualPU();
        $entityRepo = $isIndividualPu ? $individualRepo : $unitParcelRepo;

        $dangRequestIndividuals = $entityRepo->findHasDeadStateCodeByProject($requestProject);
        $dangResponseIndividuals = $entityRepo->findHasDeadStateCodeByProject($responseProject);

        foreach ($projectStatus->getChanges() as $change) {
            $projectStatus->removeChange($change);
        }

        /** @var $dangRequestIndividual array<Individual|UnitParcel> */
        /** @var $dangResponseIndividual array<Individual|UnitParcel> */

        // Search for Individual that has no state code anymore.
        $hasLostState = $this->searchForChanges($dangRequestIndividuals, $dangResponseIndividuals);
        $hasListStateResponse = $entityRepo->findByNameByProject($responseProject, array_keys($hasLostState));
        $this->createExperimentStructureChange($projectStatus, $hasListStateResponse, $hasLostState, true, $isIndividualPu);

        // Search for Individual that has a state code in response but not in request.
        $hasNewState = $this->searchForChanges($dangResponseIndividuals, $dangRequestIndividuals);
        $hasNewStateRequest = $entityRepo->findByNameByProject($requestProject, array_keys($hasNewState));
        $this->createExperimentStructureChange($projectStatus, $hasNewStateRequest, $hasNewState, false, $isIndividualPu);

    }

    /**
     * @param array<Individual|UnitParcel> $initialList Individual with state codes
     * @param array<Individual|UnitParcel> $finalList Individuals with state codes
     * @return Individual[] all individuals from 1st param that are not in the 2nd list in an array indexed by their name
     */
    private function searchForChanges(array $initialList, array $finalList): array
    {
        $changeList = [];
        foreach ($initialList as $initialItem) {
            $tmpFinalListFiltered = array_filter($finalList, function ($finalItem) use ($initialItem) {
                return $initialItem->getName() === $finalItem->getName();
            });
            if (sizeof($tmpFinalListFiltered) === 0) {
                $changeList[$initialItem->getName()] = $initialItem;
            }
        }
        return $changeList;
    }

    /**
     * @param StatusDataEntry $projectStatus
     * @param array<Individual|UnitParcel> $initialList
     * @param array<Individual|UnitParcel> $finalList
     * @param bool $reverseRequest
     */
    private function createExperimentStructureChange(StatusDataEntry $projectStatus, array $initialList, array $finalList, bool $reverseRequest, $isIndividualPu): void
    {
        foreach ($initialList as $initialItem) {
            if ($finalItem = $finalList[$initialItem->getName()]) {
                if($isIndividualPu){
                    $projectStatus->addChange(
                        (new IndividualStructureChange())
                            ->setRequestIndividual($reverseRequest ? $finalItem : $initialItem)
                            ->setResponseIndividual($reverseRequest ? $initialItem : $finalItem)
                    );
                }else{
                    $projectStatus->addChange(
                        (new IndividualStructureChange())
                            ->setRequestUnitParcel($reverseRequest ? $finalItem : $initialItem)
                            ->setResponseUnitParcel($reverseRequest ? $initialItem : $finalItem)
                    );
                }

            }
        }
    }
}
