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

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\Comment\CommentNotifier;
use Ferienpass\CoreBundle\Comment\InboundAttachmentIngest;
use Ferienpass\CoreBundle\Comment\ReplyTokenSigner;
use Ferienpass\CoreBundle\Entity\ConversationItem;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Repository\ConversationItemRepository;
use Ferienpass\CoreBundle\Repository\UserRepository;
use Psr\Log\LoggerInterface;
use Symfony\Component\RemoteEvent\Attribute\AsRemoteEventConsumer;
use Symfony\Component\RemoteEvent\Consumer\ConsumerInterface;
use Symfony\Component\RemoteEvent\RemoteEvent;

/**
 * Turns an inbound email (parsed by {@see \Ferienpass\CoreBundle\Webhook\BrevoInboundRequestParser})
 * into a threaded `email`-channel reply on the conversation it answers, then
 * alerts the colleagues taking part.
 *
 * Runs synchronously inside the webhook request (ConsumeRemoteEventMessage is not
 * routed to async here), so it stays bound to the tenant's database connection —
 * the same reason the outbound side carries tenant context explicitly.
 */
#[AsRemoteEventConsumer('brevo_inbound')]
final readonly class InboundEmailConsumer implements ConsumerInterface
{
    public function __construct(
        private ReplyTokenSigner $tokenSigner,
        private ConversationItemRepository $items,
        private UserRepository $users,
        private CommentNotifier $notifier,
        private InboundAttachmentIngest $attachments,
        private EntityManagerInterface $em,
        private LoggerInterface $webhookLogger,
    ) {
    }

    public function consume(RemoteEvent $event): void
    {
        $payload = $event->getPayload();
        $from = $payload['fromEmail'] ?? '?';

        $itemUuid = $this->tokenSigner->verify((string) ($payload['token'] ?? ''));
        if (null === $itemUuid) {
            $this->webhookLogger->warning('Inbound reply rejected: invalid or expired token.', ['from' => $from]);

            return;
        }

        $parent = $this->items->find($itemUuid);
        if (!$parent instanceof ConversationItem) {
            $this->webhookLogger->warning('Inbound reply rejected: no conversation for token.', ['from' => $from, 'item' => $itemUuid->toBase58()]);

            return;
        }

        $text = trim((string) ($payload['text'] ?? ''));
        $attachments = \is_array($payload['attachments'] ?? null) ? $payload['attachments'] : [];
        if ('' === $text && [] === $attachments) {
            $this->webhookLogger->info('Inbound reply ignored: empty body, no attachments.', ['from' => $from]);

            return;
        }

        $fromEmail = $payload['fromEmail'] ?? null;
        $author = null !== $fromEmail ? $this->users->findOneBy(['email' => $fromEmail]) : null;

        // Always record the raw From (address + display-name) for provenance, and
        // link the account when the sender matches one. getAuthorLabel() prefers
        // the linked account's name, so the displayed name is unchanged; the raw
        // From stays available (e.g. the mail-icon tooltip, audit).
        $reply = new ConversationItem(
            $parent->getSubject(),
            $author instanceof User ? $author : null,
            '' !== $text ? $text : '(Dateianhang)',
            channel: ConversationItem::CHANNEL_EMAIL,
            parent: $parent,
            authorEmail: $fromEmail,
            authorName: $payload['fromName'] ?? null,
        );

        $this->em->persist($reply);

        if ([] !== $attachments) {
            $result = $this->attachments->ingest($reply, $attachments);
            if ($result['blocked'] > 0) {
                $this->webhookLogger->warning('Inbound reply had blocked attachments.', $result);
            }
        }

        // notifyEmailReply() flushes, committing the reply, its stored
        // attachments and the colleague notifications together.
        $this->notifier->notifyEmailReply($reply);

        $this->webhookLogger->info('Inbound reply threaded.', ['from' => $from, 'item' => $parent->getUuid()->toBase58()]);
    }
}
