<?php

namespace MintyPHP\Tests\Service\Auth;

use MintyPHP\App\AppContainer;
use MintyPHP\App\Module\Contracts\SessionProvider;
use MintyPHP\App\Module\ModuleRegistry;
use MintyPHP\Http\SessionStore;
use MintyPHP\Service\Auth\AuthSessionTenantContextService;
use MintyPHP\Service\User\UserTenantContextService;
use PHPUnit\Framework\TestCase;

class AuthSessionTenantContextServiceTest extends TestCase
{
    protected function setUp(): void
    {
        $_SESSION = [];
        DummyAuthSessionProvider::$populateCalls = 0;
        DummyAuthSessionProvider::$clearCalls = 0;
    }

    public function testHydrateMarksNoActiveTenantWhenUserHasNoTenants(): void
    {
        $_SESSION['current_tenant'] = ['id' => 9];

        $userTenantContextService = $this->createMock(UserTenantContextService::class);
        $userTenantContextService->method('getAvailableTenants')->willReturn([]);
        $userTenantContextService->expects($this->never())->method('getCurrentTenant');

        $service = $this->createService($userTenantContextService);

        $service->hydrateForUser(5);

        $this->assertSame([], $_SESSION['available_tenants']);
        $this->assertTrue((bool) $_SESSION['no_active_tenant']);
        $this->assertArrayNotHasKey('current_tenant', $_SESSION);
        $this->assertArrayNotHasKey('module.addressbook.departments_by_tenant', $_SESSION);
    }

    public function testHydrateUsesFirstAvailableTenantAsFallback(): void
    {
        $availableTenants = [
            ['id' => 7, 'uuid' => 'tenant-7'],
            ['id' => 8, 'uuid' => 'tenant-8'],
        ];
        $userTenantContextService = $this->createMock(UserTenantContextService::class);
        $userTenantContextService->method('getAvailableTenants')->willReturn($availableTenants);
        $userTenantContextService->method('getCurrentTenant')->willReturn(null);

        $service = $this->createService($userTenantContextService);

        $service->hydrateForUser(5);

        $this->assertSame($availableTenants, $_SESSION['available_tenants']);
        $this->assertFalse((bool) $_SESSION['no_active_tenant']);
        $this->assertSame(7, (int) ($_SESSION['current_tenant']['id'] ?? 0));
        $this->assertArrayNotHasKey('module.addressbook.departments_by_tenant', $_SESSION);
    }

    public function testHydratePreservesThemeAndPolicyFieldsInSession(): void
    {
        $availableTenants = [
            [
                'id' => 3,
                'uuid' => 'tenant-3',
                'default_theme' => 'dark',
                'allow_user_theme' => '0',
            ],
        ];

        $userTenantContextService = $this->createMock(UserTenantContextService::class);
        $userTenantContextService->method('getAvailableTenants')->willReturn($availableTenants);
        $userTenantContextService->method('getCurrentTenant')->willReturn($availableTenants[0]);

        $service = $this->createService($userTenantContextService);

        $service->hydrateForUser(10);

        $tenant = $_SESSION['current_tenant'] ?? [];
        $this->assertSame('dark', $tenant['default_theme'] ?? null);
        $this->assertSame('0', $tenant['allow_user_theme'] ?? null);
    }

    public function testHydrateAndClearInvokeConfiguredModuleSessionProviders(): void
    {
        $fixturesDir = sys_get_temp_dir() . '/auth-session-provider-test-' . uniqid();
        mkdir($fixturesDir . '/mod-session', 0777, true);
        file_put_contents(
            $fixturesDir . '/mod-session/module.php',
            '<?php return ' . var_export([
                'id' => 'mod-session',
                'session_providers' => [DummyAuthSessionProvider::class],
            ], true) . ';'
        );

        try {
            $registry = ModuleRegistry::boot($fixturesDir, ['mod-session']);

            $userTenantContextService = $this->createMock(UserTenantContextService::class);
            $userTenantContextService->method('getAvailableTenants')->willReturn([['id' => 7, 'uuid' => 'tenant-7']]);
            $userTenantContextService->method('getCurrentTenant')->willReturn(['id' => 7, 'uuid' => 'tenant-7']);

            $service = $this->createService($userTenantContextService, $registry);
            $_SESSION['user'] = ['id' => 42];

            $service->hydrateForUser(42);
            $this->assertSame(1, DummyAuthSessionProvider::$populateCalls);
            $this->assertSame(42, (int) ($_SESSION['dummy_provider_user_id'] ?? 0));

            $service->clearModuleSessionData();
            $this->assertSame(1, DummyAuthSessionProvider::$clearCalls);
            $this->assertArrayNotHasKey('dummy_provider_user_id', $_SESSION);
        } finally {
            @unlink($fixturesDir . '/mod-session/module.php');
            @rmdir($fixturesDir . '/mod-session');
            @rmdir($fixturesDir);
        }
    }

    private function createService(
        UserTenantContextService $userTenantContextService,
        ?ModuleRegistry $registry = null
    ): AuthSessionTenantContextService {
        $registry ??= ModuleRegistry::boot(sys_get_temp_dir() . '/module-registry-empty-' . uniqid(), []);
        $container = new AppContainer();
        $container->set(ModuleRegistry::class, static fn (): ModuleRegistry => $registry);

        return new AuthSessionTenantContextService(
            $userTenantContextService,
            new SessionStore(),
            $container
        );
    }
}

final class DummyAuthSessionProvider implements SessionProvider
{
    public static int $populateCalls = 0;
    public static int $clearCalls = 0;

    public function populate(array $user, AppContainer $container): void
    {
        self::$populateCalls++;
        $_SESSION['dummy_provider_user_id'] = (int) ($user['id'] ?? 0);
    }

    public function clear(AppContainer $container): void
    {
        self::$clearCalls++;
        unset($_SESSION['dummy_provider_user_id']);
    }
}
