<?php

namespace MintyPHP\Tests\Module\Notifications\Listeners;

use MintyPHP\Module\Notifications\Listeners\UserDeletedNotificationListener;
use MintyPHP\Module\Notifications\Service\NotificationMessage;
use MintyPHP\Module\Notifications\Service\NotificationService;
use MintyPHP\Repository\User\UserReadRepositoryInterface;
use MintyPHP\Service\Access\PermissionService;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

class UserDeletedNotificationListenerTest extends TestCase
{
    private NotificationService&MockObject $notifService;
    private UserReadRepositoryInterface&MockObject $userRepo;
    private UserDeletedNotificationListener $listener;

    protected function setUp(): void
    {
        $this->notifService = $this->createMock(NotificationService::class);
        $this->userRepo = $this->createMock(UserReadRepositoryInterface::class);
        $this->listener = new UserDeletedNotificationListener($this->notifService, $this->userRepo);
    }

    public function testHandleCleansUpAndNotifiesAdmins(): void
    {
        $this->notifService->expects($this->once())
            ->method('deleteAllForUser')
            ->with(10);

        $this->userRepo->expects($this->once())
            ->method('listPrivilegedUserIdsByPermissionKeys')
            ->with([PermissionService::USERS_DELETE])
            ->willReturn([20, 30]);

        $this->notifService->expects($this->exactly(2))
            ->method('createForTenantAdminUsersFromMessage')
            ->willReturnCallback(function (int $tenantId, NotificationMessage $message, ?int $excludeUserId, array $adminSet): int {
                $this->assertContains($tenantId, [1, 2]);
                $this->assertSame('user.deleted', $message->type());
                $this->assertStringContainsString('Alice', $message->title());
                $this->assertNull($message->link());
                $this->assertSame(5, $excludeUserId);
                $this->assertArrayHasKey(20, $adminSet);
                $this->assertArrayHasKey(30, $adminSet);
                $this->assertSame('User deleted: %s', $message->titleKey());
                return 1;
            });

        $this->listener->handle('user.deleted', [
            'user_id' => 10,
            'display_name' => 'Alice',
            'tenant_ids' => [1, 2],
            'actor_user_id' => 5,
        ]);
    }

    public function testHandleSkipsOnInvalidUserId(): void
    {
        $this->notifService->expects($this->never())->method('deleteAllForUser');
        $this->notifService->expects($this->never())->method('createForTenantAdminUsersFromMessage');

        $this->listener->handle('user.deleted', ['user_id' => 0]);
        $this->listener->handle('user.deleted', []);
    }

    public function testHandleStillCleansUpWhenDisplayNameEmpty(): void
    {
        $this->notifService->expects($this->once())
            ->method('deleteAllForUser')
            ->with(10);

        $this->notifService->expects($this->never())->method('createForTenantAdminUsersFromMessage');

        $this->listener->handle('user.deleted', [
            'user_id' => 10,
            'display_name' => '',
            'tenant_ids' => [1],
        ]);
    }

    public function testHandleStillCleansUpWhenTenantIdsEmpty(): void
    {
        $this->notifService->expects($this->once())
            ->method('deleteAllForUser')
            ->with(10);

        $this->notifService->expects($this->never())->method('createForTenantAdminUsersFromMessage');

        $this->listener->handle('user.deleted', [
            'user_id' => 10,
            'display_name' => 'Alice',
            'tenant_ids' => [],
        ]);
    }

    public function testHandleExcludesActorFromRecipients(): void
    {
        $this->notifService->expects($this->once())->method('deleteAllForUser')->with(10);

        $this->userRepo->expects($this->once())
            ->method('listPrivilegedUserIdsByPermissionKeys')
            ->willReturn([20]);

        $this->notifService->expects($this->once())
            ->method('createForTenantAdminUsersFromMessage')
            ->with(
                1,
                $this->anything(),
                7, // actor excluded
                $this->anything()
            )
            ->willReturn(1);

        $this->listener->handle('user.deleted', [
            'user_id' => 10,
            'display_name' => 'Alice',
            'tenant_ids' => [1],
            'actor_user_id' => 7,
        ]);
    }

    public function testHandleSkipsNotificationWhenNoAdminUsers(): void
    {
        $this->notifService->expects($this->once())->method('deleteAllForUser')->with(10);

        $this->userRepo->expects($this->once())
            ->method('listPrivilegedUserIdsByPermissionKeys')
            ->willReturn([]);

        $this->notifService->expects($this->never())->method('createForTenantAdminUsersFromMessage');

        $this->listener->handle('user.deleted', [
            'user_id' => 10,
            'display_name' => 'Alice',
            'tenant_ids' => [1],
        ]);
    }
}
