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

use Contao\CoreBundle\Filesystem\FilesystemItem;
use Contao\CoreBundle\Filesystem\VirtualFilesystemInterface;
use Ferienpass\CoreBundle\Entity\DbafsLogo;
use Ferienpass\CoreBundle\Entity\PageConfig;
use Ferienpass\CoreBundle\EventListener\EmailLogoListener;
use Ferienpass\CoreBundle\Settings\PageConfigManager;
use PHPUnit\Framework\TestCase;
use Psr\Log\NullLogger;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Uid\Uuid;

/**
 * The logo is the one piece of branding a worker cannot derive from the request
 * or the kernel, so it has to arrive with the message.
 */
class EmailLogoListenerTest extends TestCase
{
    public function testEmbedsTheTenantsLogoAndExposesItToTheTemplate(): void
    {
        $email = new TemplatedEmail();

        $this->listener($this->pageConfigWithLogo())($this->event($email));

        $this->assertSame('cid:'.EmailLogoListener::CONTENT_ID, $email->getContext()['logoSrc']);

        $parts = $email->getAttachments();
        $this->assertCount(1, $parts);
        $this->assertSame(EmailLogoListener::CONTENT_ID, $parts[0]->getContentId());
        // Inline, not an attachment — otherwise the mail shows a paperclip
        // instead of a logo.
        $this->assertSame('inline', $parts[0]->getPreparedHeaders()->getHeaderBody('Content-Disposition'));
    }

    public function testDoesNothingWhenTheTenantHasNoLogo(): void
    {
        $email = new TemplatedEmail();

        $this->listener(new PageConfig())($this->event($email));

        $this->assertArrayNotHasKey('logoSrc', $email->getContext());
        $this->assertCount(0, $email->getAttachments());
    }

    /**
     * A template (or an app override) that decides on the logo itself keeps
     * that decision, and a second pass over the same message must not attach a
     * second copy.
     */
    public function testLeavesAnExplicitLogoSrcAlone(): void
    {
        $email = new TemplatedEmail();
        $email->context(['logoSrc' => 'cid:from-the-app']);

        $this->listener($this->pageConfigWithLogo())($this->event($email));

        $this->assertSame('cid:from-the-app', $email->getContext()['logoSrc']);
        $this->assertCount(0, $email->getAttachments());
    }

    public function testIgnoresAPlainEmail(): void
    {
        $this->expectNotToPerformAssertions();

        $this->listener($this->pageConfigWithLogo())($this->event(new Email()));
    }

    private function pageConfigWithLogo(): PageConfig
    {
        $logo = new DbafsLogo();

        $reflection = new \ReflectionProperty(DbafsLogo::class, 'uuid');
        $reflection->setValue($logo, Uuid::v7());

        $config = new PageConfig();
        $config->setLogo($logo);

        return $config;
    }

    private function listener(PageConfig $pageConfig): EmailLogoListener
    {
        $pageConfigManager = $this->createStub(PageConfigManager::class);
        $pageConfigManager->method('getPageConfig')->willReturn($pageConfig);

        $item = $this->createStub(FilesystemItem::class);
        $item->method('getName')->willReturn('logo.png');
        $item->method('getMimeType')->willReturn('image/png');

        $storage = $this->createStub(VirtualFilesystemInterface::class);
        $storage->method('get')->willReturn($item);
        $storage->method('readStream')->willReturnCallback(static function () {
            $stream = fopen('php://memory', 'rb+');
            fwrite($stream, 'PNG');
            rewind($stream);

            return $stream;
        });

        return new EmailLogoListener($pageConfigManager, $storage, new NullLogger());
    }

    private function event(Email $email): MessageEvent
    {
        return new MessageEvent($email, new Envelope(new Address('from@example.com'), [new Address('to@example.com')]), 'smtp');
    }
}
