<?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\Notification;

use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Contao\CoreBundle\Framework\ContaoFramework;
use Ferienpass\CoreBundle\Entity\Attendance;
use Ferienpass\CoreBundle\Entity\Offer\Base;
use Ferienpass\CoreBundle\Export\Offer\ICal\ICalExport;
use Ferienpass\CoreBundle\Notifier\Message\EmailMessage;
use Ferienpass\CoreBundle\Notifier\Mime\NotificationEmail;
use Symfony\Component\Notifier\Message\EmailMessage as SymfonyEmailMessage;
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\Notification\EmailNotificationInterface;
use Symfony\Component\Notifier\Notification\SmsNotificationInterface;
use Symfony\Component\Notifier\Recipient\EmailRecipientInterface;
use Symfony\Component\Notifier\Recipient\RecipientInterface;
use Symfony\Component\Notifier\Recipient\SmsRecipientInterface;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

class RemindAttendanceNotification extends AbstractNotification implements NotificationInterface, EditionAwareNotificationInterface, EmailNotificationInterface, SmsNotificationInterface
{
    /** @var list<Attendance> */
    private array $attendances = [];

    public function __construct(private readonly ICalExport $iCalExport, private readonly EventDispatcherInterface $dispatcher, private readonly ContaoFramework $contaoFramework, private readonly VirtualFilesystemInterface $attachmentsStorage)
    {
        parent::__construct();
    }

    public static function getName(): string
    {
        return 'remind_attendance';
    }

    public function getChannels(RecipientInterface $recipient): array
    {
        $channels = [];

        if ($recipient instanceof EmailRecipientInterface && $recipient->getEmail()) {
            $channels[] = 'email';
        }

        if ($recipient instanceof SmsRecipientInterface && $this->getSmsText() && $recipient->getPhone()) {
            $channels[] = 'sms';
        }

        return $channels;
    }

    public function attendance(Attendance $attendance): static
    {
        $this->attendances[] = $attendance;

        return $this;
    }

    public function getContext(): array
    {
        // The `attendances` token lists every offer in the reminder for the
        // combined layout.
        //
        // BC — TO BE REMOVED IN A FUTURE VERSION: the singular
        // `attendance`/`offer`/`participant` tokens, bound to the first
        // attendance, keep admin-customised templates written before reminders
        // were combined rendering. Drop them once those templates have migrated
        // to the `attendances` token (also drop them from getAvailableTokens()).
        $first = $this->attendances[0] ?? null;

        return array_merge(parent::getContext(), [
            'attendances' => $this->attendances,
            'attendance' => $first,
            'offer' => $first?->getOffer(),
            'participant' => $first?->getParticipant(),
            'offer_fee' => null !== $first ? $first->getOffer()->getFeePayable($first->getParticipant(), $this->dispatcher) : null,
        ]);
    }

    public static function getAvailableTokens(): array
    {
        return array_merge(parent::getAvailableTokens(), ['attendance', 'offer', 'participant', 'attendances']);
    }

    public function asEmailMessage(EmailRecipientInterface $recipient, ?string $transport = null): ?SymfonyEmailMessage
    {
        $this->contaoFramework->initialize();

        return EmailMessage::fromFerienpassNotification($this, $recipient, function (NotificationEmail $email) {
            $seen = [];
            foreach ($this->attendances as $attendance) {
                /** @var Base $offer */
                $offer = $attendance->getOffer();

                // Two children in the same offer share one email; attach each
                // offer's iCal and agreement letter only once.
                if (isset($seen[$offer->getId()])) {
                    continue;
                }
                $seen[$offer->getId()] = true;

                $email->attach($this->iCalExport->generate([$offer], stream: true), \sprintf('%s.ics', $offer->getAlias()));

                if ($offer->requiresAgreementLetter() && ($attachment = $offer->getAgreementLetter()) && ($file = $this->attachmentsStorage->get($attachment->getAttachment()->getUuid()))) {
                    $email->attach($this->attachmentsStorage->readStream($attachment->getAttachment()->getUuid()), $file->getName());
                }
            }
        });
    }

    public function asSmsMessage(SmsRecipientInterface $recipient, ?string $transport = null): ?SmsMessage
    {
        return new SmsMessage($recipient->getPhone(), $this->getSmsText());
    }
}
