<?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\AdminBundle\Components;

use Ferienpass\AdminBundle\Message\HostInvited;
use Ferienpass\AdminBundle\Message\LoginLinkHelpRequested as AdminLoginLinkHelpRequested;
use Ferienpass\AdminBundle\Message\LoginLinkRequested as AdminLoginLinkRequested;
use Ferienpass\AdminBundle\Message\PasswordLinkCreated as AdminPasswordLinkCreated;
use Ferienpass\AdminBundle\Message\PasswordResetHelpRequested as AdminPasswordResetHelpRequested;
use Ferienpass\AdminBundle\Message\PasswordResetUnavailable as AdminPasswordResetUnavailable;
use Ferienpass\CmsBundle\Message\LoginLinkHelpRequested as CmsLoginLinkHelpRequested;
use Ferienpass\CmsBundle\Message\LoginLinkRequested as CmsLoginLinkRequested;
use Ferienpass\CmsBundle\Message\PasswordLinkCreated as CmsPasswordLinkCreated;
use Ferienpass\CmsBundle\Message\PasswordResetHelpRequested as CmsPasswordResetHelpRequested;
use Ferienpass\CmsBundle\Message\PasswordResetUnavailable as CmsPasswordResetUnavailable;
use Ferienpass\CoreBundle\Message\AccountActivated;
use Ferienpass\CoreBundle\Message\AccountAlreadyExists;
use Ferienpass\CoreBundle\Message\AccountCreated;
use Ferienpass\CoreBundle\Message\AccountDisabled;
use Ferienpass\CoreBundle\Message\AnonymousAttendanceUnconfirmed;
use Ferienpass\CoreBundle\Message\ApplicationsClosed;
use Ferienpass\CoreBundle\Message\AttendanceCompletionRequested;
use Ferienpass\CoreBundle\Message\AttendanceCreated;
use Ferienpass\CoreBundle\Message\AttendanceStatusChanged;
use Ferienpass\CoreBundle\Message\AttendanceWithdrawRequested;
use Ferienpass\CoreBundle\Message\CommentPosted;
use Ferienpass\CoreBundle\Message\DecisionLetterDispatched;
use Ferienpass\CoreBundle\Message\ExportFinished;
use Ferienpass\CoreBundle\Message\HostCreated;
use Ferienpass\CoreBundle\Message\OfferCancelled;
use Ferienpass\CoreBundle\Message\OfferCompleted;
use Ferienpass\CoreBundle\Message\OfferCompleteNotify;
use Ferienpass\CoreBundle\Message\OfferFinalized;
use Ferienpass\CoreBundle\Message\OfferRelaunched;
use Ferienpass\CoreBundle\Message\PaymentReceiptCreated;
use Ferienpass\CoreBundle\Message\RemindAttendance;
use Ferienpass\CoreBundle\Message\SendAttendanceChanges;
use Ferienpass\CoreBundle\Message\SendParticipantList;

/**
 * Who a transactional e-mail is addressed to.
 *
 * This is the only meaning the Postausgang's type dots carry: the colour is the
 * audience, nothing else. It is also what tells the identically named admin/cms
 * twins apart — the five login and password messages exist once per app, and
 * only their namespace says whether the mail went to a family or to a host.
 */
enum OutboxAudience: string
{
    /** Parents and participants: accounts that live on the public site. */
    case Family = 'family';

    /** Hosts and the organising team: accounts that sign in to /admin. */
    case Host = 'host';

    /** Fans out along a conversation, so it can reach either side. */
    case Mixed = 'mixed';

    /**
     * Every message class that can produce a transactional e-mail, i.e. every
     * one a NotificationHandler listens to. SendMailing is absent on purpose:
     * a Rundmail is a campaign and keeps its own brand colour.
     *
     * Kept exhaustive by OutboxAudienceTest, which walks the handlers.
     */
    private const MAP = [
        // ── Eltern & Familien ──
        CmsLoginLinkRequested::class => self::Family,
        CmsLoginLinkHelpRequested::class => self::Family,
        CmsPasswordLinkCreated::class => self::Family,
        CmsPasswordResetHelpRequested::class => self::Family,
        CmsPasswordResetUnavailable::class => self::Family,
        AccountCreated::class => self::Family,
        AccountActivated::class => self::Family,
        AccountDisabled::class => self::Family,
        AccountAlreadyExists::class => self::Family,
        AttendanceCreated::class => self::Family,
        AttendanceStatusChanged::class => self::Family,
        AttendanceCompletionRequested::class => self::Family,
        AttendanceWithdrawRequested::class => self::Family,
        AnonymousAttendanceUnconfirmed::class => self::Family,
        RemindAttendance::class => self::Family,
        DecisionLetterDispatched::class => self::Family,
        OfferCancelled::class => self::Family,
        OfferRelaunched::class => self::Family,
        PaymentReceiptCreated::class => self::Family,

        // ── Veranstalter & Verwaltung ──
        AdminLoginLinkRequested::class => self::Host,
        AdminLoginLinkHelpRequested::class => self::Host,
        AdminPasswordLinkCreated::class => self::Host,
        AdminPasswordResetHelpRequested::class => self::Host,
        AdminPasswordResetUnavailable::class => self::Host,
        HostInvited::class => self::Host,
        HostCreated::class => self::Host,
        OfferFinalized::class => self::Host,
        OfferCompleted::class => self::Host,
        OfferCompleteNotify::class => self::Host,
        ApplicationsClosed::class => self::Host,
        SendParticipantList::class => self::Host,
        SendAttendanceChanges::class => self::Host,
        ExportFinished::class => self::Host,

        // ── Gemischt ──
        CommentPosted::class => self::Mixed,
    ];

    public static function of(string $messageClass): ?self
    {
        return self::MAP[$messageClass] ?? null;
    }

    public function color(): string
    {
        return match ($this) {
            self::Family => '#2a5eaa',
            self::Host => '#2f6f57',
            self::Mixed => '#54514b',
        };
    }

    /**
     * The audiences among the given message classes, in a fixed display order so
     * the legend does not reshuffle as the filters change.
     *
     * @param string[] $messageClasses
     *
     * @return self[]
     */
    public static function among(array $messageClasses): array
    {
        $present = [];
        foreach ($messageClasses as $messageClass) {
            $audience = self::of($messageClass);
            if (null !== $audience) {
                $present[$audience->value] = $audience;
            }
        }

        return array_values(array_filter(self::cases(), static fn (self $c) => isset($present[$c->value])));
    }
}
