<?php

declare(strict_types=1);

use App\Models\AlertDelivery;
use App\Models\Finding;
use App\Models\FindingException;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentTriageReview;
use App\Models\User;
use App\Models\Workspace;
use App\Support\Auth\Capabilities;
use App\Support\BackupHealth\TenantBackupHealthResolver;
use App\Support\GovernanceInbox\GovernanceInboxSectionBuilder;
use App\Support\Navigation\CanonicalNavigationContext;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use App\Support\OperationRunType;
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewFingerprint;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

it('builds visible governance inbox sections in canonical order with source links', function (): void {
    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    $alphaTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'name' => 'Alpha ManagedEnvironment',
        'external_id' => 'alpha-tenant',
    ]);
    $bravoTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'name' => 'Bravo ManagedEnvironment',
        'external_id' => 'bravo-tenant',
    ]);

    Finding::factory()
        ->for($alphaTenant)
        ->assignedTo((int) $user->getKey())
        ->ownedBy((int) $user->getKey())
        ->overdueByHours()
        ->create([
            'status' => Finding::STATUS_IN_PROGRESS,
            'subject_external_id' => 'assigned-finding',
        ]);

    Finding::factory()
        ->for($bravoTenant)
        ->reopened()
        ->create([
            'subject_external_id' => 'intake-finding',
        ]);

    $exceptionFinding = Finding::factory()
        ->for($alphaTenant)
        ->riskAccepted()
        ->create([
            'workspace_id' => (int) $workspace->getKey(),
            'subject_external_id' => 'exception-finding',
        ]);

    FindingException::query()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'managed_environment_id' => (int) $alphaTenant->getKey(),
        'finding_id' => (int) $exceptionFinding->getKey(),
        'requested_by_user_id' => (int) $user->getKey(),
        'owner_user_id' => (int) $user->getKey(),
        'status' => FindingException::STATUS_PENDING,
        'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
        'request_reason' => 'Needs approval',
        'requested_at' => now()->subDay(),
        'review_due_at' => now()->addDay(),
        'evidence_summary' => ['reference_count' => 0],
    ]);

    OperationRun::factory()
        ->forTenant($alphaTenant)
        ->create([
            'status' => OperationRunStatus::Completed->value,
            'outcome' => OperationRunOutcome::Failed->value,
            'completed_at' => now()->subMinute(),
        ]);

    OperationRun::factory()
        ->forTenant($bravoTenant)
        ->create([
            'type' => OperationRunType::InventorySync->value,
            'status' => OperationRunStatus::Queued->value,
            'outcome' => OperationRunOutcome::Pending->value,
            'created_at' => now()->subMinutes(6),
        ]);

    AlertDelivery::factory()->create([
        'managed_environment_id' => null,
        'workspace_id' => (int) $workspace->getKey(),
        'status' => AlertDelivery::STATUS_FAILED,
        'event_type' => 'alerts.failed_delivery',
        'payload' => [
            'title' => 'Delivery failed',
            'body' => 'Alert delivery could not be completed.',
        ],
    ]);

    $backupHealthResolver = app(TenantBackupHealthResolver::class);
    $fingerprints = app(ManagedEnvironmentTriageReviewFingerprint::class);

    $alphaBackupFingerprint = $fingerprints->forBackupHealth($backupHealthResolver->assess($alphaTenant));
    $bravoBackupFingerprint = $fingerprints->forBackupHealth($backupHealthResolver->assess($bravoTenant));

    expect($alphaBackupFingerprint)->not->toBeNull()
        ->and($bravoBackupFingerprint)->not->toBeNull();

    ManagedEnvironmentTriageReview::factory()
        ->for($alphaTenant)
        ->followUpNeeded()
        ->create([
            'workspace_id' => (int) $workspace->getKey(),
            'reviewed_by_user_id' => (int) $user->getKey(),
            'concern_family' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
            'review_fingerprint' => $alphaBackupFingerprint['fingerprint'],
            'review_snapshot' => $alphaBackupFingerprint['snapshot'],
            'reviewed_at' => now()->subDay(),
        ]);

    ManagedEnvironmentTriageReview::factory()
        ->for($bravoTenant)
        ->reviewed()
        ->create([
            'workspace_id' => (int) $workspace->getKey(),
            'reviewed_by_user_id' => (int) $user->getKey(),
            'concern_family' => PortfolioArrivalContextToken::FAMILY_BACKUP_HEALTH,
            'review_fingerprint' => hash('sha256', 'stale-review-fingerprint'),
            'review_snapshot' => $bravoBackupFingerprint['snapshot'],
            'reviewed_at' => now()->subDays(2),
        ]);

    $context = new CanonicalNavigationContext(
        sourceSurface: 'governance.inbox',
        canonicalRouteName: 'filament.admin.pages.governance.inbox',
        backLinkLabel: 'Back to governance inbox',
        backLinkUrl: '/admin/governance/inbox',
    );

    $payload = app(GovernanceInboxSectionBuilder::class)->build(
        user: $user,
        workspace: $workspace,
        authorizedTenants: [$alphaTenant, $bravoTenant],
        visibleFindingTenants: [$alphaTenant, $bravoTenant],
        reviewTenants: [$alphaTenant, $bravoTenant],
        canViewAlerts: true,
        canViewFindingExceptions: true,
        navigationContext: $context,
    );

    expect(collect($payload['sections'])->pluck('key')->all())
        ->toBe([
            'assigned_findings',
            'intake_findings',
            'finding_exceptions',
            'stale_operations',
            'alert_delivery_failures',
            'review_follow_up',
        ])
        ->and($payload['family_counts'])->toMatchArray([
            'assigned_findings' => 1,
            'intake_findings' => 1,
            'finding_exceptions' => 1,
            'stale_operations' => 2,
            'alert_delivery_failures' => 1,
            'review_follow_up' => 2,
        ]);

    $sections = collect($payload['sections'])->keyBy('key');

    expect($sections['assigned_findings']['dominant_action_url'])
        ->toContain('/admin/findings/my-work')
        ->toContain('nav%5Bback_label%5D=Back+to+governance+inbox')
        ->and($sections['finding_exceptions']['dominant_action_label'])->toBe('Open finding exceptions')
        ->and($sections['finding_exceptions']['dominant_action_url'])->toContain('/admin/finding-exceptions/queue')
        ->and($sections['finding_exceptions']['entries'][0]['destination_url'])->toContain('exception='.(string) $sections['finding_exceptions']['entries'][0]['source_key'])
        ->and($sections['stale_operations']['dominant_action_label'])->toBe('Open terminal follow-up')
        ->and($sections['stale_operations']['dominant_action_url'])->toContain('problemClass=terminal_follow_up')
        ->and($sections['alert_delivery_failures']['dominant_action_url'])->toContain('tableFilters%5Bstatus%5D%5Bvalue%5D=failed')
        ->and(collect($sections['review_follow_up']['entries'])->pluck('status_label')->all())
            ->toBe(['Follow-up needed', 'Changed since review']);
});

