<?php

declare(strict_types=1);

use App\Filament\Pages\EnvironmentDashboard;
use App\Filament\Pages\Governance\GovernanceInbox;
use App\Filament\Resources\FindingResource;
use App\Models\Finding;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Services\Auth\CapabilityResolver;
use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder;
use App\Support\Auth\Capabilities;
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
use App\Support\Links\RequiredPermissionsLinks;
use Filament\Actions\Action;
use Filament\Actions\ActionGroup;
use Illuminate\Support\Facades\Gate;
use Livewire\Features\SupportTesting\Testable;
use Livewire\Livewire;

use function Pest\Laravel\mock;

/**
 * @return array<string, array{tag:string, href:?string, interactive:bool, class:string}>
 */
function tenantDashboardGovernanceStatusRows(string $content): array
{
    $dom = new \DOMDocument;

    libxml_use_internal_errors(true);
    $dom->loadHTML($content);
    libxml_clear_errors();

    $xpath = new \DOMXPath($dom);
    $nodes = $xpath->query('//*[@data-testid="tenant-dashboard-governance-status"]');

    if ($nodes === false) {
        return [];
    }

    $rows = [];

    foreach ($nodes as $node) {
        $attributes = $node->attributes;
        $statusKey = (string) $attributes?->getNamedItem('data-status-key')?->nodeValue;

        if ($statusKey === '') {
            continue;
        }

        $rows[$statusKey] = [
            'tag' => strtolower($node->nodeName),
            'href' => $attributes?->getNamedItem('href')?->nodeValue,
            'interactive' => (($attributes?->getNamedItem('data-governance-interactive')?->nodeValue) === 'true'),
            'class' => (string) ($attributes?->getNamedItem('class')?->nodeValue ?? ''),
        ];
    }

    return $rows;
}

function mockEnvironmentDashboardAuthorizationPermissions(array $overview = []): void
{
    mock(ManagedEnvironmentRequiredPermissionsViewModelBuilder::class, function ($mock) use ($overview): void {
        $mock->shouldReceive('build')->andReturn([
            'overview' => array_replace_recursive([
                'overall' => 'ready',
                'counts' => [
                    'missing_application' => 0,
                    'missing_delegated' => 0,
                ],
                'freshness' => [
                    'is_stale' => false,
                    'last_refreshed_at' => now()->toIso8601String(),
                ],
            ], $overview),
        ]);
    });
}

function tenantDashboardProductizationHeaderActions(Testable $component): array
{
    $instance = $component->instance();

    if ($instance->getCachedHeaderActions() === []) {
        $instance->cacheInteractsWithHeaderActions();
    }

    return $instance->getCachedHeaderActions();
}

function tenantDashboardProductizationHeaderPrimaryNames(Testable $component): array
{
    return collect(tenantDashboardProductizationHeaderActions($component))
        ->reject(static fn ($action): bool => $action instanceof ActionGroup)
        ->map(static fn ($action): ?string => $action instanceof Action ? $action->getName() : null)
        ->filter()
        ->values()
        ->all();
}

function tenantDashboardProductizationHeaderMoreActionNames(Testable $component): array
{
    $moreGroup = collect(tenantDashboardProductizationHeaderActions($component))
        ->first(static fn ($action): bool => $action instanceof ActionGroup && in_array($action->getLabel(), ['More', 'Mehr'], true));

    return collect($moreGroup?->getActions() ?? [])
        ->map(static fn ($action): ?string => $action instanceof Action ? $action->getName() : null)
        ->filter()
        ->values()
        ->all();
}

it('keeps the tenant dashboard deny-as-not-found for non-members as a productization baseline', function (): void {
    [, $tenant] = createUserWithTenant(role: 'owner');
    $outsider = User::factory()->create();

    $this->actingAs($outsider)
        ->get(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $tenant))
        ->assertNotFound();
});

it('allows an entitled operator to open the current tenant dashboard baseline', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $this->actingAs($user);
    setAdminEnvironmentContext($tenant);

    $this->get(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $tenant))
        ->assertSuccessful();
});

