<?php

declare(strict_types=1);

namespace App\Tests\Unit\Administration\Infrastructure\Messaging;

use App\Administration\Domain\Event\PasswordResetTokenGenerated;
use App\Administration\Domain\Model\PasswordResetToken\PasswordResetTokenId;
use App\Administration\Infrastructure\Messaging\SendPasswordResetEmailHandler;
use App\Shared\Domain\Tenant\TenantId;
use DateTimeImmutable;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Twig\Environment;

/**
 * Tests for SendPasswordResetEmailHandler.
 *
 * Key invariants:
 * - Sends email to the user's address with a reset link
 * - Reset URL is constructed from appUrl + token value
 * - Uses the correct Twig template
 */
final class SendPasswordResetEmailHandlerTest extends TestCase
{
    private const string APP_URL = 'https://ecole-alpha.classeo.fr';
    private const string FROM_EMAIL = 'noreply@classeo.fr';
    private const string TOKEN_VALUE = 'abc123def456';

    private MailerInterface $mailer;
    private Environment $twig;

    protected function setUp(): void
    {
        $this->mailer = $this->createMock(MailerInterface::class);
        $this->twig = $this->createMock(Environment::class);
    }

    #[Test]
    public function sendsResetEmailToUser(): void
    {
        $event = $this->createEvent('user@example.com');

        $this->twig->method('render')->willReturn('<p>Reset your password</p>');

        $sentEmail = null;
        $this->mailer->expects(self::once())
            ->method('send')
            ->with(self::callback(static function (Email $email) use (&$sentEmail): bool {
                $sentEmail = $email;

                return true;
            }));

        $handler = new SendPasswordResetEmailHandler(
            $this->mailer,
            $this->twig,
            self::APP_URL,
            self::FROM_EMAIL,
        );

        ($handler)($event);

        self::assertNotNull($sentEmail);
        self::assertSame('user@example.com', $sentEmail->getTo()[0]->getAddress());
        self::assertSame(self::FROM_EMAIL, $sentEmail->getFrom()[0]->getAddress());
        self::assertStringContainsString('initialisation', $sentEmail->getSubject());
    }

    #[Test]
    public function rendersTemplateWithResetUrl(): void
    {
        $event = $this->createEvent('user@example.com');

        $this->twig->expects(self::once())
            ->method('render')
            ->with(
                'emails/password_reset.html.twig',
                self::callback(static function (array $vars): bool {
                    return $vars['email'] === 'user@example.com'
                        && $vars['resetUrl'] === 'https://ecole-alpha.classeo.fr/reset-password/' . self::TOKEN_VALUE;
                }),
            )
            ->willReturn('<p>HTML</p>');

        $this->mailer->method('send');

        $handler = new SendPasswordResetEmailHandler(
            $this->mailer,
            $this->twig,
            self::APP_URL,
            self::FROM_EMAIL,
        );

        ($handler)($event);
    }

    #[Test]
    public function handlesTrailingSlashInAppUrl(): void
    {
        $event = $this->createEvent('user@example.com');

        $this->twig->expects(self::once())
            ->method('render')
            ->with(
                self::anything(),
                self::callback(static function (array $vars): bool {
                    // Should not have double slash
                    return str_contains($vars['resetUrl'], '.fr/reset-password/')
                        && !str_contains($vars['resetUrl'], '.fr//');
                }),
            )
            ->willReturn('<p>HTML</p>');

        $this->mailer->method('send');

        $handler = new SendPasswordResetEmailHandler(
            $this->mailer,
            $this->twig,
            'https://ecole-alpha.classeo.fr/',
            self::FROM_EMAIL,
        );

        ($handler)($event);
    }

    #[Test]
    public function includesTokenValueInResetUrl(): void
    {
        $customToken = 'custom-token-value-xyz';
        $event = new PasswordResetTokenGenerated(
            tokenId: PasswordResetTokenId::generate(),
            tokenValue: $customToken,
            userId: '550e8400-e29b-41d4-a716-446655440001',
            email: 'user@example.com',
            tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
            occurredOn: new DateTimeImmutable('2026-02-15 10:00:00'),
        );

        $this->twig->expects(self::once())
            ->method('render')
            ->with(
                self::anything(),
                self::callback(static function (array $vars) use ($customToken): bool {
                    return str_ends_with($vars['resetUrl'], '/reset-password/' . $customToken);
                }),
            )
            ->willReturn('<p>HTML</p>');

        $this->mailer->method('send');

        $handler = new SendPasswordResetEmailHandler(
            $this->mailer,
            $this->twig,
            self::APP_URL,
            self::FROM_EMAIL,
        );

        ($handler)($event);
    }

    #[Test]
    public function usesDefaultFromEmail(): void
    {
        $event = $this->createEvent('user@example.com');

        $this->twig->method('render')->willReturn('<p>HTML</p>');

        $sentEmail = null;
        $this->mailer->expects(self::once())
            ->method('send')
            ->with(self::callback(static function (Email $email) use (&$sentEmail): bool {
                $sentEmail = $email;

                return true;
            }));

        // Use default fromEmail (constructor default)
        $handler = new SendPasswordResetEmailHandler(
            $this->mailer,
            $this->twig,
            self::APP_URL,
        );

        ($handler)($event);

        self::assertSame('noreply@classeo.fr', $sentEmail->getFrom()[0]->getAddress());
    }

    private function createEvent(string $email): PasswordResetTokenGenerated
    {
        return new PasswordResetTokenGenerated(
            tokenId: PasswordResetTokenId::generate(),
            tokenValue: self::TOKEN_VALUE,
            userId: '550e8400-e29b-41d4-a716-446655440001',
            email: $email,
            tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
            occurredOn: new DateTimeImmutable('2026-02-15 10:00:00'),
        );
    }
}
