<?php

declare(strict_types=1);

use App\Filament\Pages\EnvironmentDashboard;
use App\Filament\Resources\BackupSetResource;
use App\Filament\Resources\RestoreRunResource;
use App\Filament\Resources\ManagedEnvironmentResource;
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentTriageArrivalContinuity;
use App\Models\AuditLog;
use App\Models\ManagedEnvironmentTriageReview;
use Filament\Actions\Action;
use App\Services\Auth\CapabilityResolver;
use App\Support\Audit\AuditActionId;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\TenantBackupHealthAssessment;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\Rbac\UiTooltips;
use App\Support\RestoreSafety\RestoreResultAttention;
use App\Support\Tenants\TenantRecoveryTriagePresentation;
use App\Support\Workspaces\WorkspaceContext;
use Livewire\Livewire;
use Tests\Feature\Concerns\BuildsPortfolioTriageFixtures;

use function Pest\Laravel\mock;

uses(BuildsPortfolioTriageFixtures::class);

function tenantDashboardArrivalUrl(\App\Models\ManagedEnvironment $tenant, array $state): string
{
    return EnvironmentDashboard::getUrl([
        PortfolioArrivalContextToken::QUERY_PARAMETER => PortfolioArrivalContextToken::encode($state),
    ], panel: 'admin', tenant: $tenant);
}

function tenantDashboardArrivalWidget(\App\Models\User $user, \App\Models\ManagedEnvironment $tenant, array $state): mixed
{
    test()->actingAs($user);
    session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
    setAdminEnvironmentContext($tenant);
    request()->attributes->remove('portfolio_triage.arrival_context');

    return Livewire::withQueryParams([
        PortfolioArrivalContextToken::QUERY_PARAMETER => PortfolioArrivalContextToken::encode($state),
    ])->actingAs($user)->test(ManagedEnvironmentTriageArrivalContinuity::class);
}

it('renders source surface, concern, next step, and return flow for workspace backup arrivals', function (): void {
    [$user, $tenant] = $this->makePortfolioTriageActor('Workspace Backup ManagedEnvironment');
    $this->actingAs($user);

    $arrivalUrl = tenantDashboardArrivalUrl($tenant, [
        'sourceSurface' => PortfolioArrivalContextToken::SOURCE_WORKSPACE_OVERVIEW,
        'tenantRouteKey' => (string) $tenant->external_id,
        'workspaceId' => (int) $tenant->workspace_id,
        'concernFamily' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
        'concernState' => TenantBackupHealthAssessment::POSTURE_ABSENT,
        'concernReason' => TenantBackupHealthAssessment::REASON_NO_BACKUP_BASIS,
    ]);

    $this->get($arrivalUrl)
        ->assertOk()
        ->assertSee('Triage arrival')
        ->assertSee('Workspace overview triage')
        ->assertSee('Backup health')
        ->assertSee('Absent')
        ->assertSee('Opened from workspace overview triage because no usable backup basis was visible.')
        ->assertSee('Open backup sets')
        ->assertSee('Return to workspace overview')
        ->assertSee(BackupSetResource::getUrl('index', [
            'backup_health_reason' => TenantBackupHealthAssessment::REASON_NO_BACKUP_BASIS,
        ], panel: 'admin', tenant: $tenant), false)
        ->assertSee(route('admin.home'), false);
});

