<?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\Tests\MessageHandler;

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Message\DispatchMailing as DispatchMailingCommand;
use Ferienpass\CoreBundle\Message\MailingCopyMode;
use Ferienpass\CoreBundle\Message\SendMailing as SendMailingCommand;
use Ferienpass\CoreBundle\MessageHandler\DispatchMailing;
use Ferienpass\CoreBundle\Repository\AttendanceRepository;
use Ferienpass\CoreBundle\Repository\UserRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Uid\Uuid;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

final class DispatchMailingTest extends TestCase
{
    public function testDoesNotDispatchWhenEmailListIsEmpty(): void
    {
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects(self::never())->method('dispatch');

        $handler = new DispatchMailing(
            $bus,
            $this->createMock(AttendanceRepository::class),
            $this->createMock(EventDispatcherInterface::class),
            $this->createMock(EntityManagerInterface::class),
            $this->createMock(UserRepository::class),
        );

        $handler(new DispatchMailingCommand(null, [], 'Subject', 'Content', []), Uuid::v7());
    }

    public function testSkipsEmptyStringEmails(): void
    {
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects(self::never())->method('dispatch');

        $handler = new DispatchMailing(
            $bus,
            $this->createMock(AttendanceRepository::class),
            $this->createMock(EventDispatcherInterface::class),
            $this->createMock(EntityManagerInterface::class),
            $this->createMock(UserRepository::class),
        );

        $handler(new DispatchMailingCommand(null, ['', ''], 'Subject', 'Content', []), Uuid::v7());
    }

    public function testDispatchesNonPersonalizedCopyToInvokingUser(): void
    {
        $user = new User();
        $user->setEmail('admin@example.com');

        $userRepository = $this->createMock(UserRepository::class);
        $userRepository->method('findOneBy')->willReturn($user);

        $dispatched = [];
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->method('dispatch')->willReturnCallback(static function ($message) use (&$dispatched) {
            $envelope = $message instanceof Envelope ? $message : new Envelope($message);
            $dispatched[] = $envelope->getMessage();

            return $envelope;
        });

        $handler = new DispatchMailing(
            $bus,
            $this->createMock(AttendanceRepository::class),
            $this->createMock(EventDispatcherInterface::class),
            $this->createMock(EntityManagerInterface::class),
            $userRepository,
        );

        $handler(new DispatchMailingCommand('user-uuid', ['recipient@example.com'], 'Subject', 'Content', [], copyMode: MailingCopyMode::Copy), Uuid::v7());

        self::assertCount(2, $dispatched);

        // The real recipient is delivered a personalized mailing, no BCC.
        self::assertInstanceOf(SendMailingCommand::class, $dispatched[0]);
        self::assertSame('recipient@example.com', $dispatched[0]->getEmail());
        self::assertTrue($dispatched[0]->isPersonalized());
        self::assertNull($dispatched[0]->getBcc());

        // The sender's copy goes to their own address, non-personalized.
        self::assertInstanceOf(SendMailingCommand::class, $dispatched[1]);
        self::assertSame('admin@example.com', $dispatched[1]->getEmail());
        self::assertFalse($dispatched[1]->isPersonalized());
    }

    public function testBccModeBlindCopiesTheSenderOnEachMessageWithoutASeparateCopy(): void
    {
        $user = new User();
        $user->setEmail('admin@example.com');

        $userRepository = $this->createMock(UserRepository::class);
        $userRepository->method('findOneBy')->willReturn($user);

        $dispatched = [];
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->method('dispatch')->willReturnCallback(static function ($message) use (&$dispatched) {
            $envelope = $message instanceof Envelope ? $message : new Envelope($message);
            $dispatched[] = $envelope->getMessage();

            return $envelope;
        });

        $handler = new DispatchMailing(
            $bus,
            $this->createMock(AttendanceRepository::class),
            $this->createMock(EventDispatcherInterface::class),
            $this->createMock(EntityManagerInterface::class),
            $userRepository,
        );

        $handler(new DispatchMailingCommand('user-uuid', ['a@example.com', 'b@example.com'], 'Subject', 'Content', [], copyMode: MailingCopyMode::Bcc), Uuid::v7());

        // One personalized message per recipient — no separate copy — each
        // blind-copying the sender.
        self::assertCount(2, $dispatched);
        foreach ($dispatched as $message) {
            self::assertTrue($message->isPersonalized());
            self::assertSame('admin@example.com', $message->getBcc());
        }
        self::assertSame(['a@example.com', 'b@example.com'], array_map(static fn ($m) => $m->getEmail(), $dispatched));
    }

    public function testDoesNotDispatchCopyWhenInvokingUserIsUnknown(): void
    {
        $userRepository = $this->createMock(UserRepository::class);
        $userRepository->method('findOneBy')->willReturn(null);

        $dispatched = [];
        $bus = $this->createMock(MessageBusInterface::class);
        $bus->method('dispatch')->willReturnCallback(static function ($message) use (&$dispatched) {
            $envelope = $message instanceof Envelope ? $message : new Envelope($message);
            $dispatched[] = $envelope->getMessage();

            return $envelope;
        });

        $handler = new DispatchMailing(
            $bus,
            $this->createMock(AttendanceRepository::class),
            $this->createMock(EventDispatcherInterface::class),
            $this->createMock(EntityManagerInterface::class),
            $userRepository,
        );

        $handler(new DispatchMailingCommand('missing-uuid', ['recipient@example.com'], 'Subject', 'Content', [], copyMode: MailingCopyMode::Copy), Uuid::v7());

        // Only the real recipient — no copy without a resolvable sender address.
        self::assertCount(1, $dispatched);
        self::assertSame('recipient@example.com', $dispatched[0]->getEmail());
        self::assertNull($dispatched[0]->getBcc());
    }
}
