<?php

namespace MintyPHP\Tests\Service\Settings;

use MintyPHP\Repository\Access\RoleRepositoryInterface;
use MintyPHP\Repository\Org\DepartmentRepositoryInterface;
use MintyPHP\Repository\Settings\SettingRepositoryInterface;
use MintyPHP\Repository\Tenant\TenantRepositoryInterface;
use MintyPHP\Service\Settings\SettingsDefaultsGateway;
use MintyPHP\Service\Settings\SettingsMetadataGateway;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;

class SettingsDefaultsGatewayTest extends TestCase
{
    #[DataProvider('unknownReferenceProvider')]
    public function testSettersRejectUnknownReferences(string $setter, int $id): void
    {
        $settings = $this->createMock(SettingRepositoryInterface::class);
        $settings->expects($this->never())->method('set');

        $tenantRepository = $this->createMock(TenantRepositoryInterface::class);
        $roleRepository = $this->createMock(RoleRepositoryInterface::class);
        $departmentRepository = $this->createMock(DepartmentRepositoryInterface::class);
        $gateway = $this->newGateway($settings, $tenantRepository, $roleRepository, $departmentRepository);

        match ($setter) {
            'setDefaultTenantId' => $tenantRepository->expects($this->once())->method('find')->with($id)->willReturn(null),
            'setDefaultRoleId' => $roleRepository->expects($this->once())->method('find')->with($id)->willReturn(null),
            'setDefaultDepartmentId' => $departmentRepository->expects($this->once())->method('find')->with($id)->willReturn(null),
            default => throw new \LogicException("Unknown setter: {$setter}"),
        };

        $this->assertFalse($gateway->$setter($id));
    }

    public function testSettersPersistValidIds(): void
    {
        $settings = $this->createMock(SettingRepositoryInterface::class);
        $settings->expects($this->exactly(3))
            ->method('set')
            ->willReturn(true);

        $tenantRepository = $this->createMock(TenantRepositoryInterface::class);
        $tenantRepository->method('find')->willReturn(['id' => 5]);

        $roleRepository = $this->createMock(RoleRepositoryInterface::class);
        $roleRepository->method('find')->willReturn(['id' => 6]);

        $departmentRepository = $this->createMock(DepartmentRepositoryInterface::class);
        $departmentRepository->method('find')->willReturn(['id' => 7]);

        $gateway = $this->newGateway($settings, $tenantRepository, $roleRepository, $departmentRepository);
        $this->assertTrue($gateway->setDefaultTenantId(5));
        $this->assertTrue($gateway->setDefaultRoleId(6));
        $this->assertTrue($gateway->setDefaultDepartmentId(7));
    }

    public function testSettersAllowResetToNull(): void
    {
        $settings = $this->createMock(SettingRepositoryInterface::class);
        $settings->expects($this->exactly(3))->method('set')->willReturn(true);

        $gateway = $this->newGateway($settings);
        $this->assertTrue($gateway->setDefaultTenantId(null));
        $this->assertTrue($gateway->setDefaultRoleId(null));
        $this->assertTrue($gateway->setDefaultDepartmentId(null));
    }

    #[DataProvider('getterNormalizationProvider')]
    public function testGettersNormalizeNonPositiveValuesToNull(
        string $storedKey,
        string $getter,
        ?string $storedValue,
        ?int $expected
    ): void {
        $settings = $this->createMock(SettingRepositoryInterface::class);
        $settings->method('getValue')->willReturnCallback(
            static fn (string $key): ?string => $key === $storedKey ? $storedValue : null
        );

        $gateway = $this->newGateway($settings);

        $this->assertSame($expected, $gateway->$getter());
    }

    private function newGateway(
        SettingRepositoryInterface $settingRepository,
        ?TenantRepositoryInterface $tenantRepository = null,
        ?RoleRepositoryInterface $roleRepository = null,
        ?DepartmentRepositoryInterface $departmentRepository = null
    ): SettingsDefaultsGateway {
        $tenantRepository = $tenantRepository ?? $this->createMock(TenantRepositoryInterface::class);
        $roleRepository = $roleRepository ?? $this->createMock(RoleRepositoryInterface::class);
        $departmentRepository = $departmentRepository ?? $this->createMock(DepartmentRepositoryInterface::class);

        return new SettingsDefaultsGateway(
            new SettingsMetadataGateway($settingRepository),
            $tenantRepository,
            $roleRepository,
            $departmentRepository
        );
    }

    public static function unknownReferenceProvider(): array
    {
        return [
            'unknown tenant' => ['setDefaultTenantId', 99],
            'unknown role' => ['setDefaultRoleId', 55],
            'unknown department' => ['setDefaultDepartmentId', 77],
        ];
    }

    public static function getterNormalizationProvider(): array
    {
        return [
            'default tenant positive id' => ['default_tenant_id', 'getDefaultTenantId', '5', 5],
            'default tenant zero becomes null' => ['default_tenant_id', 'getDefaultTenantId', '0', null],
            'default role negative becomes null' => ['default_role_id', 'getDefaultRoleId', '-1', null],
            'default department missing stays null' => ['default_department_id', 'getDefaultDepartmentId', null, null],
        ];
    }
}
