<?php

declare(strict_types=1);

use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\WorkspaceMembership;
use App\Support\Baselines\BaselineCompareMatrixBuilder;
use App\Support\OperationRunOutcome;
use App\Support\OperationRunStatus;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\Feature\Concerns\BuildsBaselineCompareMatrixFixtures;

uses(RefreshDatabase::class, BuildsBaselineCompareMatrixFixtures::class);

it('builds visible-set-only dense rows plus support metadata from assigned baseline truth', function (): void {
    $fixture = $this->makeBaselineCompareMatrixFixture();

    $visibleRun = $this->makeBaselineCompareMatrixRun(
        $fixture['visibleTenant'],
        $fixture['profile'],
        $fixture['snapshot'],
    );

    $visibleRunTwo = $this->makeBaselineCompareMatrixRun(
        $fixture['visibleTenantTwo'],
        $fixture['profile'],
        $fixture['snapshot'],
    );

    $hiddenRun = $this->makeBaselineCompareMatrixRun(
        $fixture['hiddenTenant'],
        $fixture['profile'],
        $fixture['snapshot'],
    );

    $this->makeBaselineCompareMatrixFinding(
        $fixture['visibleTenantTwo'],
        $fixture['profile'],
        $visibleRunTwo,
        'wifi-corp-profile',
    );

    $this->makeBaselineCompareMatrixFinding(
        $fixture['hiddenTenant'],
        $fixture['profile'],
        $hiddenRun,
        'wifi-corp-profile',
        ['severity' => 'critical'],
    );

    $matrix = app(BaselineCompareMatrixBuilder::class)->build($fixture['profile'], $fixture['user']);

    $wifiRow = collect($matrix['denseRows'])->first(
        static fn (array $row): bool => ($row['subject']['subjectKey'] ?? null) === 'wifi-corp-profile',
    );

    expect($matrix['reference']['assignedTenantCount'])->toBe(3)
        ->and($matrix['reference']['visibleTenantCount'])->toBe(2)
        ->and(collect($matrix['tenantSummaries'])->pluck('tenantName')->all())->toEqualCanonicalizing([
            (string) $fixture['visibleTenant']->name,
            (string) $fixture['visibleTenantTwo']->name,
        ])
        ->and($wifiRow)->not->toBeNull()
        ->and($wifiRow['subject']['deviationBreadth'])->toBe(1)
        ->and($wifiRow['subject']['attentionLevel'])->toBe('needs_attention')
        ->and(count($wifiRow['cells']))->toBe(2)
        ->and($matrix['denseRows'])->toHaveCount(2)
        ->and($matrix['compactResults'])->toBeEmpty()
        ->and($matrix['supportSurfaceState']['legendMode'])->toBe('grouped')
        ->and($matrix['supportSurfaceState']['showAutoRefreshHint'])->toBeFalse()
        ->and($matrix['lastUpdatedAt'])->not->toBeNull();
});

