<?php

declare(strict_types=1);

use App\Filament\Resources\FindingResource;
use App\Filament\Widgets\Dashboard\RecentDriftFindings;
use App\Models\Finding;
use App\Models\InventoryItem;
use App\Support\Baselines\BaselineSubjectKey;
use Filament\Facades\Filament;
use Livewire\Livewire;

it('renders shared summary badges and RBAC row states on the finding detail page', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $finding = findingViewRbacFinding($tenant);

    $this->actingAs($user)
        ->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
        ->assertOk()
        ->assertSee('Intune RBAC Role Definition drift')
        ->assertSee('Metadata-only change')
        ->assertSee('2 changed')
        ->assertSee('1 added')
        ->assertSee('1 removed')
        ->assertSee('4 unchanged')
        ->assertSee('Changed')
        ->assertSee('Added')
        ->assertSee('Removed')
        ->assertSee('Unchanged')
        ->assertSee('Changed value')
        ->assertSee('No material change')
        ->assertSee('Role definition > Description')
        ->assertSee('Permission block 1 > Denied actions')
        ->assertSee('Permission block 1 > Conditions')
        ->assertSee('Baseline description')
        ->assertSee('Updated description')
        ->assertSee('border-warning-200')
        ->assertSee('text-gray-500')
        ->assertSee('Role Assignments are not included')
        ->assertSee('RBAC restore is not supported');
});

it('renders Allowed Actions as inline added removed and unchanged list chips', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $finding = findingViewRbacFinding($tenant);

    $this->actingAs($user)
        ->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
        ->assertOk()
        ->assertSee('Permission block 1 > Allowed actions')
        ->assertSee('Added items')
        ->assertSee('Removed items')
        ->assertSee('Unchanged items')
        ->assertSee('Microsoft.Intune/deviceConfigurations/create')
        ->assertSee('Microsoft.Intune/deviceConfigurations/delete')
        ->assertSee('Microsoft.Intune/deviceConfigurations/read')
        ->assertSee('@Resource[Microsoft.Intune/deviceConfigurations] Exists')
        ->assertSee('Microsoft.Intune/deviceConfigurations/wipe');
});

it('renders a no-change RBAC summary when the evidence only contains unchanged rows', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $finding = findingViewRbacFinding($tenant, [
        'changed_keys' => [],
        'metadata_keys' => [],
        'permission_keys' => [],
        'baseline' => [
            'normalized' => [
                'Role definition > Display name' => 'Security Reader',
                'Role definition > Description' => 'Baseline description',
            ],
            'is_built_in' => false,
            'role_permission_count' => 1,
        ],
        'current' => [
            'normalized' => [
                'Role definition > Display name' => 'Security Reader',
                'Role definition > Description' => 'Baseline description',
            ],
            'is_built_in' => false,
            'role_permission_count' => 1,
        ],
    ]);

    $this->actingAs($user)
        ->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
        ->assertOk()
        ->assertSee('0 changed')
        ->assertSee('0 added')
        ->assertSee('0 removed')
        ->assertSee('4 unchanged')
        ->assertSee('No changes detected.');
});

it('renders a stable sparse fallback when the RBAC evidence payload is empty', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $finding = findingViewRbacFinding($tenant, []);
    $evidence = is_array($finding->evidence_jsonb) ? $finding->evidence_jsonb : [];
    $evidence['rbac_role_definition'] = [];

    $finding->forceFill([
        'evidence_jsonb' => $evidence,
    ])->save();

    $this->actingAs($user)
        ->get(FindingResource::getUrl('view', ['record' => $finding], tenant: $tenant))
        ->assertOk()
        ->assertSee('0 changed')
        ->assertSee('0 added')
        ->assertSee('0 removed')
        ->assertSee('0 unchanged')
        ->assertSee('No diff data available.')
        ->assertSee('RBAC restore is not supported');
});

it('shows RBAC labels and display-name fallback in the recent drift findings widget', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $this->actingAs($user);

    $tenant->makeCurrent();
    setAdminPanelContext($tenant);

    $rawSubjectExternalId = 'rbac-role-1';
    $subjectExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalIdForPolicy(
        'intuneRoleDefinition',
        'Security Reader',
        $rawSubjectExternalId,
    );

    InventoryItem::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'external_id' => $rawSubjectExternalId,
        'policy_type' => 'intuneRoleDefinition',
        'display_name' => 'Security Reader',
        'meta_jsonb' => ['etag' => 'E1'],
        'last_seen_at' => now(),
    ]);

    $finding = Finding::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'source' => 'baseline.compare',
        'subject_type' => 'policy',
        'subject_external_id' => (string) $subjectExternalId,
        'severity' => Finding::SEVERITY_HIGH,
        'status' => Finding::STATUS_NEW,
        'evidence_jsonb' => [
            'change_type' => 'missing_policy',
            'policy_type' => 'intuneRoleDefinition',
            'subject_key' => hash('sha256', 'intuneRoleDefinition|rbac-role-1'),
            'display_name' => 'Security Reader',
            'summary' => [
                'kind' => 'rbac_role_definition',
            ],
            'baseline' => ['policy_version_id' => 10],
            'current' => ['policy_version_id' => null],
            'rbac_role_definition' => [
                'diff_kind' => 'missing',
            ],
            'fidelity' => 'mixed',
            'provenance' => [
                'baseline_profile_id' => 1,
                'baseline_snapshot_id' => 1,
                'compare_operation_run_id' => 1,
                'inventory_sync_run_id' => null,
            ],
        ],
    ]);

    Livewire::actingAs($user)->test(RecentDriftFindings::class)
        ->assertCanSeeTableRecords([$finding])
        ->assertSee('Security Reader')
        ->assertSee('Intune RBAC Role Definition drift');
});