it('keeps the productized tenant dashboard header decision-first while grouping support utilities under more', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    Finding::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'severity' => Finding::SEVERITY_HIGH,
        'status' => Finding::STATUS_NEW,
    ]);

    setAdminEnvironmentContext($tenant);

    $component = Livewire::actingAs($user)
        ->test(EnvironmentDashboard::class)
        ->assertActionVisible('primaryFollowUp')
        ->assertActionVisible('requestSupport')
        ->assertActionVisible('openSupportDiagnostics');

    $headerActions = tenantDashboardProductizationHeaderActions($component);
    $primaryAction = collect($headerActions)
        ->first(static fn ($action): bool => $action instanceof Action && $action->getName() === 'primaryFollowUp');
    $moreGroup = collect($headerActions)
        ->first(static fn ($action): bool => $action instanceof ActionGroup && in_array($action->getLabel(), ['More', 'Mehr'], true));

    expect(tenantDashboardProductizationHeaderPrimaryNames($component))
        ->toBe(['primaryFollowUp'])
        ->and(tenantDashboardProductizationHeaderMoreActionNames($component))
        ->toContain('requestSupport', 'openSupportDiagnostics')
        ->and(count($headerActions))->toBe(2)
        ->and($primaryAction)->toBeInstanceOf(Action::class)
        ->and($primaryAction?->getColor())->toBe('primary')
        ->and($moreGroup)->toBeInstanceOf(ActionGroup::class)
        ->and($moreGroup?->getColor())->toBe('gray')
        ->and(collect($moreGroup?->getActions() ?? [])->every(static fn ($action): bool => $action instanceof Action && $action->getColor() === 'gray'))->toBeTrue();
});

it('falls back to the governance inbox header action with tenant continuity when no summary action is available', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    mockEnvironmentDashboardAuthorizationPermissions();

    [$profile, $snapshot] = seedActiveBaselineForTenant($tenant);

    seedBaselineCompareRun($tenant, $profile, $snapshot, workspaceOverviewCompareCoverage());

    $backupSet = workspaceOverviewSeedHealthyBackup($tenant);

    workspaceOverviewSeedRestoreHistory($tenant, $backupSet);

    setAdminEnvironmentContext($tenant);

    $component = Livewire::actingAs($user)
        ->test(EnvironmentDashboard::class)
        ->assertActionVisible('primaryFollowUp');

    $primaryAction = collect(tenantDashboardProductizationHeaderActions($component))
        ->first(static fn ($action): bool => $action instanceof Action && $action->getName() === 'primaryFollowUp');

    expect($primaryAction)
        ->toBeInstanceOf(Action::class)
        ->and($primaryAction->getLabel())->toBe('Open governance inbox')
        ->and($primaryAction->getUrl())->toBe(GovernanceInbox::getUrl(panel: 'admin').'?'.http_build_query([
            'environment_id' => (int) $tenant->getKey(),
        ]));
});

it('derives the primary header CTA from the top recommended action instead of hard-coding operations copy', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    mockEnvironmentDashboardAuthorizationPermissions([
        'overall' => 'blocked',
        'counts' => [
            'missing_application' => 1,
            'missing_delegated' => 0,
        ],
    ]);

    setAdminEnvironmentContext($tenant);

    $summary = app(EnvironmentDashboardSummaryBuilder::class)
        ->build($tenant, $user)
        ->toArray();

    $component = Livewire::actingAs($user)
        ->test(EnvironmentDashboard::class)
        ->assertActionVisible('primaryFollowUp');

    $primaryAction = collect(tenantDashboardProductizationHeaderActions($component))
        ->first(static fn ($action): bool => $action instanceof Action && $action->getName() === 'primaryFollowUp');

    expect($summary['recommendedActions'][0]['actionLabel'] ?? null)->toBe('Review permissions')
        ->and($summary['recommendedActions'][0]['actionUrl'] ?? null)->toBe(RequiredPermissionsLinks::requiredPermissions($tenant))
        ->and($primaryAction)->toBeInstanceOf(Action::class)
        ->and($primaryAction->getLabel())->toBe('Review permissions')
        ->and($primaryAction->getUrl())->toBe(RequiredPermissionsLinks::requiredPermissions($tenant));
});