it('derives matrix cell precedence, freshness, attention, and reason summaries from compare truth', function (): void {
    $fixture = $this->makeBaselineCompareMatrixFixture();

    $matchTenant = $fixture['visibleTenant'];
    $differTenant = $fixture['visibleTenantTwo'];

    $missingTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'name' => 'Contoso Missing',
    ]);
    $ambiguousTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'name' => 'Contoso Ambiguous',
    ]);
    $notComparedTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'name' => 'Contoso Uncovered',
    ]);
    $staleTenant = ManagedEnvironment::factory()->create([
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'name' => 'Contoso Stale',
    ]);

    $fixture['user']->tenants()->syncWithoutDetaching([
        (int) $missingTenant->getKey() => ['role' => 'owner'],
        (int) $ambiguousTenant->getKey() => ['role' => 'owner'],
        (int) $notComparedTenant->getKey() => ['role' => 'owner'],
        (int) $staleTenant->getKey() => ['role' => 'owner'],
    ]);

    $this->assignTenantToBaselineProfile($fixture['profile'], $missingTenant);
    $this->assignTenantToBaselineProfile($fixture['profile'], $ambiguousTenant);
    $this->assignTenantToBaselineProfile($fixture['profile'], $notComparedTenant);
    $this->assignTenantToBaselineProfile($fixture['profile'], $staleTenant);

    $this->makeBaselineCompareMatrixRun($matchTenant, $fixture['profile'], $fixture['snapshot']);

    $differRun = $this->makeBaselineCompareMatrixRun($differTenant, $fixture['profile'], $fixture['snapshot']);
    $this->makeBaselineCompareMatrixFinding($differTenant, $fixture['profile'], $differRun, 'wifi-corp-profile', [
        'evidence_jsonb' => [
            'subject_key' => 'wifi-corp-profile',
            'change_type' => 'different_version',
        ],
    ]);

    $missingRun = $this->makeBaselineCompareMatrixRun($missingTenant, $fixture['profile'], $fixture['snapshot']);
    $this->makeBaselineCompareMatrixFinding($missingTenant, $fixture['profile'], $missingRun, 'wifi-corp-profile', [
        'evidence_jsonb' => [
            'subject_key' => 'wifi-corp-profile',
            'change_type' => 'missing_policy',
        ],
    ]);

    $ambiguousRun = $this->makeBaselineCompareMatrixRun(
        $ambiguousTenant,
        $fixture['profile'],
        $fixture['snapshot'],
        [
            'baseline_compare' => [
                'evidence_gaps' => [
                    'count' => 1,
                    'by_reason' => ['ambiguous_match' => 1],
                    'subjects' => [
                        $this->baselineCompareMatrixGap('deviceConfiguration', 'wifi-corp-profile', [
                            'reason_code' => 'ambiguous_match',
                            'resolution_outcome' => 'ambiguous_match',
                        ]),
                    ],
                ],
            ],
        ],
    );
    $this->makeBaselineCompareMatrixFinding($ambiguousTenant, $fixture['profile'], $ambiguousRun, 'wifi-corp-profile', [
        'evidence_jsonb' => [
            'subject_key' => 'wifi-corp-profile',
            'change_type' => 'missing_policy',
        ],
    ]);

    $this->makeBaselineCompareMatrixRun(
        $notComparedTenant,
        $fixture['profile'],
        $fixture['snapshot'],
        [
            'baseline_compare' => [
                'coverage' => [
                    'proof' => true,
                    'effective_types' => ['deviceConfiguration'],
                    'covered_types' => [],
                    'uncovered_types' => ['deviceConfiguration'],
                ],
            ],
        ],
    );

    $this->makeBaselineCompareMatrixRun(
        $staleTenant,
        $fixture['profile'],
        $fixture['snapshot'],
        [],
        [
            'completed_at' => $fixture['snapshot']->captured_at->copy()->subDay(),
            'context' => [
                'baseline_profile_id' => (int) $fixture['profile']->getKey(),
                'baseline_snapshot_id' => (int) $fixture['snapshot']->getKey() - 1,
                'baseline_compare' => [
                    'coverage' => [
                        'proof' => true,
                        'effective_types' => ['deviceConfiguration'],
                        'covered_types' => ['deviceConfiguration'],
                        'uncovered_types' => [],
                    ],
                    'evidence_gaps' => [
                        'count' => 0,
                        'by_reason' => [],
                        'subjects' => [],
                    ],
                ],
            ],
        ],
    );

    $matrix = app(BaselineCompareMatrixBuilder::class)->build($fixture['profile'], $fixture['user']);

    $wifiRow = collect($matrix['denseRows'])->first(
        static fn (array $row): bool => ($row['subject']['subjectKey'] ?? null) === 'wifi-corp-profile',
    );

    $cellsByTenant = collect($wifiRow['cells'] ?? [])
        ->mapWithKeys(static fn (array $cell): array => [(int) $cell['tenantId'] => $cell])
        ->all();

    expect($cellsByTenant[(int) $matchTenant->getKey()]['state'] ?? null)->toBe('match')
        ->and($cellsByTenant[(int) $matchTenant->getKey()]['attentionLevel'] ?? null)->toBe('aligned')
        ->and($cellsByTenant[(int) $differTenant->getKey()]['state'] ?? null)->toBe('differ')
        ->and($cellsByTenant[(int) $differTenant->getKey()]['attentionLevel'] ?? null)->toBe('needs_attention')
        ->and($cellsByTenant[(int) $differTenant->getKey()]['reasonSummary'] ?? null)->toBe('A baseline compare finding exists for this subject.')
        ->and($cellsByTenant[(int) $missingTenant->getKey()]['state'] ?? null)->toBe('missing')
        ->and($cellsByTenant[(int) $missingTenant->getKey()]['attentionLevel'] ?? null)->toBe('needs_attention')
        ->and($cellsByTenant[(int) $ambiguousTenant->getKey()]['state'] ?? null)->toBe('ambiguous')
        ->and($cellsByTenant[(int) $ambiguousTenant->getKey()]['reasonSummary'] ?? null)->toBe('Ambiguous Match')
        ->and($cellsByTenant[(int) $notComparedTenant->getKey()]['state'] ?? null)->toBe('not_compared')
        ->and($cellsByTenant[(int) $notComparedTenant->getKey()]['attentionLevel'] ?? null)->toBe('refresh_recommended')
        ->and($cellsByTenant[(int) $notComparedTenant->getKey()]['reasonSummary'] ?? null)->toContain('Governed subject coverage was not proven')
        ->and($cellsByTenant[(int) $staleTenant->getKey()]['state'] ?? null)->toBe('stale_result')
        ->and($cellsByTenant[(int) $staleTenant->getKey()]['freshnessState'] ?? null)->toBe('stale')
        ->and($cellsByTenant[(int) $staleTenant->getKey()]['trustLevel'] ?? null)->toBe('limited_confidence')
        ->and($cellsByTenant[(int) $staleTenant->getKey()]['attentionLevel'] ?? null)->toBe('refresh_recommended');
});

