<?php

declare(strict_types=1);

use App\Filament\Pages\Findings\MyFindingsInbox;
use App\Filament\Resources\FindingResource;
use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Livewire\Livewire;

function myWorkInboxActingUser(string $role = 'owner', string $workspaceRole = 'readonly'): array
{
    $tenant = ManagedEnvironment::factory()->create(['status' => 'active']);
    [$user, $tenant] = createUserWithTenant($tenant, role: $role, workspaceRole: $workspaceRole);

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

    return [$user, $tenant];
}

function myWorkInboxPage(?User $user = null, array $query = [])
{
    if ($user instanceof User) {
        test()->actingAs($user);
    }

    setAdminPanelContext();

    $factory = $query === [] ? Livewire::actingAs(auth()->user()) : Livewire::withQueryParams($query)->actingAs(auth()->user());

    return $factory->test(MyFindingsInbox::class);
}

function makeAssignedFindingForInbox(ManagedEnvironment $tenant, User $assignee, array $attributes = []): Finding
{
    return Finding::factory()->for($tenant)->create(array_merge([
        'workspace_id' => (int) $tenant->workspace_id,
        'owner_user_id' => (int) $assignee->getKey(),
        'assignee_user_id' => (int) $assignee->getKey(),
        'status' => Finding::STATUS_TRIAGED,
        'subject_external_id' => fake()->uuid(),
    ], $attributes));
}

it('shows only visible assigned open findings and exposes the fixed filter contract', function (): void {
    [$user, $tenantA] = myWorkInboxActingUser();
    $tenantA->forceFill(['name' => 'Alpha ManagedEnvironment'])->save();

    $tenantB = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'ManagedEnvironment Bravo',
    ]);
    createUserWithTenant($tenantB, $user, role: 'readonly', workspaceRole: 'readonly');

    $hiddenTenant = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'Hidden ManagedEnvironment',
    ]);

    $otherAssignee = User::factory()->create();
    createUserWithTenant($tenantA, $otherAssignee, role: 'operator', workspaceRole: 'readonly');

    $otherOwner = User::factory()->create();
    createUserWithTenant($tenantB, $otherOwner, role: 'owner', workspaceRole: 'readonly');

    $assignedVisible = makeAssignedFindingForInbox($tenantA, $user, [
        'subject_external_id' => 'visible-a',
        'severity' => Finding::SEVERITY_HIGH,
        'status' => Finding::STATUS_NEW,
    ]);

    $assignedOwnerSplit = makeAssignedFindingForInbox($tenantB, $user, [
        'subject_external_id' => 'visible-b',
        'owner_user_id' => (int) $otherOwner->getKey(),
        'status' => Finding::STATUS_IN_PROGRESS,
        'due_at' => now()->subDay(),
    ]);

    $ownerOnly = makeAssignedFindingForInbox($tenantA, $otherAssignee, [
        'subject_external_id' => 'owner-only',
        'owner_user_id' => (int) $user->getKey(),
    ]);

    $assignedTerminal = makeAssignedFindingForInbox($tenantA, $user, [
        'subject_external_id' => 'terminal',
        'status' => Finding::STATUS_RESOLVED,
        'resolved_at' => now(),
    ]);

    $assignedToOther = makeAssignedFindingForInbox($tenantA, $otherAssignee, [
        'subject_external_id' => 'other-assignee',
    ]);

    $hiddenAssigned = makeAssignedFindingForInbox($hiddenTenant, $user, [
        'subject_external_id' => 'hidden',
    ]);

    $component = myWorkInboxPage($user)
        ->assertCanSeeTableRecords([$assignedVisible, $assignedOwnerSplit])
        ->assertCanNotSeeTableRecords([$ownerOnly, $assignedTerminal, $assignedToOther, $hiddenAssigned])
        ->assertSee('Owner: '.$otherOwner->name)
        ->assertSee('Assigned to me only');

    expect($component->instance()->summaryCounts())->toBe([
        'open_assigned' => 2,
        'overdue_assigned' => 1,
    ]);

    expect($component->instance()->availableFilters())->toBe([
        [
            'key' => 'assignee_scope',
            'label' => 'Assigned to me',
            'fixed' => true,
            'options' => [],
        ],
        [
            'key' => 'tenant',
            'label' => 'Managed environment',
            'fixed' => false,
            'options' => [
                ['value' => (string) $tenantA->getKey(), 'label' => 'Alpha ManagedEnvironment'],
                ['value' => (string) $tenantB->getKey(), 'label' => $tenantB->name],
            ],
        ],
        [
            'key' => 'overdue',
            'label' => 'Overdue',
            'fixed' => false,
            'options' => [],
        ],
        [
            'key' => 'reopened',
            'label' => 'Reopened',
            'fixed' => false,
            'options' => [],
        ],
        [
            'key' => 'high_severity',
            'label' => 'High severity',
            'fixed' => false,
            'options' => [],
        ],
    ]);
});

