<?php

declare(strict_types=1);

use App\Filament\Resources\BackupScheduleResource;
use App\Filament\Resources\BackupScheduleResource\Pages\ListBackupSchedules;
use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\BackupSetResource\Pages\ListBackupSets;
use App\Filament\Resources\EntraGroupResource;
use App\Filament\Resources\EntraGroupResource\Pages\ListEntraGroups;
use App\Filament\Resources\FindingResource;
use App\Filament\Resources\FindingResource\Pages\ListFindings;
use App\Filament\Resources\InventoryItemResource;
use App\Filament\Resources\InventoryItemResource\Pages\ListInventoryItems;
use App\Filament\Resources\PolicyResource;
use App\Filament\Resources\PolicyResource\Pages\ListPolicies;
use App\Filament\Resources\PolicyVersionResource;
use App\Filament\Resources\PolicyVersionResource\Pages\ListPolicyVersions;
use App\Filament\Resources\RestoreRunResource;
use App\Filament\Resources\RestoreRunResource\Pages\ListRestoreRuns;
use App\Models\BackupSchedule;
use App\Models\BackupSet;
use App\Models\EntraGroup;
use App\Models\Finding;
use App\Models\InventoryItem;
use App\Models\ManagedEnvironment;
use App\Models\Policy;
use App\Models\PolicyVersion;
use App\Models\RestoreRun;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

function tenantOwnedAdminSession(ManagedEnvironment $tenant): array
{
    return [
        WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
        WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY => [
            (string) $tenant->workspace_id => (int) $tenant->getKey(),
        ],
    ];
}

dataset('tenant-owned-list-pages', [
    'policy list' => [
        ListPolicies::class,
        static fn (ManagedEnvironment $tenant, string $label): Policy => Policy::factory()->for($tenant)->create([
            'display_name' => $label,
        ]),
    ],
    'policy-version list' => [
        ListPolicyVersions::class,
        static function (ManagedEnvironment $tenant, string $label): PolicyVersion {
            $policy = Policy::factory()->for($tenant)->create([
                'display_name' => $label.' policy',
            ]);

            return PolicyVersion::factory()->for($tenant)->for($policy)->create([
                'version_number' => random_int(1, 9999),
            ]);
        },
    ],
    'backup-schedule list' => [
        ListBackupSchedules::class,
        static function (ManagedEnvironment $tenant, string $label): BackupSchedule {
            return BackupSchedule::create([
                'managed_environment_id' => (int) $tenant->getKey(),
                'name' => $label,
                '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,
                'next_run_at' => now()->addHour(),
            ]);
        },
    ],
    'backup-set list' => [
        ListBackupSets::class,
        static fn (ManagedEnvironment $tenant, string $label): BackupSet => BackupSet::factory()->for($tenant)->create([
            'name' => $label,
        ]),
    ],
    'restore-run list' => [
        ListRestoreRuns::class,
        static function (ManagedEnvironment $tenant, string $label): RestoreRun {
            $backupSet = BackupSet::factory()->for($tenant)->create([
                'name' => $label.' backup set',
            ]);

            return RestoreRun::factory()->for($tenant)->for($backupSet)->create();
        },
    ],
    'inventory-item list' => [
        ListInventoryItems::class,
        static fn (ManagedEnvironment $tenant, string $label): InventoryItem => InventoryItem::factory()->create([
            'managed_environment_id' => (int) $tenant->getKey(),
            'display_name' => $label,
        ]),
    ],
    'finding list' => [
        ListFindings::class,
        static fn (ManagedEnvironment $tenant, string $label): Finding => Finding::factory()->for($tenant)->create(),
    ],
    'entra-group list' => [
        ListEntraGroups::class,
        static fn (ManagedEnvironment $tenant, string $label): EntraGroup => EntraGroup::factory()->for($tenant)->create([
            'display_name' => $label,
        ]),
    ],
]);

