<?php

declare(strict_types=1);

use App\Filament\Pages\InventoryCoverage;
use App\Models\Tenant;
use App\Models\User;
use App\Models\WorkspaceMembership;
use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\TagBadgeCatalog;
use App\Support\Badges\TagBadgeDomain;
use Filament\Facades\Filament;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Livewire\Features\SupportTesting\Testable;
use Livewire\Livewire;

function inventoryCoverageRecordKey(string $segment, string $type): string
{
    return "{$segment}:{$type}";
}

function inventoryCoverageComponent(User $user, Tenant $tenant): Testable
{
    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    test()->actingAs($user);

    return Livewire::actingAs($user)->test(InventoryCoverage::class);
}

function removeInventoryCoverageRestoreMetadata(): void
{
    config()->set(
        'tenantpilot.supported_policy_types',
        collect(config('tenantpilot.supported_policy_types', []))
            ->map(function (array $row): array {
                unset($row['restore']);

                return $row;
            })
            ->all(),
    );

    config()->set(
        'tenantpilot.foundation_types',
        collect(config('tenantpilot.foundation_types', []))
            ->map(function (array $row): array {
                unset($row['restore']);

                return $row;
            })
            ->all(),
    );
}

it('renders searchable coverage rows for policy and foundation metadata', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $conditionalAccessKey = inventoryCoverageRecordKey('policy', 'conditionalAccessPolicy');
    $deviceConfigurationKey = inventoryCoverageRecordKey('policy', 'deviceConfiguration');
    $scopeTagKey = inventoryCoverageRecordKey('foundation', 'roleScopeTag');

    inventoryCoverageComponent($user, $tenant)
        ->assertOk()
        ->assertTableColumnExists('type')
        ->assertTableColumnExists('label')
        ->assertTableColumnExists('category')
        ->assertTableColumnExists('dependencies')
        ->assertCountTableRecords(
            count(config('tenantpilot.supported_policy_types', [])) + count(config('tenantpilot.foundation_types', [])),
        )
        ->assertCanSeeTableRecords([$conditionalAccessKey, $scopeTagKey])
        ->searchTable('conditional')
        ->assertCanSeeTableRecords([$conditionalAccessKey])
        ->assertCanNotSeeTableRecords([$deviceConfigurationKey, $scopeTagKey])
        ->searchTable('Scope Tag')
        ->assertCanSeeTableRecords([$scopeTagKey])
        ->assertCanNotSeeTableRecords([$conditionalAccessKey])
        ->searchTable(null)
        ->assertCanSeeTableRecords([$conditionalAccessKey, $deviceConfigurationKey, $scopeTagKey]);
});

it('sorts coverage rows by type and label deterministically', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $appProtectionKey = inventoryCoverageRecordKey('policy', 'appProtectionPolicy');
    $conditionalAccessKey = inventoryCoverageRecordKey('policy', 'conditionalAccessPolicy');
    $deviceComplianceKey = inventoryCoverageRecordKey('policy', 'deviceCompliancePolicy');
    $adminTemplatesKey = inventoryCoverageRecordKey('policy', 'groupPolicyConfiguration');
    $appConfigDeviceKey = inventoryCoverageRecordKey('policy', 'managedDeviceAppConfiguration');
    $appConfigMamKey = inventoryCoverageRecordKey('policy', 'mamAppConfiguration');

    inventoryCoverageComponent($user, $tenant)
        ->sortTable('type')
        ->assertCanSeeTableRecords([$appProtectionKey, $conditionalAccessKey, $deviceComplianceKey], inOrder: true)
        ->sortTable('label')
        ->assertCanSeeTableRecords([$adminTemplatesKey, $appConfigDeviceKey, $appConfigMamKey], inOrder: true);
});

it('filters coverage rows by category and restore mode when restore metadata exists', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $assignmentFilterKey = inventoryCoverageRecordKey('foundation', 'assignmentFilter');
    $scopeTagKey = inventoryCoverageRecordKey('foundation', 'roleScopeTag');
    $roleDefinitionKey = inventoryCoverageRecordKey('foundation', 'intuneRoleDefinition');
    $roleAssignmentKey = inventoryCoverageRecordKey('foundation', 'intuneRoleAssignment');
    $deviceConfigurationKey = inventoryCoverageRecordKey('policy', 'deviceConfiguration');
    $conditionalAccessKey = inventoryCoverageRecordKey('policy', 'conditionalAccessPolicy');
    $securityBaselineKey = inventoryCoverageRecordKey('policy', 'securityBaselinePolicy');

    inventoryCoverageComponent($user, $tenant)
        ->assertTableFilterExists('category')
        ->assertTableFilterExists('restore')
        ->filterTable('category', 'Foundations')
        ->assertCanSeeTableRecords([$assignmentFilterKey, $scopeTagKey])
        ->assertCanNotSeeTableRecords([$deviceConfigurationKey, $conditionalAccessKey, $roleDefinitionKey, $roleAssignmentKey])
        ->removeTableFilters()
        ->filterTable('category', 'RBAC')
        ->assertCanSeeTableRecords([$roleDefinitionKey, $roleAssignmentKey])
        ->assertCanNotSeeTableRecords([$assignmentFilterKey, $scopeTagKey, $deviceConfigurationKey])
        ->removeTableFilters()
        ->filterTable('restore', 'preview-only')
        ->assertCanSeeTableRecords([$conditionalAccessKey, $securityBaselineKey, $roleDefinitionKey, $roleAssignmentKey])
        ->assertCanNotSeeTableRecords([$deviceConfigurationKey, $assignmentFilterKey]);
});

