<?php

namespace App\Tests\Event\Service;

use App\Entity\Account;
use App\Entity\AccountResetPasswordRequest;
use App\Event\Object\EventAdminCreate;
use App\Event\Object\EventAdminDeleted;
use App\Event\Service\AdminEvent;
use App\Service\Mailer\Mailer;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

#[AllowMockObjectsWithoutExpectations]
class AdminEventTest extends TestCase
{
    private $mailer;
    private $urlGenerator;
    private $entityManager;
    private $adminEvent;

    protected function setUp(): void
    {
        $this->mailer = $this->createMock(Mailer::class);
        $this->urlGenerator = $this->createMock(UrlGeneratorInterface::class);
        $this->entityManager = $this->createMock(EntityManagerInterface::class);
        
        $this->adminEvent = new AdminEvent(
            $this->mailer,
            $this->urlGenerator,
            $this->entityManager
        );
    }

    public function testOnAdminCreate()
    {
        $account = $this->createMock(Account::class);
        $account->method('getId')->willReturn(1);
        $account->method('getEmail')->willReturn('test@example.com');
        $account->method('getFirstName')->willReturn('John');
        $account->method('getName')->willReturn('Doe');

        $requestedAccount = $this->createMock(Account::class);

        $event = new EventAdminCreate($account, $requestedAccount);

        $repo = $this->createMock(EntityRepository::class);
        $this->entityManager->method('getRepository')->with(AccountResetPasswordRequest::class)->willReturn($repo);
        $repo->method('findOneBy')->willReturn(null);

        $this->entityManager->expects($this->once())->method('persist');
        $this->entityManager->expects($this->once())->method('flush');

        $this->urlGenerator->expects($this->once())
            ->method('generate')
            ->willReturn('http://example.com/reset');

        $this->mailer->expects($this->once())
            ->method('send')
            ->with(
                'test@example.com',
                'John Doe',
                '[Intranet Ludikevent] Activation de votre accès administrateur'
            );

        $this->adminEvent->onAdminCreate($event);
    }
    
    public function testOnAdminCreateExistingValidRequest()
    {
        $account = $this->createMock(Account::class);
        $account->method('getId')->willReturn(1);
        $account->method('getEmail')->willReturn('test@example.com');
        $requestedAccount = $this->createMock(Account::class);
        $event = new EventAdminCreate($account, $requestedAccount);

        $existingRequest = new AccountResetPasswordRequest();
        $existingRequest->setExpiresAt(new \DateTimeImmutable('+1 hour'));
        $existingRequest->setToken('existing_token');

        $repo = $this->createMock(EntityRepository::class);
        $this->entityManager->method('getRepository')->with(AccountResetPasswordRequest::class)->willReturn($repo);
        $repo->method('findOneBy')->willReturn($existingRequest);

        // Should NOT persist a new one, but reuse existing
        $this->entityManager->expects($this->never())->method('persist');
        
        // Should generate URL with existing token
        $this->urlGenerator->expects($this->once())
            ->method('generate')
            ->with(
                'app_forgot_password_confirm',
                ['id' => 1, 'token' => 'existing_token']
            );
            
        $this->adminEvent->onAdminCreate($event);
    }
    
    public function testOnAdminCreateExistingExpiredRequest()
    {
        $account = $this->createMock(Account::class);
        $account->method('getId')->willReturn(1);
        $account->method('getEmail')->willReturn('test@example.com');
        $requestedAccount = $this->createMock(Account::class);
        $event = new EventAdminCreate($account, $requestedAccount);

        $existingRequest = new AccountResetPasswordRequest();
        $existingRequest->setExpiresAt(new \DateTimeImmutable('-1 hour'));

        $repo = $this->createMock(EntityRepository::class);
        $this->entityManager->method('getRepository')->with(AccountResetPasswordRequest::class)->willReturn($repo);
        $repo->method('findOneBy')->willReturn($existingRequest);

        // Should remove expired
        $this->entityManager->expects($this->once())->method('remove')->with($existingRequest);
        // Should persist new one
        $this->entityManager->expects($this->atLeastOnce())->method('persist');
        // Flush called for remove and persist
        $this->entityManager->expects($this->atLeast(2))->method('flush');
            
        $this->adminEvent->onAdminCreate($event);
    }

    public function testOnAdminDeleted()
    {
        $account = $this->createMock(Account::class);
        $requestedAccount = $this->createMock(Account::class);
        
        $event = new EventAdminDeleted($account, $requestedAccount);

        $this->mailer->expects($this->exactly(2))
            ->method('send')
            ->with(
                $this->stringContains('@'), // Simple check for email arg
                $this->stringContains('Notification'),
                "[Intranet Ludikevent] - Suppression d'un administrateur"
            );

        $this->adminEvent->onAdminDeleted($event);
    }
}
