<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CoreBundle\MessageHandler;

use Doctrine\Common\Collections\Collection;
use Ferienpass\CoreBundle\Entity\OfferAttachment;
use Ferienpass\CoreBundle\Entity\OfferMedia;
use Ferienpass\CoreBundle\Message\SyncOfferVariants as SyncOfferVariantsCommand;
use Ferienpass\CoreBundle\Repository\OfferRepositoryInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;

#[AsMessageHandler(bus: 'command.bus')]
class SyncOfferVariants
{
    public function __construct(private readonly OfferRepositoryInterface $offers)
    {
    }

    public function __invoke(SyncOfferVariantsCommand $command): void
    {
        $origin = $this->offers->findOneBy(['uuid' => $command->getOfferId()]);
        if (null === $origin) {
            // The offer was deleted between dispatch and processing (e.g. a
            // variant-base offer removed after its media/attachment changes
            // enqueued a sync). Nothing to propagate.
            return;
        }

        $propertyAccessor = PropertyAccess::createPropertyAccessor();

        foreach ($origin->getVariants() as $variant) {
            foreach ($this->offers->variantFields() as $field) {
                if (\in_array($field, ['contact', 'uploadMedia'], true)) {
                    continue;
                }

                try {
                    $newValue = $propertyAccessor->getValue($origin, $field);
                } catch (NoSuchPropertyException) {
                    continue;
                }

                if ($newValue instanceof Collection) {
                    if ('media' === $field) {
                        /** @var Collection<int, OfferMedia> $currentMedia */
                        $currentMedia = $variant->getMedia();
                        /** @var Collection<int, OfferMedia> $newMedia */
                        $newMedia = $newValue;

                        $existingMap = [];
                        foreach ($currentMedia as $cm) {
                            $existingMap[$cm->getMedia()->getUuid()->toRfc4122()] = $cm;
                        }

                        $newUuids = [];
                        foreach ($newMedia as $nm) {
                            $mediaUuid = $nm->getMedia()->getUuid()->toRfc4122();
                            $newUuids[] = $mediaUuid;
                            if (isset($existingMap[$mediaUuid])) {
                                $existingMap[$mediaUuid]->setSorting($nm->getSorting());
                            } else {
                                $variant->addMedia(new OfferMedia($variant, $nm->getMedia()));
                            }
                        }

                        foreach ($existingMap as $uuid => $cm) {
                            if (!\in_array($uuid, $newUuids, true)) {
                                $variant->removeMedia($cm);
                            }
                        }
                    } elseif ('attachments' === $field) {
                        /** @var Collection<int, OfferAttachment> $currentAttachments */
                        $currentAttachments = $variant->getAttachments();
                        /** @var Collection<int, OfferAttachment> $newAttachments */
                        $newAttachments = $newValue;

                        $existingMap = [];
                        foreach ($currentAttachments as $ca) {
                            $existingMap[$ca->getAttachment()->getUuid()->toRfc4122()] = $ca;
                        }

                        $newUuids = [];
                        foreach ($newAttachments as $na) {
                            $attachmentUuid = $na->getAttachment()->getUuid()->toRfc4122();
                            $newUuids[] = $attachmentUuid;
                            if (isset($existingMap[$attachmentUuid])) {
                                $existingMap[$attachmentUuid]->setSorting($na->getSorting());
                                $existingMap[$attachmentUuid]->setAgreementLetter($na->isAgreementLetter());
                            } else {
                                $variant->addAttachment(new OfferAttachment($variant, $na->getAttachment()));
                            }
                        }

                        foreach ($existingMap as $uuid => $ca) {
                            if (!\in_array($uuid, $newUuids, true)) {
                                $variant->removeAttachment($ca);
                            }
                        }
                    } else {
                        /** @var Collection $variantCollection */
                        $variantCollection = $propertyAccessor->getValue($variant, $field);
                        $variantCollection->clear();
                        foreach ($newValue as $item) {
                            $variantCollection->add($item);
                        }
                    }
                } else {
                    $propertyAccessor->setValue($variant, $field, $newValue);
                }
            }
        }
    }
}