it('omits the restore filter when the runtime dataset has no restore metadata', function (): void {
    removeInventoryCoverageRestoreMetadata();

    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $component = inventoryCoverageComponent($user, $tenant)
        ->assertTableFilterExists('category');

    expect($component->instance()->getTable()->getFilter('restore'))->toBeNull();
});

it('shows a single clear-filters empty state action and can reset back to a populated dataset', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $conditionalAccessKey = inventoryCoverageRecordKey('policy', 'conditionalAccessPolicy');

    inventoryCoverageComponent($user, $tenant)
        ->assertTableEmptyStateActionsExistInOrder(['clear_filters'])
        ->searchTable('no-such-coverage-entry')
        ->assertCountTableRecords(0)
        ->assertSee('No coverage entries match this view')
        ->assertSee('Clear filters')
        ->searchTable(null)
        ->assertCountTableRecords(
            count(config('tenantpilot.supported_policy_types', [])) + count(config('tenantpilot.foundation_types', [])),
        )
        ->assertCanSeeTableRecords([$conditionalAccessKey]);
});

it('preserves badge semantics and dependency indicators in the interactive table columns', function (): void {
    [$user, $tenant] = createUserWithTenant(role: 'owner');

    $conditionalAccessKey = inventoryCoverageRecordKey('policy', 'conditionalAccessPolicy');
    $deviceConfigurationKey = inventoryCoverageRecordKey('policy', 'deviceConfiguration');
    $assignmentFilterKey = inventoryCoverageRecordKey('foundation', 'assignmentFilter');

    $typeSpec = TagBadgeCatalog::spec(TagBadgeDomain::PolicyType, 'conditionalAccessPolicy');
    $categorySpec = TagBadgeCatalog::spec(TagBadgeDomain::PolicyCategory, 'Conditional Access');
    $restoreSpec = BadgeCatalog::spec(BadgeDomain::PolicyRestoreMode, 'preview-only');
    $riskSpec = BadgeCatalog::spec(BadgeDomain::PolicyRisk, 'high');

    inventoryCoverageComponent($user, $tenant)
        ->assertTableColumnFormattedStateSet('label', $typeSpec->label, $conditionalAccessKey)
        ->assertTableColumnFormattedStateSet('category', $categorySpec->label, $conditionalAccessKey)
        ->assertTableColumnFormattedStateSet('restore', $restoreSpec->label, $conditionalAccessKey)
        ->assertTableColumnFormattedStateSet('risk', $riskSpec->label, $conditionalAccessKey)
        ->assertTableColumnFormattedStateSet('segment', 'Policy', $conditionalAccessKey)
        ->assertTableColumnFormattedStateSet('segment', 'Foundation', $assignmentFilterKey)
        ->assertTableColumnStateSet('dependencies', true, $deviceConfigurationKey)
        ->assertTableColumnStateSet('dependencies', false, $assignmentFilterKey)
        ->assertTableColumnExists('label', function (TextColumn $column) use ($typeSpec): bool {
            $state = $column->getState();

            return $column->isBadge()
                && $column->getColor($state) === $typeSpec->color
                && $column->getIcon($state) === $typeSpec->icon;
        }, $conditionalAccessKey)
        ->assertTableColumnExists('category', function (TextColumn $column) use ($categorySpec): bool {
            $state = $column->getState();

            return $column->isBadge()
                && $column->getColor($state) === $categorySpec->color
                && $column->getIcon($state) === $categorySpec->icon;
        }, $conditionalAccessKey)
        ->assertTableColumnExists('restore', function (TextColumn $column) use ($restoreSpec): bool {
            $state = $column->getState();

            return $column->isBadge()
                && $column->getColor($state) === $restoreSpec->color
                && $column->getIcon($state) === $restoreSpec->icon;
        }, $conditionalAccessKey)
        ->assertTableColumnExists('risk', function (TextColumn $column) use ($riskSpec): bool {
            $state = $column->getState();

            return $column->isBadge()
                && $column->getColor($state) === $riskSpec->color
                && $column->getIcon($state) === $riskSpec->icon;
        }, $conditionalAccessKey)
        ->assertTableColumnExists('dependencies', function (IconColumn $column): bool {
            $state = $column->getState();

            return $state === true
                && $column->getColor($state) === 'success'
                && (string) $column->getIcon($state) === 'heroicon-m-check-circle';
        }, $deviceConfigurationKey)
        ->assertTableColumnExists('dependencies', function (IconColumn $column): bool {
            $state = $column->getState();

            return $state === false
                && $column->getColor($state) === 'gray'
                && (string) $column->getIcon($state) === 'heroicon-m-minus-circle';
        }, $assignmentFilterKey);
});

it('returns 404 for non-members on the inventory coverage page even when RBAC foundations exist', function (): void {
    [$owner, $tenant] = createUserWithTenant(role: 'owner');

    $this->actingAs($owner);
    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $outsider = User::factory()->create();
    WorkspaceMembership::factory()->create([
        'workspace_id' => (int) $tenant->workspace_id,
        'user_id' => (int) $outsider->getKey(),
        'role' => 'owner',
    ]);

    $this->actingAs($outsider);
    $tenant->makeCurrent();
    Filament::setTenant($tenant, true);

    $this->get(InventoryCoverage::getUrl(tenant: $tenant))
        ->assertNotFound();
});
