<?php

namespace Webapp\Core\Dto\Experiment\ExportOpenSilex;

use ApiPlatform\Core\Api\IriConverterInterface;
use ApiPlatform\Core\Api\OperationType;
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Query\Expr\Join;
use Webapp\Core\Entity\Block;
use Webapp\Core\Entity\Experiment;
use Webapp\Core\Entity\Individual;
use Webapp\Core\Entity\SubBlock;
use Webapp\Core\Entity\SurfacicUnitPlot;
use Webapp\Core\Entity\UnitPlot;

class ExperimentOpenSilexScientificObjectsExportTransformer implements DataTransformerInterface
{
    private IriConverterInterface $iriConverter;
    private EntityManagerInterface $entityManager;

    public function __construct(IriConverterInterface $iriConverter, EntityManagerInterface $entityManager)
    {
        $this->iriConverter = $iriConverter;
        $this->entityManager = $entityManager;
    }

    /**
     * @param object $object
     * @param string $to
     * @param array $context
     * @return array
     */
    public function transform($object, string $to, array $context = []): array
    {
        /** @var Experiment $object */
        $qb = $this->entityManager->createQueryBuilder();
        $blocks = $this->entityManager->getRepository(Block::class)->createQueryBuilder('blocks')
            ->innerJoin('blocks.experiment', 'ex2', Join::WITH, 'blocks.experiment = ex2.id')
            ->andWhere('ex2 = :experiment')
            ->setParameter('experiment', $object)
            ->getQuery()
            ->getResult();
        $subBlocks = $this->entityManager->getRepository(SubBlock::class)->createQueryBuilder('subBlocks')
            ->innerJoin('subBlocks.block', 'bl2', Join::WITH, 'subBlocks.block = bl2.id')
            ->andWhere($qb->expr()->in('bl2', ':blocks'))
            ->setParameter('blocks', $blocks)
            ->getQuery()
            ->getResult();
        $unitPlots = $this->entityManager->getRepository(UnitPlot::class)->createQueryBuilder('unitPlot')
            ->leftJoin('unitPlot.block', 'bl2', Join::WITH, 'unitPlot.block = bl2.id')
            ->leftJoin('unitPlot.subBlock', 'sb2', Join::WITH, 'unitPlot.subBlock = sb2.id')
            ->andWhere(
                $qb->expr()->orX(
                    $qb->expr()->in('bl2', ':blocks'),
                    $qb->expr()->in('sb2', ':subBlocks')
                )
            )
            ->setParameter('blocks', $blocks)
            ->setParameter('subBlocks', $subBlocks)
            ->getQuery()
            ->getResult();
        $surfacicUnitPlots = $this->entityManager->getRepository(SurfacicUnitPlot::class)->createQueryBuilder('surfacicUnitPlot')
            ->leftJoin('surfacicUnitPlot.block', 'bl2', Join::WITH, 'surfacicUnitPlot.block = bl2.id')
            ->leftJoin('surfacicUnitPlot.subBlock', 'sb2', Join::WITH, 'surfacicUnitPlot.subBlock = sb2.id')
            ->andWhere(
                $qb->expr()->orX(
                    $qb->expr()->in('bl2', ':blocks'),
                    $qb->expr()->in('sb2', ':subBlocks')
                )
            )
            ->setParameter('blocks', $blocks)
            ->setParameter('subBlocks', $subBlocks)
            ->getQuery()
            ->getResult();
        $individuals = $this->entityManager->getRepository(Individual::class)->createQueryBuilder('individuals')
            ->innerJoin('individuals.unitPlot', 'up2', Join::WITH, 'individuals.unitPlot = up2.id')
            ->andWhere($qb->expr()->in('up2', ':unitPlots'))
            ->setParameter('unitPlots', $unitPlots)
            ->getQuery()
            ->getResult();


        return array_merge(
            [(new ExperimentExportOpenSilexScientificObject())
                ->setNumber("Dispositif " . $object->getName())
                ->setGeometry($object->getGeometry())
                ->setIri($this->iriConverter->getIriFromItem($object))],
            array_map(fn(Block $block) => (new ExperimentExportOpenSilexScientificObject())
                ->setNumber("Bloc " . $block->getNumber())
                ->setGeometry($block->getGeometry())
                ->setIri($this->iriConverter->getIriFromItem($block))
                ->setParent($this->iriConverter->getIriFromItem($block->parent())),
                $blocks),
            array_map(fn(SubBlock $subBlock) => (new ExperimentExportOpenSilexScientificObject())
                ->setNumber("Sous-bloc " . $subBlock->getNumber())
                ->setGeometry($subBlock->getGeometry())
                ->setIri($this->iriConverter->getIriFromItem($subBlock))
                ->setParent($this->iriConverter->getIriFromItem($subBlock->parent())),
                $subBlocks),
            array_map(fn(SurfacicUnitPlot $surfacicUnitPlot) => (new ExperimentExportOpenSilexScientificObject())
                ->setAppeared($surfacicUnitPlot->getAppeared())
                ->setDisappeared($surfacicUnitPlot->getDisappeared())
                ->setGeometry($surfacicUnitPlot->getGeometry())
                ->setTreatment($this->iriConverter->getIriFromItem($surfacicUnitPlot->getTreatment()))
                ->setNumber("Parcelle surfacique " . $surfacicUnitPlot->getNumber())
                ->setIri($this->iriConverter->getIriFromItem($surfacicUnitPlot))
                ->setParent($this->iriConverter->getIriFromItem($surfacicUnitPlot->parent())),
                $surfacicUnitPlots),
            array_map(fn(UnitPlot $unitPlot) => (new ExperimentExportOpenSilexScientificObject())
                ->setTreatment($this->iriConverter->getIriFromItem($unitPlot->getTreatment()))
                ->setGeometry($unitPlot->getGeometry())
                ->setNumber("Parcelle unitaire " . $unitPlot->getNumber())
                ->setIri($this->iriConverter->getIriFromItem($unitPlot))
                ->setParent($this->iriConverter->getIriFromItem($unitPlot->parent())),
                $unitPlots),
            array_map(fn(Individual $individual) => (new ExperimentExportOpenSilexScientificObject())
                ->setAppeared($individual->getAppeared())
                ->setGeometry($individual->getGeometry())
                ->setDisappeared($individual->getDisappeared())
                ->setNumber("Individu " . $individual->getNumber())
                ->setIri($this->iriConverter->getIriFromItem($individual))
                ->setParent($this->iriConverter->getIriFromItem($individual->parent())),
                $individuals)
        );
    }

    public function supportsTransformation($data, string $to, array $context = []): bool
    {
        if ($data instanceof ExperimentExportOpenSilexScientificObject) {
            return false;
        }
        return ExperimentExportOpenSilexScientificObject::class === $to &&
            $context['operation_type'] === OperationType::ITEM &&
            $context['item_operation_name'] === 'exportOpenSilexScientificObjects';
    }
}
