<?php

declare(strict_types=1);

use App\Filament\Resources\TenantResource;
use App\Filament\Resources\TenantResource\Pages\ListTenants;
use App\Filament\Resources\TenantResource\Pages\ViewTenant;
use App\Models\Tenant;
use App\Models\User;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Livewire\Livewire;

describe('Tenant resource authorization', function () {
    it('cannot be created by non-members', function () {
        $user = User::factory()->create();

        $this->actingAs($user);

        expect(TenantResource::canCreate())->toBeFalse();
    });

    it('cannot be created via CRUD (onboarding wizard is the only path)', function () {
        [$user] = createUserWithTenant(role: 'manager');

        $this->actingAs($user);

        expect(TenantResource::canCreate())->toBeFalse();
    });

    it('can be edited by managers (TENANT_MANAGE)', function () {
        [$user, $tenant] = createUserWithTenant(role: 'manager');

        $this->actingAs($user);

        expect(TenantResource::canEdit($tenant))->toBeTrue();
    });

    it('cannot be deleted by managers (TENANT_DELETE)', function () {
        [$user, $tenant] = createUserWithTenant(role: 'manager');

        $this->actingAs($user);

        expect(TenantResource::canDelete($tenant))->toBeFalse();
    });

    it('can be deleted by owners (TENANT_DELETE)', function () {
        [$user, $tenant] = createUserWithTenant(role: 'owner');

        $this->actingAs($user);

        expect(TenantResource::canDelete($tenant))->toBeTrue();
    });

    it('cannot edit tenants it cannot access', function () {
        [$user] = createUserWithTenant(role: 'manager');
        $otherTenant = Tenant::factory()->create();

        $this->actingAs($user);

        expect(TenantResource::canEdit($otherTenant))->toBeFalse();
    });

    it('returns not found for tenant detail pages outside the actor tenant scope', function (): void {
        [$user] = createUserWithTenant(role: 'owner', ensureDefaultMicrosoftProviderConnection: false);
        $otherTenant = Tenant::factory()->active()->create();

        $this->actingAs($user)
            ->withSession([WorkspaceContext::SESSION_KEY => (int) $otherTenant->workspace_id])
            ->get(TenantResource::getUrl('view', ['record' => $otherTenant], panel: 'admin'))
            ->assertNotFound();
    });

    it('returns not found for numeric tenant detail paths outside the actor tenant scope', function (): void {
        [$user, $tenant] = createUserWithTenant(role: 'owner', ensureDefaultMicrosoftProviderConnection: false);
        $otherTenant = Tenant::factory()->active()->create();

        $this->actingAs($user)
            ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
            ->get(TenantResource::getUrl('view', ['record' => (string) $otherTenant->getKey()], panel: 'admin'))
            ->assertNotFound();
    });

    it('does not grant lifecycle mutation abilities for inaccessible tenants regardless of lifecycle state', function (\Closure $tenantFactory): void {
        [$user] = createUserWithTenant(role: 'owner', ensureDefaultMicrosoftProviderConnection: false);
        $otherTenant = $tenantFactory();

        $this->actingAs($user);

        expect(TenantResource::canEdit($otherTenant))->toBeFalse()
            ->and(TenantResource::canDelete($otherTenant))->toBeFalse();
    })->with([
        'draft' => [fn (): Tenant => Tenant::factory()->draft()->create()],
        'onboarding' => [fn (): Tenant => Tenant::factory()->onboarding()->create()],
        'active' => [fn (): Tenant => Tenant::factory()->active()->create()],
        'archived' => [fn (): Tenant => Tenant::factory()->archived()->create()],
    ]);

    it('keeps onboarding and archived tenants manageable when the actor is entitled', function () {
        $onboardingTenant = Tenant::factory()->onboarding()->create();
        [$user, $onboardingTenant] = createUserWithTenant(
            tenant: $onboardingTenant,
            role: 'manager',
            ensureDefaultMicrosoftProviderConnection: false,
        );

        $archivedTenant = Tenant::factory()->archived()->create([
            'workspace_id' => (int) $onboardingTenant->workspace_id,
        ]);

        createUserWithTenant(
            tenant: $archivedTenant,
            user: $user,
            role: 'manager',
            workspaceRole: 'manager',
            ensureDefaultMicrosoftProviderConnection: false,
        );

        $archivedTenant = Tenant::withTrashed()->findOrFail((int) $archivedTenant->getKey());

        $this->actingAs($user);

        expect(TenantResource::canEdit($onboardingTenant))->toBeTrue()
            ->and(TenantResource::canEdit($archivedTenant))->toBeTrue()
            ->and(TenantResource::canDelete($onboardingTenant))->toBeFalse()
            ->and(TenantResource::canDelete($archivedTenant))->toBeFalse();
    });

    it('keeps mutation capability checks separate from lifecycle-specific action visibility', function (): void {
        $onboardingTenant = Tenant::factory()->onboarding()->create();
        [$user, $onboardingTenant] = createUserWithTenant(
            tenant: $onboardingTenant,
            role: 'owner',
            ensureDefaultMicrosoftProviderConnection: false,
        );

        $archivedTenant = Tenant::factory()->archived()->create([
            'workspace_id' => (int) $onboardingTenant->workspace_id,
        ]);

        createUserWithTenant(
            tenant: $archivedTenant,
            user: $user,
            role: 'owner',
            workspaceRole: 'owner',
            ensureDefaultMicrosoftProviderConnection: false,
        );

        $archivedTenant = Tenant::withTrashed()->findOrFail((int) $archivedTenant->getKey());

        $this->actingAs($user);

        expect(TenantResource::canDelete($onboardingTenant))->toBeTrue()
            ->and(TenantResource::canDelete($archivedTenant))->toBeTrue();
    });

    it('keeps the tenant resource index usable with no selected tenant context', function () {
        [$user, $tenant] = createUserWithTenant(role: 'owner');

        $this->actingAs($user)
            ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
            ->get(TenantResource::getUrl(panel: 'admin'))
            ->assertSuccessful()
            ->assertSee((string) $tenant->name);
    });

    it('resolves archive and verify actions as enabled for owners on active tenant surfaces', function (): void {
        $tenant = Tenant::factory()->active()->create();
        [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false);

        session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

        Livewire::actingAs($user)
            ->test(ListTenants::class)
            ->assertTableActionVisible('archive', $tenant)
            ->assertTableActionEnabled('archive', $tenant)
            ->assertTableActionVisible('syncTenant', $tenant)
            ->assertTableActionEnabled('syncTenant', $tenant)
            ->assertTableActionVisible('verify', $tenant)
            ->assertTableActionEnabled('verify', $tenant);

        Filament::setTenant(null, true);

        Livewire::actingAs($user)
            ->test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
            ->assertActionVisible('archive')
            ->assertActionEnabled('archive')
            ->assertActionVisible('syncTenant')
            ->assertActionEnabled('syncTenant')
            ->assertActionVisible('verify')
            ->assertActionEnabled('verify');
    });

    it('keeps lifecycle mutation actions visible but disabled for managers without delete capability', function (): void {
        $tenant = Tenant::factory()->active()->create();
        [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'manager', ensureDefaultMicrosoftProviderConnection: false);

        session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);

        Livewire::actingAs($user)
            ->test(ListTenants::class)
            ->assertTableActionVisible('archive', $tenant)
            ->assertTableActionDisabled('archive', $tenant);

        Filament::setTenant(null, true);

        Livewire::actingAs($user)
            ->test(ViewTenant::class, ['record' => $tenant->getRouteKey()])
            ->assertActionVisible('archive')
            ->assertActionDisabled('archive');
    });
});