/**
 * @param  array<string, mixed>  $rbacOverrides
 */
function findingViewRbacFinding(\App\Models\ManagedEnvironment $tenant, array $rbacOverrides = []): Finding
{
    $subjectExternalId = BaselineSubjectKey::workspaceSafeSubjectExternalIdForPolicy(
        'intuneRoleDefinition',
        'Security Reader',
        'rbac-role-1',
    );

    return Finding::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'source' => 'baseline.compare',
        'subject_type' => 'policy',
        'subject_external_id' => (string) $subjectExternalId,
        'evidence_fidelity' => 'content',
        'severity' => Finding::SEVERITY_LOW,
        'evidence_jsonb' => [
            'change_type' => 'different_version',
            'policy_type' => 'intuneRoleDefinition',
            'subject_key' => hash('sha256', 'intuneRoleDefinition|rbac-role-1'),
            'display_name' => 'Security Reader',
            'summary' => [
                'kind' => 'rbac_role_definition',
            ],
            'baseline' => [
                'policy_version_id' => 10,
                'hash' => 'baseline',
            ],
            'current' => [
                'policy_version_id' => 11,
                'hash' => 'current',
            ],
            'rbac_role_definition' => findingViewRbacEvidenceFixture($rbacOverrides),
            'fidelity' => 'content',
            'provenance' => [
                'baseline_profile_id' => 1,
                'baseline_snapshot_id' => 1,
                'compare_operation_run_id' => 1,
                'inventory_sync_run_id' => 1,
            ],
        ],
    ]);
}

/**
 * @param  array<string, mixed>  $overrides
 * @return array<string, mixed>
 */
function findingViewRbacEvidenceFixture(array $overrides = []): array
{
    return findingViewRbacFixtureMerge([
        'diff_kind' => 'metadata_only',
        'diff_fingerprint' => 'rbac-diff-1',
        'changed_keys' => [
            'Role definition > Description',
            'Permission block 1 > Allowed actions',
        ],
        'metadata_keys' => [
            'Role definition > Description',
        ],
        'permission_keys' => [
            'Permission block 1 > Allowed actions',
        ],
        'baseline' => [
            'normalized' => [
                'Role definition > Display name' => 'Security Reader',
                'Role definition > Description' => 'Baseline description',
                'Role definition > Scope tag IDs' => ['0', 'scope-1'],
                'Permission block 1 > Allowed actions' => [
                    'Microsoft.Intune/deviceConfigurations/delete',
                    'Microsoft.Intune/deviceConfigurations/read',
                ],
                'Permission block 1 > Denied actions' => [
                    'Microsoft.Intune/deviceConfigurations/wipe',
                ],
            ],
            'is_built_in' => false,
            'role_permission_count' => 1,
        ],
        'current' => [
            'normalized' => [
                'Role definition > Display name' => 'Security Reader',
                'Role definition > Description' => 'Updated description',
                'Role definition > Scope tag IDs' => ['0', 'scope-1'],
                'Permission block 1 > Allowed actions' => [
                    'Microsoft.Intune/deviceConfigurations/create',
                    'Microsoft.Intune/deviceConfigurations/read',
                ],
                'Permission block 1 > Conditions' => [
                    '@Resource[Microsoft.Intune/deviceConfigurations] Exists',
                ],
            ],
            'is_built_in' => false,
            'role_permission_count' => 1,
        ],
    ], $overrides);
}

/**
 * @param  array<string, mixed>  $base
 * @param  array<string, mixed>  $overrides
 * @return array<string, mixed>
 */
function findingViewRbacFixtureMerge(array $base, array $overrides): array
{
    foreach ($overrides as $key => $value) {
        if ($key === 'normalized') {
            $base[$key] = $value;

            continue;
        }

        if (
            is_string($key)
            && array_key_exists($key, $base)
            && is_array($value)
            && is_array($base[$key])
            && ! array_is_list($value)
            && ! array_is_list($base[$key])
        ) {
            $base[$key] = findingViewRbacFixtureMerge($base[$key], $value);

            continue;
        }

        $base[$key] = $value;
    }

    return $base;
}
