<?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\AuditLog;
use App\Models\Tenant;
use App\Services\Audit\WorkspaceAuditLogger;
use App\Support\Audit\AuditActionId;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Actions\Action;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;

uses(RefreshDatabase::class);

it('shows resume onboarding instead of archive for draft and onboarding tenants on list and detail surfaces', function (\Closure $tenantFactory): void {
    $tenant = $tenantFactory();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false);

    createOnboardingDraft([
        'workspace' => $tenant->workspace,
        'tenant' => $tenant,
        'started_by' => $user,
        'updated_by' => $user,
        'state' => [
            'entra_tenant_id' => (string) $tenant->tenant_id,
            'tenant_name' => (string) $tenant->name,
        ],
    ]);

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

    Livewire::actingAs($user)
        ->test(ListTenants::class)
        ->assertTableActionVisible('related_onboarding', $tenant)
        ->assertTableActionExists('related_onboarding', fn (Action $action): bool => $action->getLabel() === 'Resume onboarding', $tenant)
        ->assertTableActionHidden('archive', $tenant)
        ->assertTableActionHidden('restore', $tenant);

    Filament::setTenant(null, true);

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

    expect(collect(TenantResource::tenantViewContextEntries($tenant))
        ->firstWhere('key', 'related_onboarding')['value'] ?? null)
        ->toBe('Resume onboarding');
})->with([
    'draft' => [fn (): Tenant => Tenant::factory()->draft()->create()],
    'onboarding' => [fn (): Tenant => Tenant::factory()->onboarding()->create()],
]);

it('shows archive only for active tenants on list and detail surfaces', function (): void {
    $tenant = Tenant::factory()->active()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

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

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

    Filament::setTenant(null, true);

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

it('shows restore only for archived tenants on list and detail surfaces', function (): void {
    $tenant = Tenant::factory()->archived()->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('restore', $tenant)
        ->assertTableActionHidden('archive', $tenant)
        ->assertTableActionHidden('related_onboarding', $tenant);

    Filament::setTenant(null, true);

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

    expect(collect(TenantResource::tenantViewContextEntries($tenant))->pluck('key')->all())
        ->not->toContain('related_onboarding');
});

it('shows verification only for active tenants on administrative list and detail surfaces', function (
    \Closure $tenantFactory,
    bool $shouldBeVisible,
): void {
    $tenant = $tenantFactory();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false);

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

    $list = Livewire::actingAs($user)->test(ListTenants::class);

    if ($shouldBeVisible) {
        $list->assertTableActionVisible('verify', $tenant);
    } else {
        $list->assertTableActionHidden('verify', $tenant);
    }

    Filament::setTenant(null, true);

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

    if ($shouldBeVisible) {
        $view->assertActionVisible('verify');
    } else {
        $view->assertActionHidden('verify');
    }
})->with([
    'draft' => [fn (): Tenant => Tenant::factory()->draft()->create(), false],
    'onboarding' => [fn (): Tenant => Tenant::factory()->onboarding()->create(), false],
    'active' => [fn (): Tenant => Tenant::factory()->active()->create(), true],
    'archived' => [fn (): Tenant => Tenant::factory()->archived()->create(), false],
]);

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

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

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

it('shows capability-denied actions as disabled but keeps lifecycle-denied actions hidden', function (): void {
    $activeTenant = Tenant::factory()->active()->create([
        'name' => 'Capability Denied Active Tenant',
    ]);
    [$user, $activeTenant] = createUserWithTenant(tenant: $activeTenant, role: 'manager', ensureDefaultMicrosoftProviderConnection: false);

    $onboardingTenant = Tenant::factory()->onboarding()->create([
        'workspace_id' => (int) $activeTenant->workspace_id,
        'name' => 'Lifecycle Denied Onboarding Tenant',
    ]);

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

    createOnboardingDraft([
        'workspace' => $onboardingTenant->workspace,
        'tenant' => $onboardingTenant,
        'started_by' => $user,
        'updated_by' => $user,
        'state' => [
            'entra_tenant_id' => (string) $onboardingTenant->tenant_id,
            'tenant_name' => (string) $onboardingTenant->name,
        ],
    ]);

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

    Livewire::actingAs($user)
        ->test(ListTenants::class)
        ->assertTableActionVisible('archive', $activeTenant)
        ->assertTableActionDisabled('archive', $activeTenant)
        ->assertTableActionHidden('archive', $onboardingTenant)
        ->assertTableActionVisible('related_onboarding', $onboardingTenant);

    Filament::setTenant(null, true);

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

    Livewire::actingAs($user)
        ->test(ViewTenant::class, ['record' => $onboardingTenant->getRouteKey()])
        ->assertActionHidden('archive')
        ->assertActionHidden('restore');

    expect(collect(TenantResource::tenantViewContextEntries($onboardingTenant))->pluck('key')->all())
        ->toContain('related_onboarding');
});

it('returns 404 on tenant detail routes for non-members regardless of lifecycle state', function (\Closure $tenantFactory): void {
    $tenant = $tenantFactory();
    [$user] = createUserWithTenant(role: 'owner', ensureDefaultMicrosoftProviderConnection: false);

    $this->actingAs($user)
        ->get(TenantResource::getUrl('view', ['record' => $tenant]))
        ->assertNotFound();
})->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 tenant detail lifecycle actions bound to the viewed record instead of the selected header tenant', function (): void {
    $selectedTenant = Tenant::factory()->active()->create();
    [$user, $selectedTenant] = createUserWithTenant(tenant: $selectedTenant, role: 'owner', ensureDefaultMicrosoftProviderConnection: false);

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

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

    Filament::setTenant($selectedTenant, true);

    Livewire::actingAs($user)
        ->test(ViewTenant::class, ['record' => $archivedTenant->getRouteKey()])
        ->assertActionVisible('restore')
        ->assertActionHidden('archive');

    expect(collect(TenantResource::tenantViewContextEntries($archivedTenant))->pluck('key')->all())
        ->not->toContain('related_onboarding');
});

it('refuses lifecycle-invalid archive and restore mutations without changing tenant state', function (): void {
    $activeTenant = Tenant::factory()->active()->create();
    [$user, $activeTenant] = createUserWithTenant(tenant: $activeTenant, role: 'owner');

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

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

    $this->actingAs($user);

    $auditLogger = app(WorkspaceAuditLogger::class);

    TenantResource::restoreTenant($activeTenant, $auditLogger);
    TenantResource::archiveTenant($onboardingTenant, $auditLogger, 'Trying to archive an onboarding tenant should be rejected.');

    $activeTenant->refresh();
    $onboardingTenant->refresh();

    expect($activeTenant->trashed())->toBeFalse()
        ->and($onboardingTenant->trashed())->toBeFalse()
        ->and($onboardingTenant->status)->toBe(Tenant::STATUS_ONBOARDING)
        ->and(AuditLog::query()
            ->whereIn('action', [
                AuditActionId::TenantArchived->value,
                AuditActionId::TenantRestored->value,
            ])
            ->whereIn('resource_id', [
                (string) $activeTenant->getKey(),
                (string) $onboardingTenant->getKey(),
            ])
            ->exists())->toBeFalse();
});
