<?php
/**
 * BookiFlex Notification Extension
 *
 * @package    BookiFlex
 * @subpackage Notification
 * @author     BookiFlex
 * @copyright  Copyright (c) 2025 BookiFlex
 * @license    GPL-2.0-or-later
 *
 * @link       https://bookiflex.com
 */


declare(strict_types=1);
namespace BookiFlex\Extensions\Notification\Service;

use BookiFlex\Extensions\Notification\Model\Notification;
use BookiFlex\Extensions\Notification\Model\NotificationStatus;
use BookiFlex\Extensions\Notification\Provider\NotificationContextProvider;
use BookiFlex\Extensions\Notification\Repository\NotificationRepository;
use Cycle\ORM\EntityManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;

defined( 'ABSPATH' ) || exit;

class NotificationHandler
{
    public function __construct(
        private readonly NotificationRepository $notificationRepository,
        private readonly NotificationContextProvider $notificationDataProvider,
        private readonly MailerInterface $mailer,
        private readonly EntityManagerInterface $entityManager,
        private readonly ?LoggerInterface $logger = null
    ) {
    }

    /**
     * Handle notifications by identifiers (for backward compatibility with tests).
     */
    public function handle(array $identifiers): void
    {
        $this->logger?->debug('Resolving notifications by identifiers', ['identifiers' => $identifiers]);
        $notifications = $this->getNotifications($identifiers);
        $this->handleNotifications($notifications);
    }

    /**
     * Send a batch of notification entities.
     *
     * @param Notification[] $notifications
     */
    public function handleNotifications(array $notifications): void
    {
        foreach ($notifications as $notification) {
            $this->logger?->debug('Processing notification', ['sid' => $notification->getSid()]);

            // Increment attempt counter (increments on both success and failure)
            $notification->incAttempts();

            try {
                $message = $this->getEmailTemplate($notification);
                $this->logger?->debug('Created email template', [
                    'to'           => $notification->getSendTo(),
                    'fromSnapshot' => $notification->hasSnapshot(),
                ]);

                $this->mailer->send($message);
                $this->logger?->debug('Email sent successfully', ['sid' => $notification->getSid()]);
                $notification->markAsSent();
            } catch (TransportExceptionInterface $e) {
                $this->logger?->error('Transport error sending email', ['exception' => $e, 'message' => $e->getMessage()]);
                if ($notification->getAttempts() >= Notification::MAX_ATTEMPTS) {
                    $notification->markAsFailed();
                }
            } catch (\Exception $e) {
                $this->logger?->error('Error sending email', ['exception' => $e, 'message' => $e->getMessage()]);
                if ($notification->getAttempts() >= Notification::MAX_ATTEMPTS) {
                    $notification->markAsFailed();
                }
            }

            $this->entityManager->persist($notification);
        }

        $this->entityManager->run();
    }

    public function getEmailTemplate(Notification $notification): TemplatedEmail
    {
        if ($notification->hasSnapshot()) {
            $snapshot = $notification->getSnapshot();
            $template = $snapshot['template'];
            $subject  = $snapshot['subject'];
            $context  = $snapshot['context'];
        } else {
            [$template, $subject, $context] = $this->buildContextOnTheFly($notification);
        }

        $from = new Address(Configuration::getMailFromEmail(), Configuration::getMailFromName());
        $email = (new TemplatedEmail())
            ->htmlTemplate($template)
            ->from($from)
            ->to($notification->getSendTo())
            ->priority(Email::PRIORITY_HIGH)
            ->subject($subject)
            ->context($context);

        // Add BCC if configured
        $bcc = Configuration::getMailBcc();
        if (!empty($bcc)) {
            $email->bcc($bcc);
        }

        return $email;
    }

    /**
     * Build template context on-the-fly (backward compatibility for notifications without snapshot).
     *
     * @return array{0: string, 1: string, 2: array} [template, subject, context]
     */
    private function buildContextOnTheFly(Notification $notification): array
    {
        $reservation = $notification->getReservation();
        $this->notificationDataProvider->setReservation($reservation, $notification->getEvent());

        $template = $this->notificationDataProvider->getTemplate($notification->getStakeholder());
        $subject  = $this->notificationDataProvider->getSubject($notification->getEvent(), $notification->getStakeholder());

        try {
            $context = $this->notificationDataProvider->getContext();
        } catch (\Throwable $exception) {
            throw new \RuntimeException($exception->getMessage());
        }

        return [$template, $subject, $context];
    }

    /**
     * @param array $identifiers
     * @return Notification[]
     */
    private function getNotifications(array $identifiers): array
    {
        return $this->notificationRepository->findAll(['sid' => ['in' => $identifiers], 'status' => NotificationStatus::PENDING]);
    }
}