it('renders governance status rows as interactive only when a repo-real follow-up url is available', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    mockEnvironmentDashboardAuthorizationPermissions();

    [$profile, $baselineSnapshot] = seedActiveBaselineForTenant($tenant);
    seedBaselineCompareRun($tenant, $profile, $baselineSnapshot, workspaceOverviewCompareCoverage());

    $backupSet = workspaceOverviewSeedHealthyBackup($tenant);
    workspaceOverviewSeedRestoreHistory($tenant, $backupSet);

    $evidenceSnapshot = seedEnvironmentReviewEvidence($tenant);
    $environmentReview = composeEnvironmentReviewForTest($tenant, $user, $evidenceSnapshot);

    Gate::define(Capabilities::TENANT_VIEW, fn (): bool => false);
    Gate::define(Capabilities::EVIDENCE_VIEW, fn (): bool => false);
    Gate::define(Capabilities::ENVIRONMENT_REVIEW_VIEW, fn (): bool => false);

    $summary = app(EnvironmentDashboardSummaryBuilder::class)
        ->build($tenant, $user)
        ->toArray();

    expect($summary['governanceStatus'])->toHaveCount(5)
        ->and(collect($summary['governanceStatus'])->firstWhere('key', 'baseline_compare')['actionUrl'] ?? null)->toBeNull()
        ->and(collect($summary['governanceStatus'])->firstWhere('key', 'evidence_coverage')['actionUrl'] ?? null)->toBeNull()
        ->and(collect($summary['governanceStatus'])->firstWhere('key', 'review_freshness')['actionUrl'] ?? null)->toBeNull()
        ->and(collect($summary['governanceStatus'])->firstWhere('key', 'provider_permissions')['actionUrl'] ?? null)->not->toBeNull()
        ->and(collect($summary['governanceStatus'])->firstWhere('key', 'backup_posture')['actionUrl'] ?? null)->toBeNull();

    $this->actingAs($user);
    setAdminEnvironmentContext($tenant);

    $content = $this->get(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $tenant))
        ->assertSuccessful()
        ->getContent();

    $rows = tenantDashboardGovernanceStatusRows($content);

    expect($rows)->toHaveCount(5)
        ->and($rows['baseline_compare']['tag'])->toBe('div')
        ->and($rows['baseline_compare']['interactive'])->toBeFalse()
        ->and($rows['baseline_compare']['class'])->not->toContain('hover:bg-gray-50')
        ->and($rows['baseline_compare']['class'])->not->toContain('hover:shadow-sm')
        ->and($rows['evidence_coverage']['tag'])->toBe('div')
        ->and($rows['evidence_coverage']['interactive'])->toBeFalse()
        ->and($rows['review_freshness']['tag'])->toBe('div')
        ->and($rows['review_freshness']['interactive'])->toBeFalse()
        ->and($rows['provider_permissions']['tag'])->toBe('a')
        ->and($rows['provider_permissions']['interactive'])->toBeTrue()
        ->and($rows['provider_permissions']['href'])->toBe(collect($summary['governanceStatus'])->firstWhere('key', 'provider_permissions')['actionUrl'])
        ->and($rows['provider_permissions']['class'])->toContain('hover:shadow-md')
        ->and($rows['backup_posture']['tag'])->toBe('div')
        ->and($rows['backup_posture']['interactive'])->toBeFalse();
});