it('applies policy, state, severity, and subject-focus filters honestly without changing compare truth', function (): void {
    $fixture = $this->makeBaselineCompareMatrixFixture();

    $visibleRun = $this->makeBaselineCompareMatrixRun(
        $fixture['visibleTenant'],
        $fixture['profile'],
        $fixture['snapshot'],
    );

    $this->makeBaselineCompareMatrixRun(
        $fixture['visibleTenantTwo'],
        $fixture['profile'],
        $fixture['snapshot'],
        [],
        [
            'status' => OperationRunStatus::Completed->value,
            'outcome' => OperationRunOutcome::Failed->value,
        ],
    );

    $this->makeBaselineCompareMatrixFinding(
        $fixture['visibleTenant'],
        $fixture['profile'],
        $visibleRun,
        'wifi-corp-profile',
        ['severity' => 'critical'],
    );

    $deviceOnly = app(BaselineCompareMatrixBuilder::class)->build($fixture['profile'], $fixture['user'], [
        'policyTypes' => ['deviceConfiguration'],
    ]);

    $driftOnly = app(BaselineCompareMatrixBuilder::class)->build($fixture['profile'], $fixture['user'], [
        'states' => ['differ'],
        'severities' => ['critical'],
    ]);

    $subjectFocus = app(BaselineCompareMatrixBuilder::class)->build($fixture['profile'], $fixture['user'], [
        'focusedSubjectKey' => 'wifi-corp-profile',
    ]);

    expect(count($deviceOnly['denseRows']))->toBe(1)
        ->and($deviceOnly['denseRows'][0]['subject']['policyType'])->toBe('deviceConfiguration')
        ->and(count($driftOnly['denseRows']))->toBe(1)
        ->and($driftOnly['denseRows'][0]['subject']['subjectKey'])->toBe('wifi-corp-profile')
        ->and(count($subjectFocus['denseRows']))->toBe(1)
        ->and($subjectFocus['denseRows'][0]['subject']['subjectKey'])->toBe('wifi-corp-profile');
});

it('emits compact single-tenant results from the visible set only when one tenant remains in scope', function (): void {
    $fixture = $this->makeBaselineCompareMatrixFixture();

    $run = $this->makeBaselineCompareMatrixRun(
        $fixture['visibleTenant'],
        $fixture['profile'],
        $fixture['snapshot'],
    );

    $this->makeBaselineCompareMatrixFinding(
        $fixture['visibleTenant'],
        $fixture['profile'],
        $run,
        'wifi-corp-profile',
        ['severity' => 'critical'],
    );

    $this->makeBaselineCompareMatrixRun(
        $fixture['visibleTenantTwo'],
        $fixture['profile'],
        $fixture['snapshot'],
    );

    $viewer = User::factory()->create();

    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $fixture['workspace']->getKey(),
        'user_id' => (int) $viewer->getKey(),
        'role' => 'owner',
    ]);

    $viewer->tenants()->syncWithoutDetaching([
        (int) $fixture['visibleTenant']->getKey() => ['role' => 'owner'],
    ]);

    $matrix = app(BaselineCompareMatrixBuilder::class)->build($fixture['profile'], $viewer);

    expect($matrix['reference']['assignedTenantCount'])->toBe(3)
        ->and($matrix['reference']['visibleTenantCount'])->toBe(1)
        ->and($matrix['compactResults'])->toHaveCount(2)
        ->and(collect($matrix['compactResults'])->pluck('tenantId')->unique()->all())->toBe([(int) $fixture['visibleTenant']->getKey()])
        ->and(collect($matrix['compactResults'])->firstWhere('subjectKey', 'wifi-corp-profile')['state'] ?? null)->toBe('differ')
        ->and(collect($matrix['compactResults'])->firstWhere('subjectKey', 'wifi-corp-profile')['attentionLevel'] ?? null)->toBe('needs_attention');
});