it('defaults to the active environment prefilter and lets the operator clear it without dropping personal scope', function (): void {
    [$user, $tenantA] = myWorkInboxActingUser();

    $tenantB = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'Beta ManagedEnvironment',
    ]);
    createUserWithTenant($tenantB, $user, role: 'readonly', workspaceRole: 'readonly');

    $findingA = makeAssignedFindingForInbox($tenantA, $user, [
        'subject_external_id' => 'tenant-a',
        'status' => Finding::STATUS_NEW,
    ]);
    $findingB = makeAssignedFindingForInbox($tenantB, $user, [
        'subject_external_id' => 'tenant-b',
        'status' => Finding::STATUS_TRIAGED,
    ]);

    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenantA->workspace_id => (int) $tenantB->getKey(),
    ]);

    $component = myWorkInboxPage($user)
        ->assertSet('tableFilters.managed_environment_id.value', (string) $tenantB->getKey())
        ->assertCanSeeTableRecords([$findingB])
        ->assertCanNotSeeTableRecords([$findingA])
        ->assertActionVisible('clear_tenant_filter');

    expect($component->instance()->appliedScope())->toBe([
        'workspace_scoped' => true,
        'assignee_scope' => 'current_user_only',
        'tenant_prefilter_source' => 'active_tenant_context',
        'tenant_label' => $tenantB->name,
    ]);

    $component->callAction('clear_tenant_filter')
        ->assertCanSeeTableRecords([$findingA, $findingB]);

    expect($component->instance()->appliedScope())->toBe([
        'workspace_scoped' => true,
        'assignee_scope' => 'current_user_only',
        'tenant_prefilter_source' => 'none',
        'tenant_label' => null,
    ]);
});

it('orders overdue work before reopened work and keeps deterministic due-date and id tie breaks', function (): void {
    [$user, $tenant] = myWorkInboxActingUser();

    $overdue = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'overdue',
        'status' => Finding::STATUS_IN_PROGRESS,
        'due_at' => now()->subDay(),
    ]);

    $reopened = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'reopened',
        'status' => Finding::STATUS_REOPENED,
        'reopened_at' => now()->subHours(6),
        'due_at' => now()->addDay(),
    ]);

    $ordinarySooner = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'ordinary-sooner',
        'status' => Finding::STATUS_TRIAGED,
        'due_at' => now()->addDays(2),
    ]);

    $ordinaryLater = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'ordinary-later',
        'status' => Finding::STATUS_NEW,
        'due_at' => now()->addDays(4),
    ]);

    $ordinaryNoDue = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'ordinary-no-due',
        'status' => Finding::STATUS_TRIAGED,
        'due_at' => null,
    ]);

    myWorkInboxPage($user)
        ->assertCanSeeTableRecords([$overdue, $reopened, $ordinarySooner, $ordinaryLater, $ordinaryNoDue], inOrder: true)
        ->assertSee('Reopened');
});

