<?php

declare(strict_types=1);

use App\Filament\Pages\Findings\FindingsHygieneReport;
use App\Models\AuditLog;
use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\ManagedEnvironmentMembership;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Support\Auth\Capabilities;
use App\Support\Audit\AuditActionId;
use App\Support\Workspaces\WorkspaceContext;
use App\Support\Workspaces\WorkspaceOverviewBuilder;
use Carbon\CarbonImmutable;

use function Pest\Laravel\mock;

afterEach(function (): void {
    CarbonImmutable::setTestNow();
});

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

    return [$user, $tenant, $tenant->workspace()->firstOrFail()];
}

function makeFindingsHygieneOverviewFinding(ManagedEnvironment $tenant, array $attributes = []): Finding
{
    $subjectDisplayName = $attributes['subject_display_name'] ?? null;
    unset($attributes['subject_display_name']);

    if (is_string($subjectDisplayName) && $subjectDisplayName !== '') {
        $attributes['evidence_jsonb'] = array_merge(
            is_array($attributes['evidence_jsonb'] ?? null) ? $attributes['evidence_jsonb'] : [],
            ['display_name' => $subjectDisplayName],
        );
    }

    return Finding::factory()->for($tenant)->create(array_merge([
        'workspace_id' => (int) $tenant->workspace_id,
        'subject_external_id' => fake()->uuid(),
        'status' => Finding::STATUS_TRIAGED,
    ], $attributes));
}

function recordFindingsHygieneOverviewAudit(Finding $finding, string $action, CarbonImmutable $recordedAt): AuditLog
{
    return AuditLog::query()->create([
        'workspace_id' => (int) $finding->workspace_id,
        'managed_environment_id' => (int) $finding->managed_environment_id,
        'action' => $action,
        'status' => 'success',
        'resource_type' => 'finding',
        'resource_id' => (string) $finding->getKey(),
        'summary' => 'Test workflow activity',
        'recorded_at' => $recordedAt,
    ]);
}

it('adds a findings hygiene signal to the workspace overview and renders the report CTA', function (): void {
    CarbonImmutable::setTestNow(CarbonImmutable::create(2026, 4, 22, 9, 0, 0, 'UTC'));

    [$user, $tenant, $workspace] = findingsHygieneOverviewContext();

    $lostMember = User::factory()->create(['name' => 'Lost Member']);
    createUserWithTenant($tenant, $lostMember, role: 'readonly', workspaceRole: 'readonly');
    ManagedEnvironmentMembership::query()
        ->where('managed_environment_id', (int) $tenant->getKey())
        ->where('user_id', (int) $lostMember->getKey())
        ->delete();

    makeFindingsHygieneOverviewFinding($tenant, [
        'owner_user_id' => (int) $user->getKey(),
        'assignee_user_id' => (int) $lostMember->getKey(),
        'subject_display_name' => 'Broken Assignment',
    ]);

    $staleInProgress = makeFindingsHygieneOverviewFinding($tenant, [
        'owner_user_id' => (int) $user->getKey(),
        'assignee_user_id' => (int) $user->getKey(),
        'status' => Finding::STATUS_IN_PROGRESS,
        'in_progress_at' => now()->subDays(8),
        'subject_display_name' => 'Stale In Progress',
    ]);
    recordFindingsHygieneOverviewAudit($staleInProgress, AuditActionId::FindingInProgress->value, CarbonImmutable::now()->subDays(8));

    $signal = app(WorkspaceOverviewBuilder::class)->build($workspace, $user)['findings_hygiene_signal'];

    expect($signal)
        ->toBe([
            'headline' => '2 visible hygiene issues need follow-up',
            'description' => '1 broken assignment and 1 stale in-progress finding need repair.',
            'unique_issue_count' => 2,
            'broken_assignment_count' => 1,
            'stale_in_progress_count' => 1,
            'is_calm' => false,
            'cta_label' => 'Open hygiene report',
            'cta_url' => FindingsHygieneReport::getUrl(panel: 'admin'),
        ]);

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get(route('admin.workspace.home', ['workspace' => $workspace]))
        ->assertOk()
        ->assertSee('Findings hygiene')
        ->assertSee('Unique issues: 2')
        ->assertSee('Broken assignments: 1')
        ->assertSee('Stale in progress: 1')
        ->assertSee('Open hygiene report');
});

it('keeps the overview signal calm and suppresses hidden-tenant hygiene issues from counts and copy', function (): void {
    [$user, $visibleTenant, $workspace] = findingsHygieneOverviewContext();

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

    $lostMember = User::factory()->create(['name' => 'Hidden Lost Member']);
    createUserWithTenant($hiddenTenant, $lostMember, role: 'readonly', workspaceRole: 'readonly');
    ManagedEnvironmentMembership::query()
        ->where('managed_environment_id', (int) $hiddenTenant->getKey())
        ->where('user_id', (int) $lostMember->getKey())
        ->delete();

    makeFindingsHygieneOverviewFinding($hiddenTenant, [
        'owner_user_id' => (int) $user->getKey(),
        'assignee_user_id' => (int) $lostMember->getKey(),
        'subject_display_name' => 'Hidden Hygiene Issue',
    ]);

    mock(CapabilityResolver::class, function ($mock) use ($visibleTenant, $hiddenTenant): void {
        $mock->shouldReceive('primeMemberships')->atLeast()->once();
        $mock->shouldReceive('isMember')
            ->andReturnUsing(static function (User $user, ManagedEnvironment $tenant) use ($visibleTenant, $hiddenTenant): bool {
                expect([(int) $visibleTenant->getKey(), (int) $hiddenTenant->getKey()])
                    ->toContain((int) $tenant->getKey());

                return true;
            });
        $mock->shouldReceive('can')
            ->andReturnUsing(static function (User $user, ManagedEnvironment $tenant, string $capability) use ($visibleTenant, $hiddenTenant): bool {
                expect([(int) $visibleTenant->getKey(), (int) $hiddenTenant->getKey()])
                    ->toContain((int) $tenant->getKey());

                return $capability === Capabilities::TENANT_FINDINGS_VIEW
                    && (int) $tenant->getKey() === (int) $visibleTenant->getKey();
            });
    });

    $signal = app(WorkspaceOverviewBuilder::class)->build($workspace, $user)['findings_hygiene_signal'];

    expect($signal['unique_issue_count'])->toBe(0)
        ->and($signal['broken_assignment_count'])->toBe(0)
        ->and($signal['stale_in_progress_count'])->toBe(0)
        ->and($signal['is_calm'])->toBeTrue()
        ->and($signal['headline'])->toBe('Findings hygiene is calm')
        ->and($signal['description'])->toContain('No broken assignments or stale in-progress work are visible');

    $this->actingAs($user)
        ->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
        ->get(route('admin.workspace.home', ['workspace' => $workspace]))
        ->assertOk()
        ->assertSee('Findings hygiene is calm')
        ->assertSee('Unique issues: 0')
        ->assertSee('Calm')
        ->assertDontSee('Hidden Hygiene Issue');
});