dataset('tenant-owned-detail-pages', [
    'policy view' => [
        PolicyResource::class,
        'view',
        static fn (ManagedEnvironment $tenant, string $label): Policy => Policy::factory()->for($tenant)->create([
            'display_name' => $label,
        ]),
    ],
    'policy-version view' => [
        PolicyVersionResource::class,
        'view',
        static function (ManagedEnvironment $tenant, string $label): PolicyVersion {
            $policy = Policy::factory()->for($tenant)->create([
                'display_name' => $label.' policy',
            ]);

            return PolicyVersion::factory()->for($tenant)->for($policy)->create([
                'version_number' => random_int(1, 9999),
            ]);
        },
    ],
    'backup-set view' => [
        BackupSetResource::class,
        'view',
        static fn (ManagedEnvironment $tenant, string $label): BackupSet => BackupSet::factory()->for($tenant)->create([
            'name' => $label,
        ]),
    ],
    'restore-run view' => [
        RestoreRunResource::class,
        'view',
        static function (ManagedEnvironment $tenant, string $label): RestoreRun {
            $backupSet = BackupSet::factory()->for($tenant)->create([
                'name' => $label.' backup set',
            ]);

            return RestoreRun::factory()->for($tenant)->for($backupSet)->create();
        },
    ],
    'finding view' => [
        FindingResource::class,
        'view',
        static fn (ManagedEnvironment $tenant, string $label): Finding => Finding::factory()->for($tenant)->create(),
    ],
    'entra-group view' => [
        EntraGroupResource::class,
        'view',
        static fn (ManagedEnvironment $tenant, string $label): EntraGroup => EntraGroup::factory()->for($tenant)->create([
            'display_name' => $label,
        ]),
    ],
    'backup-schedule edit' => [
        BackupScheduleResource::class,
        'edit',
        static function (ManagedEnvironment $tenant, string $label): BackupSchedule {
            return BackupSchedule::create([
                'managed_environment_id' => (int) $tenant->getKey(),
                'name' => $label,
                '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,
                'next_run_at' => now()->addHour(),
            ]);
        },
    ],
]);

it('scopes covered tenant-owned admin lists to the remembered canonical tenant', function (string $pageClass, Closure $makeRecord): 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');

    $allowed = $makeRecord($tenantA, 'Allowed record');
    $blocked = $makeRecord($tenantB, 'Blocked record');

    $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(),
    ]);

    Livewire::actingAs($user)->test($pageClass)
        ->assertCanSeeTableRecords([$allowed])
        ->assertCanNotSeeTableRecords([$blocked]);
})->with('tenant-owned-list-pages');

it('returns not found for covered tenant-owned admin detail pages outside the remembered canonical tenant', function (string $resourceClass, string $page, Closure $makeRecord): 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');

    $allowed = $makeRecord($tenantA, 'Allowed record');
    $blocked = $makeRecord($tenantB, 'Blocked record');

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

    $session = tenantOwnedAdminSession($tenantA);

    $this->withSession($session)
        ->get($resourceClass::getUrl($page, ['record' => $allowed], panel: 'admin', tenant: $tenantA))
        ->assertSuccessful();

    $this->withSession($session)
        ->get($resourceClass::getUrl($page, ['record' => $blocked], panel: 'admin', tenant: $tenantA))
        ->assertNotFound();
})->with('tenant-owned-detail-pages');

it('returns not found for admin inventory item detail pages outside the explicit tenant query scope', 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');

    $allowed = InventoryItem::factory()->create([
        'managed_environment_id' => (int) $tenantA->getKey(),
        'display_name' => 'Allowed inventory item',
    ]);

    $blocked = InventoryItem::factory()->create([
        'managed_environment_id' => (int) $tenantB->getKey(),
        'display_name' => 'Blocked inventory item',
    ]);

    $session = tenantOwnedAdminSession($tenantA);
    $allowedUrl = InventoryItemResource::getUrl('view', ['record' => $allowed], panel: 'admin', tenant: $tenantA).'?tenant='.(string) $tenantA->external_id;
    $blockedUrl = InventoryItemResource::getUrl('view', ['record' => $blocked], panel: 'admin', tenant: $tenantA).'?tenant='.(string) $tenantA->external_id;

    $this->actingAs($user)
        ->withSession($session)
        ->get($allowedUrl)
        ->assertSuccessful();

    $this->actingAs($user)
        ->withSession($session)
        ->get($blockedUrl)
        ->assertNotFound();
});
