<?php

namespace MintyPHP\Tests\Module\Notifications\Listeners;

use MintyPHP\Module\Notifications\Listeners\UserAssignmentChangedNotificationListener;
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;

final class UserAssignmentChangedNotificationListenerTest extends TestCase
{
    private NotificationService&MockObject $notificationService;
    private UserReadRepositoryInterface&MockObject $userReadRepository;
    private UserAssignmentChangedNotificationListener $listener;

    protected function setUp(): void
    {
        $this->notificationService = $this->createMock(NotificationService::class);
        $this->userReadRepository = $this->createMock(UserReadRepositoryInterface::class);
        $this->listener = new UserAssignmentChangedNotificationListener(
            $this->notificationService,
            $this->userReadRepository
        );
    }

    public function testHandleNotifiesAffectedUserAndTenantAdmins(): void
    {
        $this->userReadRepository->expects($this->once())->method('find')->with(10)->willReturn([
            'id' => 10,
            'uuid' => 'user-uuid',
            'display_name' => 'Alice',
        ]);
        $this->userReadRepository->expects($this->once())
            ->method('listPrivilegedUserIdsByPermissionKeys')
            ->with([PermissionService::USERS_UPDATE_ASSIGNMENTS])
            ->willReturn([20, 30]);

        $this->notificationService->expects($this->once())
            ->method('createFromMessage')
            ->with(
                10,
                null,
                $this->callback(function (NotificationMessage $message): bool {
                    return $message->type() === 'user.assignment_changed'
                        && $message->link() === 'admin/users/edit/user-uuid'
                        && $message->titleKey() === 'User assignments updated: %s'
                        && $message->bodyKey() === 'Tenant, role, or department assignments were changed.';
                })
            )
            ->willReturn(99);

        $this->notificationService->expects($this->once())
            ->method('createForTenantAdminUsersFromMessage')
            ->with(
                1,
                $this->isInstanceOf(NotificationMessage::class),
                7,
                $this->isArray()
            )
            ->willReturn(1);

        $this->listener->handle('user.assignment_changed', [
            'user_id' => 10,
            'tenant_ids' => [1],
            'actor_user_id' => 7,
            'changes' => ['roles' => ['before' => [1], 'after' => [2]]],
        ]);
    }

    public function testHandleExcludesAffectedUserWhenActorEqualsTarget(): void
    {
        $this->userReadRepository->expects($this->once())->method('find')->with(10)->willReturn([
            'id' => 10,
            'uuid' => 'user-uuid',
            'display_name' => 'Alice',
        ]);
        $this->userReadRepository->expects($this->once())
            ->method('listPrivilegedUserIdsByPermissionKeys')
            ->willReturn([20]);

        $this->notificationService->expects($this->never())->method('createFromMessage');
        $this->notificationService->expects($this->once())
            ->method('createForTenantAdminUsersFromMessage')
            ->with(
                1,
                $this->anything(),
                10,
                $this->anything()
            );

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

    public function testHandleSkipsInvalidPayload(): void
    {
        $this->notificationService->expects($this->never())->method('createFromMessage');
        $this->notificationService->expects($this->never())->method('createForTenantAdminUsersFromMessage');

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