<?php

namespace Webapp\Core\ApiOperation;

use ApiPlatform\Core\Api\IriConverterInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Security\Core\Security;
use Webapp\Core\Entity\Block;
use Webapp\Core\Entity\Experiment;
use Webapp\Core\Entity\Individual;
use Webapp\Core\Entity\Move\Copy;
use Webapp\Core\Entity\OutExperimentationZone;
use Webapp\Core\Entity\Platform;
use Webapp\Core\Entity\SubBlock;
use Webapp\Core\Entity\SurfacicUnitPlot;
use Webapp\Core\Entity\UnitPlot;
use Webapp\Core\Enumeration\PathLevelEnum;
use Webapp\Core\Service\CloneService;

/**
 * Class CopyBusinessObjectOperation.
 */
final class CopyBusinessObjectOperation extends MultipleBusinessObjectAbstractOperation
{
    private CloneService $cloneService;

    public function __construct(Security $security, IriConverterInterface $iriConverter, EntityManagerInterface $entityManager, CloneService $cloneService)
    {
        parent::__construct($security, $iriConverter, $entityManager);
        $this->cloneService = $cloneService;
    }

    /**
     * @param Request $request
     * @param $data Copy
     * @return mixed
     */
    public function __invoke(Request $request, $data)
    {
        /** @var Platform | Experiment $parent common parent to do security checks */
        $parent = null;
        /** @var array<SurfacicUnitPlot | Individual> $objectLeafs iri => leaf object */
        $objectLeafs = $this->getObjectLeafs($data->getObjectIris(), $parent);

        /** @var array<SurfacicUnitPlot | Individual> $positionMap x,y => all parent's leaf objects */
        $positionMap = [];
        /** @var array<int> $maxNumbers PathlevelEnum => maxNumber */
        $maxNumbers = [PathLevelEnum::BLOCK => 0, PathLevelEnum::SUB_BLOCK => 0, PathLevelEnum::UNIT_PLOT => 0, PathLevelEnum::SURFACIC_UNIT_PLOT => 0, PathLevelEnum::INDIVIDUAL => 0, PathLevelEnum::OEZ => 0];
        $individualUp = null;

        $iriMap = [];
        $createdObjects = [];

        $objectPathLevel = null;
        foreach ($data->getObjectIris() as $key => $tab) {
            if (count($tab) > 0) {
                if ($objectPathLevel !== null) {
                    throw new BadRequestHttpException();
                }
                $objectPathLevel = $key;
            }
        }
        $illegalCopy = false;
        switch ($objectPathLevel) {
            case PathLevelEnum::INDIVIDUAL:
                $illegalCopy = !$data->getParent() instanceof UnitPlot;
                break;
            case PathLevelEnum::SURFACIC_UNIT_PLOT:
            case PathLevelEnum::UNIT_PLOT:
                $illegalCopy = !$data->getParent() instanceof SubBlock && !$data->getParent() instanceof Block;
                break;
            case PathLevelEnum::SUB_BLOCK:
                $illegalCopy = !$data->getParent() instanceof Block;
                break;
            case PathLevelEnum::BLOCK:
                $illegalCopy = !$data->getParent() instanceof Experiment;
                break;
        }
        if ($illegalCopy) {
            throw new BadRequestHttpException();
        }

        if ($parent instanceof Platform) {
            $this->handlePlatform($parent, $positionMap, false, $maxNumbers, $individualUp);
            foreach ($parent->getExperiments() as $experiment) {
                foreach ($experiment->getProtocol()->getTreatments() as $treatment) {
                    $iriMap[$this->iriConverter->getIriFromItem($treatment)] = $treatment;
                }
            }
        } else {
            $this->handleExperiment($parent, $positionMap, false, $maxNumbers, $individualUp);
            foreach ($parent->getProtocol()->getTreatments() as $treatment) {
                $iriMap[$this->iriConverter->getIriFromItem($treatment)] = $treatment;
            }
        }

        if (isset($data->getObjectIris()[PathLevelEnum::OEZ])) {
            foreach ($data->getObjectIris()[PathLevelEnum::OEZ] as $iri) {
                /** @var OutExperimentationZone $oez */
                $oez = $this->iriConverter->getItemFromIri($iri);
                $iriMap[$this->iriConverter->getIriFromItem($oez->getNature())] = $oez->getNature();
                $createdObjects[] = $iriMap[$iri] = $this->cloneService->cloneOutExperimentationZone($oez, $iriMap, $maxNumbers);
                $data->getParent()->addOutExperimentationZone($iriMap[$iri]);
            }
        }
        if (isset($data->getObjectIris()[PathLevelEnum::INDIVIDUAL])) {
            foreach ($data->getObjectIris()[PathLevelEnum::INDIVIDUAL] as $iri) {
                /** @var Individual $individual */
                $individual = $this->iriConverter->getItemFromIri($iri);
                $createdObjects[] = $iriMap[$iri] = $this->cloneService->cloneIndividual($individual, $maxNumbers);
                $data->getParent()->addIndividual($iriMap[$iri]);
            }
        }
        if (isset($data->getObjectIris()[PathLevelEnum::SURFACIC_UNIT_PLOT])) {
            foreach ($data->getObjectIris()[PathLevelEnum::SURFACIC_UNIT_PLOT] as $iri) {
                /** @var SurfacicUnitPlot $surfacicUnitPlot */
                $surfacicUnitPlot = $this->iriConverter->getItemFromIri($iri);
                $createdObjects[] = $iriMap[$iri] = $this->cloneService->cloneSurfacicUnitPlot($surfacicUnitPlot, $iriMap, $maxNumbers);
                $data->getParent()->addSurfacicUnitPlots($iriMap[$iri]);
            }
        }
        if (isset($data->getObjectIris()[PathLevelEnum::UNIT_PLOT])) {
            foreach ($data->getObjectIris()[PathLevelEnum::UNIT_PLOT] as $iri) {
                /** @var UnitPlot $unitPlot */
                $unitPlot = $this->iriConverter->getItemFromIri($iri);
                $createdObjects[] = $iriMap[$iri] = $this->cloneService->cloneUnitPlot($unitPlot, $iriMap, $maxNumbers);
                $data->getParent()->addUnitPlots($iriMap[$iri]);
            }
        }
        if (isset($data->getObjectIris()[PathLevelEnum::SUB_BLOCK])) {
            foreach ($data->getObjectIris()[PathLevelEnum::SUB_BLOCK] as $iri) {
                /** @var SubBlock $subBlock */
                $subBlock = $this->iriConverter->getItemFromIri($iri);
                $createdObjects[] = $iriMap[$iri] = $this->cloneService->cloneSubBloc($subBlock, $iriMap, $maxNumbers);
                $data->getParent()->addSubBlocks($iriMap[$iri]);
            }
        }
        if (isset($data->getObjectIris()[PathLevelEnum::BLOCK])) {
            foreach ($data->getObjectIris()[PathLevelEnum::BLOCK] as $iri) {
                /** @var Block $block */
                $block = $this->iriConverter->getItemFromIri($iri);
                $createdObjects[] = $iriMap[$iri] = $this->cloneService->cloneBlock($block, $iriMap, $maxNumbers);
                $data->getParent()->addBlocks($iriMap[$iri]);
            }
        }

        foreach (array_keys($objectLeafs) as $iri) {
            $item = $iriMap[$iri];
            $item->setX($item->getX() + $data->getDx());
            $item->setY($item->getY() + $data->getDY());
            if (
                $item->getX() < 1 || $item->getY() < 1 || (
                    isset($positionMap[$item->getX() . ',' . $item->getY()]) &&
                    !isset($objectToMove[$this->iriConverter->getIriFromItem($positionMap[$item->getX() . ',' . $item->getY()])])
                )
            ) {
                throw new BadRequestHttpException();
            }
        }

        $this->entityManager->flush();
        $data->setId(0)->setCreatedObjects($createdObjects);

        return $data;
    }
}
