<?php

declare(strict_types=1);

namespace App\Module\Mail\Infrastructure\Controller;

use App\Module\Mail\Domain\Exception\MailProviderException;
use App\Module\Mail\Domain\Provider\MailProviderInterface;
use App\Module\Mail\Domain\Repository\MailMessageRepositoryInterface;
use App\Module\Mail\Infrastructure\Security\MailAccessChecker;
use DateTimeInterface;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;

#[Route('/api/mail/messages/{id}', name: 'mail_message_detail', methods: ['GET'], priority: 1, requirements: ['id' => '\d+'])]
#[IsGranted('IS_AUTHENTICATED_FULLY')]
class MailMessageDetailController extends AbstractController
{
    public function __construct(
        private readonly MailMessageRepositoryInterface $messageRepository,
        private readonly MailProviderInterface $mailProvider,
        private readonly MailAccessChecker $accessChecker,
        private readonly CacheItemPoolInterface $cache,
    ) {}

    public function __invoke(int $id): JsonResponse
    {
        $this->accessChecker->ensureCanAccessMail($this->getUser());

        $message = $this->messageRepository->findById($id);
        if (null === $message) {
            throw new NotFoundHttpException('Message not found');
        }

        $cacheKey = 'mail_body_'.md5($message->getMessageId());
        $item     = $this->cache->getItem($cacheKey);

        if (!$item->isHit()) {
            try {
                $detail = $this->mailProvider->fetchMessage(
                    $message->getFolder()->getPath(),
                    $message->getUid()
                );
                $item->set($detail);
                $item->expiresAfter(300);
                $this->cache->save($item);
            } catch (MailProviderException) {
                throw new ServiceUnavailableHttpException(null, 'IMAP unavailable: could not fetch message body');
            }
        }

        $detail = $item->get();

        $messageId   = $message->getId();
        $attachments = array_map(static fn ($att) => [
            'partNumber' => $att->partNumber,
            'filename'   => $att->filename,
            'mimeType'   => $att->mimeType,
            'size'       => $att->size,
            'downloadId' => rtrim(strtr(base64_encode($messageId.':'.$att->partNumber), '+/', '-_'), '='),
        ], $detail->attachments);

        return $this->json([
            'id'             => $message->getId(),
            'messageId'      => $message->getMessageId(),
            'uid'            => $message->getUid(),
            'folderPath'     => $message->getFolder()->getPath(),
            'subject'        => $detail->header->subject,
            'fromAddress'    => $detail->header->fromAddress,
            'fromName'       => $detail->header->fromName,
            'toAddresses'    => $detail->header->toAddresses,
            'ccAddresses'    => $detail->header->ccAddresses,
            'sentAt'         => $detail->header->sentAt->format(DateTimeInterface::ATOM),
            'isRead'         => $message->isRead(),
            'isFlagged'      => $message->isFlagged(),
            'hasAttachments' => $message->hasAttachments(),
            'bodyHtml'       => $detail->bodyHtml,
            'bodyText'       => $detail->bodyText,
            'attachments'    => $attachments,
        ]);
    }
}
