<?php

namespace MintyPHP\Tests\Service\Tenant;

use MintyPHP\Repository\Org\DepartmentRepository;
use MintyPHP\Repository\Tenant\TenantRepository;
use MintyPHP\Repository\Tenant\UserTenantRepository;
use MintyPHP\Service\Access\PermissionService;
use MintyPHP\Service\Tenant\TenantScopeService;
use PHPUnit\Framework\TestCase;

class TenantScopeServiceTest extends TestCase
{
    public function testLegacyPermissionsDoNotGrantGlobalAccess(): void
    {
        $checkedPermissions = [];

        $tenantRepository = $this->createMock(TenantRepository::class);
        $tenantRepository->method('listIds')->willReturn([1, 2, 3]);
        $tenantRepository->method('listActiveIdsByIds')
            ->willReturnCallback(static fn (array $ids): array => array_values(array_intersect([1, 2, 3], $ids)));

        $departmentRepository = $this->createMock(DepartmentRepository::class);
        $userTenantRepository = $this->createMock(UserTenantRepository::class);
        $userTenantRepository->method('listTenantIdsByUserId')
            ->willReturnCallback(static function (int $userId): array {
                if ($userId === 10) {
                    return [1];
                }
                if ($userId === 99) {
                    return [2];
                }
                return [];
            });

        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->method('userHas')
            ->willReturnCallback(static function (int $userId, string $permission) use (&$checkedPermissions): bool {
                $checkedPermissions[] = $permission;
                if ($userId !== 10) {
                    return false;
                }

                return in_array($permission, [PermissionService::SETTINGS_UPDATE, PermissionService::TENANTS_UPDATE], true);
            });

        $service = new TenantScopeService(
            $tenantRepository,
            $departmentRepository,
            $userTenantRepository,
            $permissionService
        );

        $this->assertFalse($service->hasGlobalAccess(10));
        $this->assertFalse($service->canAccess('users', 99, 10));
        $this->assertNotEmpty($checkedPermissions);
        $this->assertSame(
            [PermissionService::TENANT_SCOPE_GLOBAL],
            array_values(array_unique($checkedPermissions))
        );
    }

    public function testGlobalPermissionGrantsBypass(): void
    {
        $tenantRepository = $this->createMock(TenantRepository::class);
        $departmentRepository = $this->createMock(DepartmentRepository::class);
        $userTenantRepository = $this->createMock(UserTenantRepository::class);
        $permissionService = $this->createMock(PermissionService::class);

        $permissionService->method('userHas')
            ->willReturnCallback(static fn (int $userId, string $permission): bool => $userId === 77 && $permission === PermissionService::TENANT_SCOPE_GLOBAL);

        $service = new TenantScopeService(
            $tenantRepository,
            $departmentRepository,
            $userTenantRepository,
            $permissionService
        );

        $this->assertTrue($service->hasGlobalAccess(77));
        $this->assertTrue($service->canAccess('users', 12345, 77));
    }

    public function testGetUserTenantIdsReturnsAllActiveTenantsWhenGlobalPermissionIsPresent(): void
    {
        $tenantRepository = $this->createMock(TenantRepository::class);
        $tenantRepository->expects($this->once())
            ->method('listIds')
            ->willReturn([1, 2, 3]);
        $tenantRepository->expects($this->once())
            ->method('listActiveIdsByIds')
            ->with([1, 2, 3])
            ->willReturn([1, 3]);

        $departmentRepository = $this->createMock(DepartmentRepository::class);
        $userTenantRepository = $this->createMock(UserTenantRepository::class);
        $userTenantRepository->expects($this->never())->method('listTenantIdsByUserId');

        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->method('userHas')
            ->willReturnCallback(static fn (int $userId, string $permission): bool => $userId === 55 && $permission === PermissionService::TENANT_SCOPE_GLOBAL);

        $service = new TenantScopeService(
            $tenantRepository,
            $departmentRepository,
            $userTenantRepository,
            $permissionService
        );

        $this->assertSame([1, 3], $service->getUserTenantIds(55));
    }

    public function testGetUserTenantIdsWithoutGlobalPermissionUsesAssignedTenantsOnly(): void
    {
        $tenantRepository = $this->createMock(TenantRepository::class);
        $tenantRepository->expects($this->once())
            ->method('listActiveIdsByIds')
            ->with([2, 4])
            ->willReturn([2]);
        $tenantRepository->expects($this->never())->method('listIds');

        $departmentRepository = $this->createMock(DepartmentRepository::class);
        $userTenantRepository = $this->createMock(UserTenantRepository::class);
        $userTenantRepository->expects($this->once())
            ->method('listTenantIdsByUserId')
            ->with(88)
            ->willReturn([2, 4, 4]);

        $permissionService = $this->createMock(PermissionService::class);
        $permissionService->method('userHas')->willReturn(false);

        $service = new TenantScopeService(
            $tenantRepository,
            $departmentRepository,
            $userTenantRepository,
            $permissionService
        );

        $this->assertSame([2], $service->getUserTenantIds(88));
    }
}
