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

use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Message\AccountActivated as AccountActivatedEvent;
use Ferienpass\CoreBundle\Message\AccountAlreadyExists as AccountAlreadyExistsEvent;
use Ferienpass\CoreBundle\Message\AccountCreated as AccountCreatedEvent;
use Ferienpass\CoreBundle\Message\AccountDisabled as AccountDisabledEvent;
use Ferienpass\CoreBundle\Notification\AccountCreatedNotification;
use Ferienpass\CoreBundle\NotificationHandler\AccountActivated;
use Ferienpass\CoreBundle\NotificationHandler\AccountCreated;
use Ferienpass\CoreBundle\NotificationHandler\AccountDisabled;
use Ferienpass\CoreBundle\NotificationHandler\AccountRegistrationHelp;
use Ferienpass\CoreBundle\NotificationHandler\PhoneFormatter;
use Ferienpass\CoreBundle\Notifier\Notifier;
use Ferienpass\CoreBundle\Repository\UserRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\UriSigner;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Uid\Uuid;

final class AccountNotificationsTest extends TestCase
{
    // ---- AccountActivated ----

    public function testAccountActivatedDoesNothingWhenUserNotFound(): void
    {
        $users = $this->createMock(UserRepository::class);
        $users->method('find')->willReturn(null);

        $notifier = $this->createMock(Notifier::class);
        $notifier->expects(self::never())->method('send');

        $handler = new AccountActivated($notifier, $users);
        $handler(new AccountActivatedEvent((string) Uuid::v7()), Uuid::v7());
    }

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

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

        $notifier = $this->createMock(Notifier::class);
        $notifier->method('accountActivated')->willReturn(null);
        $notifier->expects(self::never())->method('send');

        $handler = new AccountActivated($notifier, $users);
        $handler(new AccountActivatedEvent((string) Uuid::v7()), Uuid::v7());
    }

    // ---- AccountDisabled ----

    public function testAccountDisabledDoesNothingWhenUserNotFound(): void
    {
        $users = $this->createMock(UserRepository::class);
        $users->method('find')->willReturn(null);

        $notifier = $this->createMock(Notifier::class);
        $notifier->expects(self::never())->method('send');

        $handler = new AccountDisabled(
            $notifier,
            $users,
            $this->createMock(UrlGeneratorInterface::class),
            $this->createMock(UriSigner::class)
        );
        $handler(new AccountDisabledEvent((string) Uuid::v7()), Uuid::v7());
    }

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

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

        $notifier = $this->createMock(Notifier::class);
        $notifier->method('accountCreated')->willReturn(null);
        $notifier->expects(self::never())->method('send');

        $handler = new AccountDisabled(
            $notifier,
            $users,
            $this->createMock(UrlGeneratorInterface::class),
            $this->createMock(UriSigner::class)
        );
        $handler(new AccountDisabledEvent((string) Uuid::v7()), Uuid::v7());
    }

    // ---- AccountRegistrationHelp ----

    public function testAccountRegistrationHelpDoesNothingWhenUserNotFound(): void
    {
        $users = $this->createMock(UserRepository::class);
        $users->method('find')->willReturn(null);

        $notifier = $this->createMock(Notifier::class);
        $notifier->expects(self::never())->method('send');

        $handler = new AccountRegistrationHelp($notifier, $users);
        $handler(new AccountAlreadyExistsEvent((string) Uuid::v7()), Uuid::v7());
    }

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

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

        $notifier = $this->createMock(Notifier::class);
        $notifier->method('accountRegistrationHelp')->willReturn(null);
        $notifier->expects(self::never())->method('send');

        $handler = new AccountRegistrationHelp($notifier, $users);
        $handler(new AccountAlreadyExistsEvent((string) Uuid::v7()), Uuid::v7());
    }

    // ---- Activation link points at the merged confirm/activate route ----
    // Regression guard: the activation route was consolidated from
    // `cms_registration_activate` into `cms_registration_confirm`. A stale route
    // name here throws RouteNotFoundException in the worker when an account is
    // (re)registered, with no user-facing trace.

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

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

        $notification = $this->createMock(AccountCreatedNotification::class);
        $notification->method('belongsTo')->willReturnSelf();
        $notification->method('actionUrl')->willReturnSelf();

        $notifier = $this->createMock(Notifier::class);
        $notifier->method('accountCreated')->willReturn($notification);
        $notifier->expects(self::once())->method('send');

        $urlGenerator = $this->createMock(UrlGeneratorInterface::class);
        $urlGenerator->expects(self::once())
            ->method('generate')
            ->with(
                'cms_registration_confirm',
                self::callback(static fn (array $p): bool => \array_key_exists('uuid', $p) && \array_key_exists('valid', $p)),
                UrlGeneratorInterface::ABSOLUTE_URL,
            )
            ->willReturn('https://example.com/registrierung/uuid?valid=2099-01-01T00:00:00+00:00');

        $uriSigner = $this->createMock(UriSigner::class);
        $uriSigner->method('sign')->willReturnArgument(0);

        $handler = new AccountCreated($notifier, $users, $urlGenerator, $uriSigner, $this->createMock(PhoneFormatter::class));
        $handler(new AccountCreatedEvent((string) Uuid::v7()), Uuid::v7());
    }

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

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

        $notification = $this->createMock(AccountCreatedNotification::class);
        $notification->method('belongsTo')->willReturnSelf();
        $notification->method('actionUrl')->willReturnSelf();

        $notifier = $this->createMock(Notifier::class);
        $notifier->method('accountCreated')->willReturn($notification);
        $notifier->expects(self::once())->method('send');

        $urlGenerator = $this->createMock(UrlGeneratorInterface::class);
        $urlGenerator->expects(self::once())
            ->method('generate')
            ->with(
                'cms_registration_confirm',
                self::callback(static fn (array $p): bool => \array_key_exists('uuid', $p) && \array_key_exists('valid', $p)),
                UrlGeneratorInterface::ABSOLUTE_URL,
            )
            ->willReturn('https://example.com/registrierung/uuid?valid=2099-01-01T00:00:00+00:00');

        $uriSigner = $this->createMock(UriSigner::class);
        $uriSigner->method('sign')->willReturnArgument(0);

        $handler = new AccountDisabled($notifier, $users, $urlGenerator, $uriSigner);
        $handler(new AccountDisabledEvent((string) Uuid::v7()), Uuid::v7());
    }
}