it('uses the existing governance status action urls as clickable row targets when the actor is entitled', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    mockEnvironmentDashboardAuthorizationPermissions();

    [$profile, $baselineSnapshot] = seedActiveBaselineForTenant($tenant);
    seedBaselineCompareRun($tenant, $profile, $baselineSnapshot, workspaceOverviewCompareCoverage());

    $backupSet = workspaceOverviewSeedHealthyBackup($tenant);
    workspaceOverviewSeedRestoreHistory($tenant, $backupSet);

    $evidenceSnapshot = seedEnvironmentReviewEvidence($tenant);
    $environmentReview = composeEnvironmentReviewForTest($tenant, $user, $evidenceSnapshot);

    $summary = app(EnvironmentDashboardSummaryBuilder::class)
        ->build($tenant, $user)
        ->toArray();

    $governanceStatus = collect($summary['governanceStatus'])
        ->keyBy('key');

    expect($governanceStatus['baseline_compare']['actionUrl'] ?? null)->not->toBeNull()
        ->and($governanceStatus['evidence_coverage']['actionUrl'] ?? null)->not->toBeNull()
        ->and($governanceStatus['review_freshness']['actionUrl'] ?? null)->not->toBeNull()
        ->and($governanceStatus['provider_permissions']['actionUrl'] ?? null)->not->toBeNull();

    $this->actingAs($user);
    setAdminEnvironmentContext($tenant);

    $content = $this->get(EnvironmentDashboard::getUrl(panel: 'admin', tenant: $tenant))
        ->assertSuccessful()
        ->getContent();

    $rows = tenantDashboardGovernanceStatusRows($content);

    expect($rows['baseline_compare']['tag'])->toBe('a')
        ->and($rows['baseline_compare']['href'])->toBe($governanceStatus['baseline_compare']['actionUrl'])
        ->and($rows['evidence_coverage']['tag'])->toBe('a')
        ->and($rows['evidence_coverage']['href'])->toBe($governanceStatus['evidence_coverage']['actionUrl'])
        ->and($rows['review_freshness']['tag'])->toBe('a')
        ->and($rows['review_freshness']['href'])->toBe($governanceStatus['review_freshness']['actionUrl'])
        ->and($rows['provider_permissions']['tag'])->toBe('a')
        ->and($rows['provider_permissions']['href'])->toBe($governanceStatus['provider_permissions']['actionUrl']);
});

it('keeps blocked findings follow-up disabled on the dashboard and forbidden at the destination', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $this->actingAs($user);

    Gate::define(Capabilities::TENANT_FINDINGS_VIEW, fn (): bool => false);

    $this->mock(CapabilityResolver::class, function ($mock) use ($tenant): void {
        $mock->shouldReceive('primeMemberships')->andReturnNull();
        $mock->shouldReceive('isMember')
            ->andReturnUsing(static fn ($actor, ManagedEnvironment $resolvedTenant): bool => (int) $resolvedTenant->getKey() === (int) $tenant->getKey());

        $mock->shouldReceive('can')
            ->andReturnUsing(static function ($actor, ManagedEnvironment $resolvedTenant, string $capability) use ($tenant): bool {
                expect((int) $resolvedTenant->getKey())->toBe((int) $tenant->getKey());

                return match ($capability) {
                    Capabilities::TENANT_FINDINGS_VIEW => false,
                    default => true,
                };
            });
    });

    [$profile, $snapshot] = seedActiveBaselineForTenant($tenant);

    seedBaselineCompareRun($tenant, $profile, $snapshot, workspaceOverviewCompareCoverage());

    $backupSet = workspaceOverviewSeedHealthyBackup($tenant);

    workspaceOverviewSeedRestoreHistory($tenant, $backupSet);

    Finding::factory()->create([
        'managed_environment_id' => (int) $tenant->getKey(),
        'workspace_id' => (int) $tenant->workspace_id,
        'finding_type' => Finding::FINDING_TYPE_DRIFT,
        'severity' => Finding::SEVERITY_HIGH,
        'status' => Finding::STATUS_NEW,
    ]);

    $summary = app(EnvironmentDashboardSummaryBuilder::class)
        ->build($tenant, $user)
        ->toArray();

    $findingsAction = collect($summary['recommendedActions'])->firstWhere('key', 'high_severity_findings');

    expect($findingsAction)
        ->not->toBeNull()
        ->and($findingsAction['actionDisabled'])->toBeTrue()
        ->and($findingsAction['actionUrl'])->toBeNull()
        ->and($findingsAction['helperText'])->toContain('opening findings requires additional permissions');

    $this->get(FindingResource::getUrl('index', [
        'tab' => 'needs_action',
        'high_severity' => 1,
    ], panel: 'admin', tenant: $tenant))->assertForbidden();
});
