<?php

declare(strict_types=1);

use App\Models\BackupSchedule;
use App\Models\EntraGroup;
use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Gate;

uses(RefreshDatabase::class);

it('keeps EntraGroupPolicy scoped to the remembered canonical admin tenant', function (): void {
    $tenantA = ManagedEnvironment::factory()->create();
    [$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
    $tenantB = ManagedEnvironment::factory()->create(['workspace_id' => (int) $tenantA->workspace_id]);
    createUserWithTenant(tenant: $tenantB, user: $user, role: 'owner');

    $groupA = EntraGroup::factory()->for($tenantA)->create();
    $groupB = EntraGroup::factory()->for($tenantB)->create();

    $this->actingAs($user);
    Filament::setCurrentPanel('admin');
    Filament::setTenant(null, true);
    Filament::bootCurrentPanel();

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenantA->workspace_id => (int) $tenantA->getKey(),
    ]);

    expect(Gate::forUser($user)->allows('view', $groupA))->toBeTrue();
    expect(Gate::forUser($user)->allows('view', $groupB))->toBeFalse();

    try {
        Gate::forUser($user)->authorize('view', $groupB);

        $this->fail('Expected canonical wrong-tenant group authorization to be denied.');
    } catch (AuthorizationException $exception) {
        expect($exception->status())->toBe(404);
    }
});

it('keeps BackupSchedulePolicy scoped to the remembered canonical admin tenant', function (): void {
    $tenantA = ManagedEnvironment::factory()->create();
    [$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'owner');
    $tenantB = ManagedEnvironment::factory()->create(['workspace_id' => (int) $tenantA->workspace_id]);
    createUserWithTenant(tenant: $tenantB, user: $user, role: 'owner');

    $scheduleA = BackupSchedule::create([
        'managed_environment_id' => (int) $tenantA->getKey(),
        'name' => 'Allowed schedule',
        'is_enabled' => true,
        'timezone' => 'UTC',
        'frequency' => 'daily',
        'time_of_day' => '10:00:00',
        'days_of_week' => null,
        'policy_types' => ['deviceConfiguration'],
        'include_foundations' => true,
        'retention_keep_last' => 30,
    ]);

    $scheduleB = BackupSchedule::create([
        'managed_environment_id' => (int) $tenantB->getKey(),
        'name' => 'Blocked schedule',
        'is_enabled' => true,
        'timezone' => 'UTC',
        'frequency' => 'daily',
        'time_of_day' => '11:00:00',
        'days_of_week' => null,
        'policy_types' => ['deviceConfiguration'],
        'include_foundations' => true,
        'retention_keep_last' => 30,
    ]);

    $this->actingAs($user);
    Filament::setCurrentPanel('admin');
    Filament::setTenant(null, true);
    Filament::bootCurrentPanel();

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenantA->workspace_id => (int) $tenantA->getKey(),
    ]);

    expect(Gate::forUser($user)->allows('view', $scheduleA))->toBeTrue();
    expect(Gate::forUser($user)->allows('view', $scheduleB))->toBeFalse();

    try {
        Gate::forUser($user)->authorize('delete', $scheduleB);

        $this->fail('Expected canonical wrong-tenant schedule authorization to be denied.');
    } catch (AuthorizationException $exception) {
        expect($exception->status())->toBe(404);
    }
});

it('keeps FindingPolicy scoped to the remembered canonical admin tenant', function (): void {
    $tenantA = ManagedEnvironment::factory()->create();
    [$user, $tenantA] = createUserWithTenant(tenant: $tenantA, role: 'manager');
    $tenantB = ManagedEnvironment::factory()->create(['workspace_id' => (int) $tenantA->workspace_id]);
    createUserWithTenant(tenant: $tenantB, user: $user, role: 'manager');

    $findingA = Finding::factory()->for($tenantA)->create();
    $findingB = Finding::factory()->for($tenantB)->create();

    $this->actingAs($user);
    Filament::setCurrentPanel('admin');
    Filament::setTenant(null, true);
    Filament::bootCurrentPanel();

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenantA->workspace_id => (int) $tenantA->getKey(),
    ]);

    expect(Gate::forUser($user)->allows('view', $findingA))->toBeTrue();
    expect(Gate::forUser($user)->allows('view', $findingB))->toBeFalse();

    try {
        Gate::forUser($user)->authorize('triage', $findingB);

        $this->fail('Expected canonical wrong-tenant finding authorization to be denied.');
    } catch (AuthorizationException $exception) {
        expect($exception->status())->toBe(404);
    }
});

it('keeps in-scope capability denials forbidden while wrong-tenant denials stay not found', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$readonly, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');

    $schedule = BackupSchedule::create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'name' => 'Readonly schedule',
        'is_enabled' => true,
        'timezone' => 'UTC',
        'frequency' => 'daily',
        'time_of_day' => '10:00:00',
        'days_of_week' => null,
        'policy_types' => ['deviceConfiguration'],
        'include_foundations' => true,
        'retention_keep_last' => 30,
    ]);

    $finding = Finding::factory()->for($tenant)->create();

    $this->actingAs($readonly);
    Filament::setCurrentPanel('admin');
    Filament::setTenant(null, true);
    Filament::bootCurrentPanel();

    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenant->workspace_id => (int) $tenant->getKey(),
    ]);

    expect(fn () => Gate::forUser($readonly)->authorize('delete', $schedule))
        ->toThrow(AuthorizationException::class);

    expect(fn () => Gate::forUser($readonly)->authorize('triage', $finding))
        ->toThrow(AuthorizationException::class);
});
