<?php

declare(strict_types=1);

use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
use App\Filament\Pages\Reviews\ReviewRegister;
use App\Filament\Resources\EnvironmentReviewResource;
use App\Filament\Resources\EnvironmentReviewResource\Pages\ListEnvironmentReviews;
use App\Filament\Resources\EnvironmentReviewResource\Pages\ViewEnvironmentReview;
use App\Models\EnvironmentReview;
use App\Models\ManagedEnvironment;
use App\Models\ReviewPack;
use App\Models\User;
use App\Services\EnvironmentReviews\EnvironmentReviewLifecycleService;
use App\Support\EnvironmentReviewStatus;
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Illuminate\Support\Facades\Storage;
use Livewire\Features\SupportTesting\Testable;
use Livewire\Livewire;
use Tests\Feature\Concerns\BuildsGovernanceArtifactTruthFixtures;

uses(BuildsGovernanceArtifactTruthFixtures::class);

beforeEach(function (): void {
    Storage::fake('exports');
});

function environmentReviewContractHeaderActions(Testable $component): array
{
    $instance = $component->instance();

    if ($instance->getCachedHeaderActions() === []) {
        $instance->cacheInteractsWithHeaderActions();
    }

    return $instance->getCachedHeaderActions();
}

it('disables environment-review global search while keeping the view page available for resource inspection', function (): void {
    $reflection = new ReflectionClass(EnvironmentReviewResource::class);

    expect($reflection->getStaticPropertyValue('isGloballySearchable'))->toBeFalse()
        ->and(array_keys(EnvironmentReviewResource::getPages()))->toContain('view');
});

it('keeps environment review list and canonical register empty states to a single CTA', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $this->actingAs($user);
    setAdminEnvironmentContext($tenant);

    Livewire::actingAs($user)
        ->test(ListEnvironmentReviews::class)
        ->assertTableEmptyStateActionsExistInOrder(['create_first_review'])
        ->assertSee('No environment reviews yet')
        ->mountAction('create_review')
        ->assertActionMounted('create_review');

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

    Livewire::actingAs($user)
        ->test(ReviewRegister::class)
        ->searchTable('no-such-review')
        ->assertTableEmptyStateActionsExistInOrder(['clear_filters_empty'])
        ->assertSee('No review records match this view');
});

it('keeps environment review list inspection on row click and reserves the row action for executive export', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');
    $review = composeEnvironmentReviewForTest($tenant, $user);

    $this->actingAs($user);
    setAdminEnvironmentContext($tenant);

    $livewire = Livewire::actingAs($user)
        ->test(ListEnvironmentReviews::class)
        ->assertCanSeeTableRecords([$review]);

    $table = $livewire->instance()->getTable();
    $rowActionNames = collect($table->getActions())
        ->map(static fn ($action): ?string => $action->getName())
        ->filter()
        ->values()
        ->all();

    expect($rowActionNames)->toEqualCanonicalizing(['export_executive_pack'])
        ->and($table->getBulkActions())->toBeEmpty()
        ->and($table->getRecordUrl($review))->toBe(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant));
});

it('requires confirmation for destructive environment-review actions and preserves disabled management visibility for readonly users', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$owner, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
    [$readonly] = createUserWithTenant(tenant: $tenant, user: User::factory()->create(), role: 'readonly');
    $review = composeEnvironmentReviewForTest($tenant, $owner);
    $refreshRule = GovernanceActionCatalog::rule('refresh_review');

    setAdminEnvironmentContext($tenant);

    Livewire::actingAs($readonly)
        ->test(ViewEnvironmentReview::class, ['record' => $review->getKey()])
        ->assertActionVisible('refresh_review')
        ->assertActionDisabled('refresh_review')
        ->assertActionVisible('publish_review')
        ->assertActionDisabled('publish_review')
        ->assertActionVisible('export_executive_pack')
        ->assertActionDisabled('export_executive_pack')
        ->assertActionVisible('archive_review')
        ->assertActionDisabled('archive_review');

    Livewire::actingAs($owner)
        ->test(ViewEnvironmentReview::class, ['record' => $review->getKey()])
        ->assertActionExists('refresh_review', fn (Action $action): bool => $action->getLabel() === $refreshRule->canonicalLabel
            && $action->isConfirmationRequired()
            && $action->getModalHeading() === $refreshRule->modalHeading
            && $action->getModalDescription() === $refreshRule->modalDescription)
        ->mountAction('refresh_review')
        ->assertActionMounted('refresh_review');

    Livewire::actingAs($owner)
        ->test(ViewEnvironmentReview::class, ['record' => $review->getKey()])
        ->mountAction('publish_review')
        ->assertActionMounted('publish_review')
        ->callMountedAction()
        ->assertHasActionErrors(['publish_reason']);

    $published = app(EnvironmentReviewLifecycleService::class)->publish($review, $owner, 'Ready for publication.');

    Livewire::actingAs($owner)
        ->test(ViewEnvironmentReview::class, ['record' => $published->getKey()])
        ->mountAction('archive_review')
        ->assertActionMounted('archive_review')
        ->callMountedAction()
        ->assertHasActionErrors(['archive_reason']);
});

