<?php

declare(strict_types=1);

namespace Ferienpass\AdminBundle\Tests\MessageHandler;

use Ferienpass\AdminBundle\Message\ResetPassword as ResetPasswordCommand;
use Ferienpass\AdminBundle\MessageHandler\ResetPassword;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Repository\UserRepository;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use SymfonyCasts\Bundle\ResetPassword\Exception\ResetPasswordExceptionInterface;
use SymfonyCasts\Bundle\ResetPassword\Model\ResetPasswordToken;
use SymfonyCasts\Bundle\ResetPassword\ResetPasswordHelperInterface;

final class ResetPasswordTest extends TestCase
{
    public function testDoesNothingWhenUserNotFound(): void
    {
        $users = $this->createMock(UserRepository::class);
        $users->method('findOneBy')->willReturn(null);

        $helper = $this->createMock(ResetPasswordHelperInterface::class);
        $helper->expects(self::never())->method('generateResetToken');

        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects(self::never())->method('dispatch');

        $handler = new ResetPassword($users, $helper, $bus);
        $handler(new ResetPasswordCommand('unknown@example.com'));
    }

    public function testDispatchesPasswordLinkCreatedOnSuccess(): void
    {
        $user = new User();

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

        $token = new ResetPasswordToken('my-reset-token', new \DateTime('+1 hour'));

        $helper = $this->createMock(ResetPasswordHelperInterface::class);
        $helper->method('generateResetToken')->willReturn($token);

        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects(self::once())->method('dispatch')->willReturn(new Envelope(new \stdClass()));

        $handler = new ResetPassword($users, $helper, $bus);
        $handler(new ResetPasswordCommand('user@example.com'));
    }

    public function testSilentlyReturnsWhenResetPasswordExceptionThrown(): void
    {
        $user = new User();

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

        $exception = new class('error') extends \RuntimeException implements ResetPasswordExceptionInterface {
            public function getReason(): string
            {
                return $this->getMessage();
            }
        };

        $helper = $this->createMock(ResetPasswordHelperInterface::class);
        $helper->method('generateResetToken')->willThrowException($exception);

        $bus = $this->createMock(MessageBusInterface::class);
        $bus->expects(self::never())->method('dispatch');

        $handler = new ResetPassword($users, $helper, $bus);
        $handler(new ResetPasswordCommand('user@example.com'));

        $this->addToAssertionCount(1);
    }
}