it('renders registry arrival continuity with restore follow-up and preserved return filters', function (): void {
    [$user, $tenant] = $this->makePortfolioTriageActor('Registry Recovery ManagedEnvironment');
    $restoreRun = $this->seedPortfolioRecoveryConcern($tenant, RestoreResultAttention::STATE_COMPLETED_WITH_FOLLOW_UP);
    $this->actingAs($user);

    $returnUrl = ManagedEnvironmentResource::getUrl('index', [
        'recovery_evidence' => [TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_WEAKENED],
        'triage_sort' => TenantRecoveryTriagePresentation::TRIAGE_SORT_WORST_FIRST,
    ], panel: 'admin');

    $arrivalUrl = tenantDashboardArrivalUrl($tenant, [
        'sourceSurface' => PortfolioArrivalContextToken::SOURCE_TENANT_REGISTRY,
        'tenantRouteKey' => (string) $tenant->external_id,
        'workspaceId' => (int) $tenant->workspace_id,
        'concernFamily' => PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
        'concernState' => TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_WEAKENED,
        'concernReason' => RestoreResultAttention::STATE_COMPLETED_WITH_FOLLOW_UP,
        'returnFilters' => [
            'recovery_evidence' => [TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_WEAKENED],
            'triage_sort' => TenantRecoveryTriagePresentation::TRIAGE_SORT_WORST_FIRST,
        ],
    ]);

    $this->get($arrivalUrl)
        ->assertOk()
        ->assertSee('ManagedEnvironment registry triage')
        ->assertSee('Recovery evidence')
        ->assertSee('Weakened')
        ->assertSee('Open restore run')
        ->assertSee('Return to tenant triage')
        ->assertSee(RestoreRunResource::getUrl('view', [
            'record' => (int) $restoreRun?->getKey(),
            'recovery_posture_reason' => RestoreResultAttention::STATE_COMPLETED_WITH_FOLLOW_UP,
        ], panel: 'admin', tenant: $tenant), false)
        ->assertSee($returnUrl, false);
});

it('suppresses the continuity block for generic or malformed sessions', function (): void {
    [$user, $tenant] = $this->makePortfolioTriageActor('Generic ManagedEnvironment Session');
    $this->actingAs($user);

    $this->get(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $tenant))
        ->assertOk()
        ->assertDontSee('Triage arrival');

    $this->get(EnvironmentDashboard::getUrl([
        PortfolioArrivalContextToken::QUERY_PARAMETER => 'not-base64url',
    ], panel: 'admin', tenant: $tenant))
        ->assertOk()
        ->assertDontSee('Triage arrival');
});

it('keeps the continuity block truthful when current truth has changed and multiple concerns remain visible', function (): void {
    [$user, $tenant] = $this->makePortfolioTriageActor('Truth Shift ManagedEnvironment');
    $backupSet = $this->seedPortfolioBackupConcern($tenant, TenantBackupHealthAssessment::POSTURE_STALE);
    $this->seedPortfolioRecoveryConcern($tenant, RestoreResultAttention::STATE_PARTIAL, $backupSet);
    $this->actingAs($user);

    $arrivalUrl = tenantDashboardArrivalUrl($tenant, [
        'sourceSurface' => PortfolioArrivalContextToken::SOURCE_TENANT_REGISTRY,
        'tenantRouteKey' => (string) $tenant->external_id,
        'workspaceId' => (int) $tenant->workspace_id,
        'concernFamily' => PortfolioArrivalContextToken::FAMILY_RECOVERY_EVIDENCE,
        'concernState' => TenantRecoveryTriagePresentation::RECOVERY_EVIDENCE_UNVALIDATED,
        'concernReason' => 'no_history',
        'returnFilters' => [
            'triage_sort' => TenantRecoveryTriagePresentation::TRIAGE_SORT_WORST_FIRST,
        ],
    ]);

    $this->get($arrivalUrl)
        ->assertOk()
        ->assertSee('Current recovery evidence now looks Weakened.')
        ->assertSee('Backup posture also still needs follow-up.')
        ->assertSee('Open restore run');
});

it('degrades the next-step CTA when the operator cannot open deeper follow-up surfaces', function (): void {
    [$user, $tenant] = $this->makePortfolioTriageActor('Restricted Arrival ManagedEnvironment');
    $this->actingAs($user);

    mock(CapabilityResolver::class, function ($mock) use ($tenant): void {
        $mock->shouldReceive('primeMemberships')
            ->zeroOrMoreTimes();

        $mock->shouldReceive('isMember')
            ->andReturnUsing(static fn ($user, $resolvedTenant): bool => (int) $resolvedTenant->getKey() === (int) $tenant->getKey());

        $mock->shouldReceive('can')
            ->andReturnUsing(static function ($user, $resolvedTenant, string $capability) use ($tenant): bool {
                expect((int) $resolvedTenant->getKey())->toBe((int) $tenant->getKey());

                return $capability !== Capabilities::TENANT_VIEW;
            });
    });

    $arrivalUrl = tenantDashboardArrivalUrl($tenant, [
        'sourceSurface' => PortfolioArrivalContextToken::SOURCE_WORKSPACE_OVERVIEW,
        'tenantRouteKey' => (string) $tenant->external_id,
        'workspaceId' => (int) $tenant->workspace_id,
        'concernFamily' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
        'concernState' => TenantBackupHealthAssessment::POSTURE_ABSENT,
        'concernReason' => TenantBackupHealthAssessment::REASON_NO_BACKUP_BASIS,
    ]);

    $this->get($arrivalUrl)
        ->assertOk()
        ->assertSee('Triage arrival')
        ->assertSee(UiTooltips::INSUFFICIENT_PERMISSION)
        ->assertDontSee(BackupSetResource::getUrl('index', [
            'backup_health_reason' => TenantBackupHealthAssessment::REASON_NO_BACKUP_BASIS,
        ], panel: 'admin', tenant: $tenant), false);
});