it('keeps environment review header hierarchy to one primary action and moves related links into summary context', function (): void {
    [$owner, $tenant] = createUserWithTenant(role: 'owner');
    $review = composeEnvironmentReviewForTest($tenant, $owner);

    setAdminEnvironmentContext($tenant);

    $this->actingAs($owner)
        ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant))
        ->assertOk()
        ->assertSee('Related context')
        ->assertSee('Evidence snapshot');

    $component = Livewire::actingAs($owner)
        ->test(ViewEnvironmentReview::class, ['record' => $review->getKey()]);

    $topLevelActionNames = collect(environmentReviewContractHeaderActions($component))
        ->reject(static fn ($action): bool => $action instanceof ActionGroup)
        ->map(static fn ($action): ?string => $action instanceof Action ? $action->getName() : null)
        ->filter()
        ->values()
        ->all();
    $groupLabels = collect(environmentReviewContractHeaderActions($component))
        ->filter(static fn ($action): bool => $action instanceof ActionGroup)
        ->map(static fn (ActionGroup $action): string => (string) $action->getLabel())
        ->values()
        ->all();

    expect($topLevelActionNames)->toBe(['publish_review'])
        ->and($groupLabels)->toBe(['More', 'Danger']);
});

it('uses the current review-pack download as the only customer-workspace detail header action', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'readonly');
    $snapshot = seedEnvironmentReviewEvidence($tenant);

    $review = composeEnvironmentReviewForTest($tenant, $user, $snapshot);
    $review->forceFill([
        'status' => EnvironmentReviewStatus::Published->value,
        'published_at' => now(),
        'published_by_user_id' => (int) $user->getKey(),
    ])->save();

    Storage::disk('exports')->put('review-packs/customer-detail-primary.zip', 'PK-test');

    $pack = ReviewPack::factory()->ready()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'environment_review_id' => (int) $review->getKey(),
        'evidence_snapshot_id' => (int) $snapshot->getKey(),
        'initiated_by_user_id' => (int) $user->getKey(),
        'file_path' => 'review-packs/customer-detail-primary.zip',
        'file_disk' => 'exports',
    ]);

    $review->forceFill(['current_export_review_pack_id' => (int) $pack->getKey()])->save();

    setAdminEnvironmentContext($tenant);

    $this->actingAs($user)
        ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant).'?'.http_build_query([
            CustomerReviewWorkspace::DETAIL_CONTEXT_QUERY_KEY => 1,
        ]))
        ->assertOk()
        ->assertSee('Artifact reference')
        ->assertSee('Current export')
        ->assertSee('#'.$pack->getKey())
        ->assertSee('Lifecycle')
        ->assertSee('Current')
        ->assertSee('Retention')
        ->assertSee('Retained');

    $component = Livewire::withQueryParams([CustomerReviewWorkspace::DETAIL_CONTEXT_QUERY_KEY => 1])
        ->actingAs($user)
        ->test(ViewEnvironmentReview::class, ['record' => $review->getKey()])
        ->assertActionVisible('download_current_review_pack')
        ->assertActionEnabled('download_current_review_pack')
        ->assertActionDoesNotExist('publish_review')
        ->assertActionDoesNotExist('refresh_review')
        ->assertActionDoesNotExist('create_next_review')
        ->assertActionDoesNotExist('export_executive_pack')
        ->assertActionDoesNotExist('archive_review');

    $component->assertActionExists('download_current_review_pack', fn (Action $action): bool => $action->getLabel() === 'Download governance package');

    $topLevelActionNames = collect(environmentReviewContractHeaderActions($component))
        ->map(static fn ($action): ?string => $action instanceof Action ? $action->getName() : null)
        ->filter()
        ->values()
        ->all();

    expect($topLevelActionNames)->toBe(['download_current_review_pack']);
});

it('shows publication truth and next-step guidance when a review is not yet publishable', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$owner, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
    $snapshot = seedEnvironmentReviewEvidence($tenant);

    $review = EnvironmentReview::query()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'evidence_snapshot_id' => (int) $snapshot->getKey(),
        'initiated_by_user_id' => (int) $owner->getKey(),
        'status' => 'draft',
        'completeness_state' => 'complete',
        'summary' => [
            'publish_blockers' => ['Review the approval note before publication.'],
            'section_state_counts' => ['complete' => 6, 'partial' => 0, 'missing' => 0, 'stale' => 0],
        ],
        'fingerprint' => hash('sha256', 'environment-review-ui-contract'),
        'generated_at' => now(),
    ]);

    $this->actingAs($owner)
        ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant))
        ->assertOk()
        ->assertSee('Outcome summary')
        ->assertDontSee('Artifact truth')
        ->assertSee('Artifact reference')
        ->assertSee('Review #'.$review->getKey())
        ->assertSee('Lifecycle')
        ->assertSee('Current')
        ->assertSee('Retention')
        ->assertSee('Retained')
        ->assertSee('Publication blocked')
        ->assertSee('Resolve the review blockers before publication');
});

it('keeps executive posture from claiming publication readiness for internal-only reviews', function (): void {
    $tenant = ManagedEnvironment::factory()->create();
    [$owner, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');

    $review = $this->makeInternalOnlyArtifactTruthReview($tenant, $owner);

    $this->actingAs($owner)
        ->get(EnvironmentReviewResource::environmentScopedUrl('view', ['record' => $review], $tenant))
        ->assertOk()
        ->assertSee('Internal only')
        ->assertSee('Publication readiness')
        ->assertSee('This review is currently safe for internal use only.')
        ->assertDontSee('This review is ready for publication and executive-pack export.');
});