it('keeps an explicitly selected visible family with an honest empty state when tenant filtering removes every row', function (): void {
    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    $alphaTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'name' => 'Alpha ManagedEnvironment',
        'external_id' => 'alpha-tenant',
    ]);
    $bravoTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'name' => 'Bravo ManagedEnvironment',
        'external_id' => 'bravo-tenant',
    ]);

    AlertDelivery::factory()->create([
        'managed_environment_id' => null,
        'workspace_id' => (int) $workspace->getKey(),
        'status' => AlertDelivery::STATUS_FAILED,
    ]);

    $payload = app(GovernanceInboxSectionBuilder::class)->build(
        user: $user,
        workspace: $workspace,
        authorizedTenants: [$alphaTenant, $bravoTenant],
        visibleFindingTenants: [],
        reviewTenants: [],
        canViewAlerts: true,
        selectedTenant: $alphaTenant,
        selectedFamily: 'alert_delivery_failures',
    );

    expect($payload['sections'])->toHaveCount(1)
        ->and($payload['sections'][0]['key'])->toBe('alert_delivery_failures')
        ->and($payload['sections'][0]['count'])->toBe(0)
        ->and($payload['sections'][0]['empty_state'])->toContain('environment filter');
});

it('omits finding exceptions when the exception family is hidden or tenant scope is inaccessible', function (): void {
    $workspace = Workspace::factory()->create();
    $user = User::factory()->create();

    $visibleTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'name' => 'Visible ManagedEnvironment',
    ]);
    $hiddenTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'name' => 'Hidden ManagedEnvironment',
    ]);

    $finding = Finding::factory()
        ->for($hiddenTenant)
        ->riskAccepted()
        ->create(['workspace_id' => (int) $workspace->getKey()]);

    FindingException::query()->create([
        'workspace_id' => (int) $workspace->getKey(),
        'managed_environment_id' => (int) $hiddenTenant->getKey(),
        'finding_id' => (int) $finding->getKey(),
        'requested_by_user_id' => (int) $user->getKey(),
        'owner_user_id' => (int) $user->getKey(),
        'status' => FindingException::STATUS_PENDING,
        'current_validity_state' => FindingException::VALIDITY_MISSING_SUPPORT,
        'request_reason' => 'Hidden request',
        'requested_at' => now()->subDay(),
        'review_due_at' => now()->addDay(),
        'evidence_summary' => ['reference_count' => 0],
    ]);

    $builder = app(GovernanceInboxSectionBuilder::class);

    $payloadWithoutCapability = $builder->build(
        user: $user,
        workspace: $workspace,
        authorizedTenants: [$visibleTenant, $hiddenTenant],
        visibleFindingTenants: [],
        reviewTenants: [],
        canViewAlerts: false,
        canViewFindingExceptions: false,
    );

    $payloadWithHiddenTenantOnly = $builder->build(
        user: $user,
        workspace: $workspace,
        authorizedTenants: [$visibleTenant],
        visibleFindingTenants: [],
        reviewTenants: [],
        canViewAlerts: false,
        canViewFindingExceptions: true,
    );

    expect(collect($payloadWithoutCapability['available_families'])->pluck('key')->all())
        ->not->toContain('finding_exceptions')
        ->and($payloadWithHiddenTenantOnly['family_counts']['finding_exceptions'] ?? null)->toBe(0)
        ->and($payloadWithHiddenTenantOnly['sections'])->toBe([]);
});