it('shows review-state context and requires preview confirmation before marking the current concern reviewed', function (): void {
    [$user, $tenant] = $this->makePortfolioTriageActor(
        'Dashboard Review ManagedEnvironment',
        workspaceRole: 'owner',
    );
    $this->seedPortfolioBackupConcern($tenant, TenantBackupHealthAssessment::POSTURE_STALE);

    $component = tenantDashboardArrivalWidget($user, $tenant, [
        'sourceSurface' => PortfolioArrivalContextToken::SOURCE_TENANT_REGISTRY,
        'tenantRouteKey' => (string) $tenant->external_id,
        'workspaceId' => (int) $tenant->workspace_id,
        'concernFamily' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
        'concernState' => TenantBackupHealthAssessment::POSTURE_STALE,
        'concernReason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_STALE,
        'returnFilters' => $this->portfolioReturnFilters(
            [TenantBackupHealthAssessment::POSTURE_STALE],
        ),
    ])
        ->assertSee('Not reviewed')
        ->assertActionVisible('markReviewed')
        ->assertActionExists('markReviewed', fn (Action $action): bool => $action->isConfirmationRequired()
            && str_contains((string) $action->getModalDescription(), 'Concern family: Backup health')
            && str_contains((string) $action->getModalDescription(), 'Target state: Reviewed')
            && str_contains((string) $action->getModalDescription(), 'TenantPilot only'))
        ->mountAction('markReviewed');

    expect(ManagedEnvironmentTriageReview::query()->count())->toBe(0);

    $component
        ->callMountedAction()
        ->assertSee('Reviewed');

    expect(ManagedEnvironmentTriageReview::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('concern_family', PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH)
        ->where('current_state', ManagedEnvironmentTriageReview::STATE_REVIEWED)
        ->whereNull('resolved_at')
        ->exists())->toBeTrue()
        ->and(AuditLog::query()
            ->where('workspace_id', (int) $tenant->workspace_id)
            ->where('managed_environment_id', (int) $tenant->getKey())
            ->where('action', AuditActionId::ManagedEnvironmentTriageReviewMarkedReviewed->value)
            ->exists())->toBeTrue();
});

it('renders changed-since-review when the current concern fingerprint no longer matches the stored review', function (): void {
    [$user, $tenant] = $this->makePortfolioTriageActor('Dashboard Changed ManagedEnvironment');
    $this->seedPortfolioBackupConcern($tenant, TenantBackupHealthAssessment::POSTURE_STALE);
    $this->seedPortfolioTriageReview(
        $tenant,
        PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
        ManagedEnvironmentTriageReview::STATE_REVIEWED,
        $user,
        changedFingerprint: true,
    );

    tenantDashboardArrivalWidget($user, $tenant, [
        'sourceSurface' => PortfolioArrivalContextToken::SOURCE_TENANT_REGISTRY,
        'tenantRouteKey' => (string) $tenant->external_id,
        'workspaceId' => (int) $tenant->workspace_id,
        'concernFamily' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
        'concernState' => TenantBackupHealthAssessment::POSTURE_STALE,
        'concernReason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_STALE,
        'returnFilters' => $this->portfolioReturnFilters(
            [TenantBackupHealthAssessment::POSTURE_STALE],
        ),
    ])
        ->assertSee('Changed since review')
        ->assertSee($user->name);
});