it('applies reopened and high-severity filters without widening beyond assigned work', function (): void {
    [$user, $tenant] = myWorkInboxActingUser();

    $reopenedHigh = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'reopened-high',
        'status' => Finding::STATUS_REOPENED,
        'reopened_at' => now()->subHour(),
        'severity' => Finding::SEVERITY_CRITICAL,
    ]);

    $highOnly = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'high-only',
        'status' => Finding::STATUS_TRIAGED,
        'severity' => Finding::SEVERITY_HIGH,
    ]);

    $ordinary = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'ordinary',
        'status' => Finding::STATUS_NEW,
        'severity' => Finding::SEVERITY_MEDIUM,
    ]);

    myWorkInboxPage($user)
        ->set('tableFilters.reopened.isActive', true)
        ->assertCanSeeTableRecords([$reopenedHigh])
        ->assertCanNotSeeTableRecords([$highOnly, $ordinary])
        ->set('tableFilters.reopened.isActive', false)
        ->set('tableFilters.high_severity.isActive', true)
        ->assertCanSeeTableRecords([$reopenedHigh, $highOnly])
        ->assertCanNotSeeTableRecords([$ordinary]);
});

it('renders the environment-prefilter empty-state branch and offers only a clear-filter recovery action', function (): void {
    [$user, $tenantA] = myWorkInboxActingUser();

    $tenantB = ManagedEnvironment::factory()->create([
        'status' => 'active',
        'workspace_id' => (int) $tenantA->workspace_id,
        'name' => 'Work ManagedEnvironment',
    ]);
    createUserWithTenant($tenantB, $user, role: 'readonly', workspaceRole: 'readonly');

    makeAssignedFindingForInbox($tenantB, $user, [
        'subject_external_id' => 'available-elsewhere',
    ]);

    $component = myWorkInboxPage($user, [
        'tenant' => (string) $tenantA->external_id,
    ])
        ->assertCanNotSeeTableRecords([])
        ->assertSee('No assigned findings match this environment scope')
        ->assertTableEmptyStateActionsExistInOrder(['clear_tenant_filter_empty']);

    expect($component->instance()->summaryCounts())->toBe([
        'open_assigned' => 0,
        'overdue_assigned' => 0,
    ]);
});

it('renders the calm zero-work branch and points back to environment selection when no active environment context exists', function (): void {
    [$user, $tenant] = myWorkInboxActingUser();

    session()->forget(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY);
    Filament::setTenant(null, true);

    myWorkInboxPage($user)
        ->assertSee('No visible assigned findings right now')
        ->assertTableEmptyStateActionsExistInOrder(['choose_environment_empty']);
});

it('uses the active visible environment for the calm empty-state drillback when environment context exists', function (): void {
    [$user, $tenant] = myWorkInboxActingUser();

    session()->put(WorkspaceContext::LAST_ENVIRONMENT_IDS_SESSION_KEY, [
        (string) $tenant->workspace_id => (int) $tenant->getKey(),
    ]);

    $component = myWorkInboxPage($user)
        ->assertSee('No visible assigned findings right now')
        ->assertTableEmptyStateActionsExistInOrder(['open_tenant_findings_empty']);

    expect($component->instance()->emptyState())->toMatchArray([
        'action_name' => 'open_tenant_findings_empty',
        'action_label' => 'Open environment findings',
        'action_kind' => 'url',
        'action_url' => FindingResource::getUrl('index', panel: 'admin', tenant: $tenant),
    ]);
});

it('builds environment detail drilldowns with inbox continuity', function (): void {
    [$user, $tenant] = myWorkInboxActingUser();

    $finding = makeAssignedFindingForInbox($tenant, $user, [
        'subject_external_id' => 'continuity',
    ]);

    $component = myWorkInboxPage($user);
    $detailUrl = $component->instance()->getTable()->getRecordUrl($finding);

    expect($detailUrl)->toContain(FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant))
        ->and($detailUrl)->toContain('nav%5Bback_label%5D=Back+to+my+findings');

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
        ->get($detailUrl)
        ->assertOk()
        ->assertSee('Back to my findings');
});
