<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\PersonBundle\Service\AccompanyingPeriodWork;

use Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWork;
use Doctrine\ORM\EntityManagerInterface;

/**
 * Service for merging two AccompanyingPeriodWork entities into a single entity.
 */
readonly class AccompanyingPeriodWorkMergeService
{
    public function __construct(private EntityManagerInterface $em) {}

    /**
     * Merges two AccompanyingPeriodWork entities into one by transferring relevant data and removing the obsolete entity.
     *
     * @param AccompanyingPeriodWork $toKeep   the entity to retain after the merge
     * @param AccompanyingPeriodWork $toDelete the entity to be removed after transferring data
     *
     * @return AccompanyingPeriodWork the kept accompanying period work
     */
    public function merge(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): AccompanyingPeriodWork
    {
        $this->em->wrapInTransaction(function (EntityManagerInterface $entityManager) use ($toKeep, $toDelete) {
            $this->alterStartDate($toKeep, $toDelete);
            $this->alterEndDate($toKeep, $toDelete);
            $this->concatenateComments($toKeep, $toDelete);
            $this->transferEvaluationsSQL($toKeep, $toDelete);
            $this->transferWorkflowsSQL($toKeep, $toDelete);
            $this->updateReferences($toKeep, $toDelete);
            $entityManager->remove($toDelete);
        });

        return $toKeep;
    }

    private function transferWorkflowsSQL(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
    {
        $this->em->getConnection()->executeQuery(
            "UPDATE chill_main_workflow_entity w
                SET relatedentityid = :toKeepId
                WHERE w.relatedentityid = :toDeleteId
                AND w.relatedentityclass = 'Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWork'",
            ['toKeepId' => $toKeep->getId(), 'toDeleteId' => $toDelete->getId()]
        );
    }

    private function transferEvaluationsSQL(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
    {
        $this->em->getConnection()->executeQuery(
            'UPDATE chill_person_accompanying_period_work_evaluation cpapwe
                SET accompanyingperiodwork_id = :toKeepId
                WHERE cpapwe.accompanyingperiodwork_id = :toDeleteId',
            ['toKeepId' => $toKeep->getId(), 'toDeleteId' => $toDelete->getId()]
        );
    }

    private function alterStartDate(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
    {
        $startDate = min($toKeep->getStartDate(), $toDelete->getStartDate());
        $toKeep->setStartDate($startDate);
    }

    private function alterEndDate(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
    {
        if (null === $toKeep->getEndDate() || null === $toDelete->getEndDate()) {
            $toKeep->setEndDate(null);

            return;
        }

        $endDate = max($toKeep->getEndDate(), $toDelete->getEndDate());
        $toKeep->setEndDate($endDate);
    }

    private function concatenateComments(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
    {
        if ('' !== $toDelete->getNote()) {
            $toKeep->setNote($toKeep->getNote()."\n\n-----------------\n\n".$toDelete->getNote());
        }

        if (count($toDelete->getPrivateComment()->getComments()) > 0) {
            $toKeep->getPrivateComment()->concatenateComments($toDelete->getPrivateComment());
        }
    }

    private function updateReferences(AccompanyingPeriodWork $toKeep, AccompanyingPeriodWork $toDelete): void
    {
        foreach ($toDelete->getReferrers() as $referrer) {
            // we only keep the current referrer
            $toKeep->addReferrer($referrer);
        }

        foreach ($toDelete->getPersons() as $person) {
            $toKeep->addPerson($person);
        }

        if (null === $toKeep->getHandlingThierParty()) {
            $toKeep->setHandlingThierParty($toDelete->getHandlingThierParty());
        }

        foreach ($toDelete->getThirdParties() as $thirdParty) {
            $toKeep->addThirdParty($thirdParty);
        }

        foreach ($toDelete->getGoals() as $goal) {
            $toKeep->addGoal($goal);
        }

        foreach ($toDelete->getResults() as $result) {
            $toKeep->addResult($result);
        }
    }
}
