<?php

declare(strict_types=1);

namespace App\Filament\Pages;

use App\Filament\Clusters\Inventory\InventoryCluster;
use App\Filament\Concerns\ResolvesPanelTenantContext;
use App\Filament\Resources\InventoryItemResource;
use App\Filament\Widgets\Inventory\InventoryKpiHeader;
use App\Models\OperationRun;
use App\Models\ManagedEnvironment;
use App\Models\User;
use App\Models\Workspace;
use App\Services\Auth\CapabilityResolver;
use App\Support\Auth\Capabilities;
use App\Support\Badges\BadgeCatalog;
use App\Support\Badges\BadgeDomain;
use App\Support\Badges\BadgeRenderer;
use App\Support\Badges\TagBadgeCatalog;
use App\Support\Badges\TagBadgeDomain;
use App\Support\Inventory\TenantCoverageTruth;
use App\Support\Inventory\TenantCoverageTruthResolver;
use App\Support\Navigation\NavigationScope;
use App\Support\OperationRunLinks;
use App\Support\OperationRunType;
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
use App\Support\Ui\ActionSurface\ActionSurfaceDefaults;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceSlot;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Facades\Filament;
use Filament\Pages\Page;
use Filament\Support\Enums\FontFamily;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use UnitEnum;

class InventoryCoverage extends Page implements HasTable
{
    use InteractsWithTable;
    use ResolvesPanelTenantContext;

    protected static string|BackedEnum|null $navigationIcon = 'heroicon-o-table-cells';

    protected static ?int $navigationSort = 3;

    protected static string|UnitEnum|null $navigationGroup = 'Inventory';

    protected static ?string $navigationLabel = 'Coverage';

    protected static ?string $cluster = InventoryCluster::class;

    protected string $view = 'filament.pages.inventory-coverage';

    protected ?TenantCoverageTruth $cachedCoverageTruth = null;

    public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
    {
        return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly)
            ->withDefaults(new ActionSurfaceDefaults(
                moreGroupLabel: 'More',
                exportIsDefaultBulkActionForReadOnly: false,
            ))
            ->exempt(ActionSurfaceSlot::ListHeader, 'Inventory coverage stays read-only and uses KPI widgets instead of header actions.')
            ->exempt(ActionSurfaceSlot::InspectAffordance, 'Inventory coverage rows are runtime-derived metadata and intentionally omit inspect affordances.')
            ->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'Derived coverage rows do not expose row actions.')
            ->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'Derived coverage rows do not expose bulk actions.')
            ->satisfy(ActionSurfaceSlot::ListEmptyState, 'The empty state provides a clear-filters CTA to return to the full tenant coverage report.');
    }

    public static function shouldRegisterNavigation(): bool
    {
        return NavigationScope::shouldRegisterEnvironmentNavigation()
            && parent::shouldRegisterNavigation();
    }

    /**
     * @param  array<mixed>  $parameters
     */
    public static function getUrl(array $parameters = [], bool $isAbsolute = true, ?string $panel = null, ?Model $tenant = null): string
    {
        $panelId = $panel ?? Filament::getCurrentOrDefaultPanel()?->getId() ?? 'admin';

        if ($panelId !== 'admin') {
            return parent::getUrl($parameters, $isAbsolute, $panelId, $tenant);
        }

        $resolvedTenant = static::resolveAdminUrlTenant($parameters, $tenant);

        if (! $resolvedTenant instanceof ManagedEnvironment) {
            return url('/admin');
        }

        $workspace = static::resolveAdminUrlWorkspace($resolvedTenant, $parameters);

        if (! $workspace instanceof Workspace && ! is_string($workspace) && ! is_int($workspace)) {
            return url('/admin');
        }

        $parameters['environment'] ??= $resolvedTenant;
        $parameters['workspace'] ??= $workspace;
        unset($parameters['tenant']);

        return parent::getUrl($parameters, $isAbsolute, $panelId, null);
    }

    public static function canAccess(): bool
    {
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();

        if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User) {
            return false;
        }

        /** @var CapabilityResolver $resolver */
        $resolver = app(CapabilityResolver::class);

        return $resolver->isMember($user, $tenant)
            && $resolver->can($user, $tenant, Capabilities::TENANT_VIEW);
    }

    public function mount(): void
    {
        $this->mountInteractsWithTable();
    }

    protected function getHeaderWidgets(): array
    {
        return [
            InventoryKpiHeader::class,
        ];
    }

    public function table(Table $table): Table
    {
        return $table
            ->persistFiltersInSession()
            ->persistSearchInSession()
            ->persistSortInSession()
            ->searchable()
            ->searchPlaceholder('Search by type or label')
            ->defaultSort('follow_up_priority')
            ->defaultPaginationPageOption(50)
            ->paginated(\App\Support\Filament\TablePaginationProfiles::customPage())
            ->records(function (
                ?string $sortColumn,
                ?string $sortDirection,
                ?string $search,
                array $filters,
                int $page,
                int $recordsPerPage
            ): LengthAwarePaginator {
                $rows = $this->filterRows(
                    rows: $this->coverageRows(),
                    search: $search,
                    filters: $filters,
                );

                $rows = $this->sortRows(
                    rows: $rows,
                    sortColumn: $sortColumn,
                    sortDirection: $sortDirection,
                );

                return $this->paginateRows(
                    rows: $rows,
                    page: $page,
                    recordsPerPage: $recordsPerPage,
                );
            })
            ->columns([
                TextColumn::make('coverage_state')
                    ->label('Coverage state')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::InventoryCoverageState))
                    ->color(BadgeRenderer::color(BadgeDomain::InventoryCoverageState))
                    ->icon(BadgeRenderer::icon(BadgeDomain::InventoryCoverageState))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::InventoryCoverageState))
                    ->sortable(),
                TextColumn::make('label')
                    ->label('Type')
                    ->sortable()
                    ->badge()
                    ->formatStateUsing(function (?string $state, array $record): string {
                        return TagBadgeCatalog::spec(
                            TagBadgeDomain::PolicyType,
                            $record['type'] ?? $state,
                        )->label;
                    })
                    ->color(function (?string $state, array $record): string {
                        return TagBadgeCatalog::spec(
                            TagBadgeDomain::PolicyType,
                            $record['type'] ?? $state,
                        )->color;
                    })
                    ->icon(function (?string $state, array $record): ?string {
                        return TagBadgeCatalog::spec(
                            TagBadgeDomain::PolicyType,
                            $record['type'] ?? $state,
                        )->icon;
                    })
                    ->iconColor(function (?string $state, array $record): ?string {
                        $spec = TagBadgeCatalog::spec(
                            TagBadgeDomain::PolicyType,
                            $record['type'] ?? $state,
                        );

                        return $spec->iconColor ?? $spec->color;
                    })
                    ->wrap(),
                TextColumn::make('follow_up_guidance')
                    ->label('Follow-up guidance')
                    ->wrap()
                    ->toggleable(),
                TextColumn::make('observed_item_count')
                    ->label('Observed items')
                    ->numeric()
                    ->sortable(),
                TextColumn::make('category')
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => TagBadgeCatalog::spec(TagBadgeDomain::PolicyCategory, $state)->label)
                    ->color(fn (?string $state): string => TagBadgeCatalog::spec(TagBadgeDomain::PolicyCategory, $state)->color)
                    ->icon(fn (?string $state): ?string => TagBadgeCatalog::spec(TagBadgeDomain::PolicyCategory, $state)->icon)
                    ->iconColor(function (?string $state): ?string {
                        $spec = TagBadgeCatalog::spec(TagBadgeDomain::PolicyCategory, $state);

                        return $spec->iconColor ?? $spec->color;
                    })
                    ->toggleable()
                    ->wrap(),
                TextColumn::make('restore')
                    ->label('Restore')
                    ->badge()
                    ->formatStateUsing(function (?string $state): string {
                        return filled($state)
                            ? BadgeCatalog::spec(BadgeDomain::PolicyRestoreMode, $state)->label
                            : 'Not provided';
                    })
                    ->color(function (?string $state): string {
                        return filled($state)
                            ? BadgeCatalog::spec(BadgeDomain::PolicyRestoreMode, $state)->color
                            : 'gray';
                    })
                    ->icon(function (?string $state): ?string {
                        return filled($state)
                            ? BadgeCatalog::spec(BadgeDomain::PolicyRestoreMode, $state)->icon
                            : 'heroicon-m-minus-circle';
                    })
                    ->iconColor(function (?string $state): ?string {
                        if (! filled($state)) {
                            return 'gray';
                        }

                        $spec = BadgeCatalog::spec(BadgeDomain::PolicyRestoreMode, $state);

                        return $spec->iconColor ?? $spec->color;
                    })
                    ->toggleable(),
                IconColumn::make('dependencies')
                    ->label('Dependencies')
                    ->boolean()
                    ->trueIcon('heroicon-m-check-circle')
                    ->falseIcon('heroicon-m-minus-circle')
                    ->trueColor('success')
                    ->falseColor('gray')
                    ->alignCenter()
                    ->toggleable(),
                TextColumn::make('type')
                    ->label('Type key')
                    ->sortable()
                    ->fontFamily(FontFamily::Mono)
                    ->copyable()
                    ->wrap()
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('segment')
                    ->label('Segment')
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => $state === 'foundation' ? 'Foundation' : 'Policy')
                    ->color(fn (?string $state): string => $state === 'foundation' ? 'gray' : 'info')
                    ->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('risk')
                    ->label('Risk')
                    ->badge()
                    ->formatStateUsing(BadgeRenderer::label(BadgeDomain::PolicyRisk))
                    ->color(BadgeRenderer::color(BadgeDomain::PolicyRisk))
                    ->icon(BadgeRenderer::icon(BadgeDomain::PolicyRisk))
                    ->iconColor(BadgeRenderer::iconColor(BadgeDomain::PolicyRisk))
                    ->toggleable(isToggledHiddenByDefault: true),
            ])
            ->filters($this->tableFilters())
            ->emptyStateHeading('No coverage rows match this report')
            ->emptyStateDescription('Clear the current search or filters to return to the full tenant coverage report.')
            ->emptyStateIcon('heroicon-o-funnel')
            ->emptyStateActions([
                Action::make('clear_filters')
                    ->label('Clear filters')
                    ->icon('heroicon-o-x-mark')
                    ->color('gray')
                    ->action(function (): void {
                        $this->resetTable();
                    }),
            ])
            ->actions([])
            ->bulkActions([]);
    }

    /**
     * @return array<int, SelectFilter>
     */
    protected function tableFilters(): array
    {
        $filters = [
            SelectFilter::make('coverage_state')
                ->label('Coverage state')
                ->options([
                    'succeeded' => BadgeCatalog::spec(BadgeDomain::InventoryCoverageState, 'succeeded')->label,
                    'failed' => BadgeCatalog::spec(BadgeDomain::InventoryCoverageState, 'failed')->label,
                    'skipped' => BadgeCatalog::spec(BadgeDomain::InventoryCoverageState, 'skipped')->label,
                    'unknown' => BadgeCatalog::spec(BadgeDomain::InventoryCoverageState, 'unknown')->label,
                ]),
            SelectFilter::make('category')
                ->label('Category')
                ->options($this->categoryFilterOptions()),
        ];

        if ($this->restoreFilterOptions() !== []) {
            $filters[] = SelectFilter::make('restore')
                ->label('Restore mode')
                ->options($this->restoreFilterOptions());
        }

        return $filters;
    }

    /**
     * @return Collection<string, array{
     *     __key: string,
     *     key: string,
     *     type: string,
     *     segment: string,
     *     label: string,
     *     category: string,
     *     platform: ?string,
     *     coverage_state: string,
     *     follow_up_required: bool,
     *     follow_up_priority: int,
     *     follow_up_guidance: string,
     *     observed_item_count: int,
     *     basis_item_count: ?int,
     *     basis_error_code: ?string,
     *     restore: ?string,
     *     risk: ?string,
     *     dependencies: bool,
     *     is_basis_payload_backed: bool
     * }>
     */
    protected function coverageRows(): Collection
    {
        $truth = $this->coverageTruth();

        if (! $truth instanceof TenantCoverageTruth) {
            return collect();
        }

        return collect($truth->rows)
            ->mapWithKeys(static fn ($row): array => [
                $row->key => $row->toArray(),
            ]);
    }

    /**
     * @param  Collection<string, array<string, mixed>>  $rows
     * @param  array<string, mixed>  $filters
     * @return Collection<string, array<string, mixed>>
     */
    protected function filterRows(Collection $rows, ?string $search, array $filters): Collection
    {
        $normalizedSearch = Str::lower(trim((string) $search));
        $coverageState = $filters['coverage_state']['value'] ?? null;
        $category = $filters['category']['value'] ?? null;
        $restore = $filters['restore']['value'] ?? null;

        return $rows
            ->when(
                $normalizedSearch !== '',
                function (Collection $rows) use ($normalizedSearch): Collection {
                    return $rows->filter(function (array $row) use ($normalizedSearch): bool {
                        return str_contains(Str::lower((string) $row['type']), $normalizedSearch)
                            || str_contains(Str::lower((string) $row['label']), $normalizedSearch);
                    });
                },
            )
            ->when(
                filled($coverageState),
                fn (Collection $rows): Collection => $rows->where('coverage_state', (string) $coverageState),
            )
            ->when(
                filled($category),
                fn (Collection $rows): Collection => $rows->where('category', (string) $category),
            )
            ->when(
                filled($restore),
                fn (Collection $rows): Collection => $rows->where('restore', (string) $restore),
            );
    }

    /**
     * @param  Collection<string, array<string, mixed>>  $rows
     * @return Collection<string, array<string, mixed>>
     */
    protected function sortRows(Collection $rows, ?string $sortColumn, ?string $sortDirection): Collection
    {
        $sortColumn = in_array($sortColumn, ['type', 'label', 'observed_item_count', 'coverage_state', 'follow_up_priority'], true)
            ? $sortColumn
            : null;

        if ($sortColumn === null) {
            return $rows;
        }

        $records = $rows->all();

        uasort($records, function (array $left, array $right) use ($sortColumn, $sortDirection): int {
            $comparison = match ($sortColumn) {
                'observed_item_count' => ((int) ($left[$sortColumn] ?? 0)) <=> ((int) ($right[$sortColumn] ?? 0)),
                'follow_up_priority' => ((int) ($left[$sortColumn] ?? 0)) <=> ((int) ($right[$sortColumn] ?? 0)),
                default => strnatcasecmp(
                    (string) ($left[$sortColumn] ?? ''),
                    (string) ($right[$sortColumn] ?? ''),
                ),
            };

            if ($comparison === 0 && $sortColumn === 'follow_up_priority') {
                $comparison = ((int) ($right['observed_item_count'] ?? 0)) <=> ((int) ($left['observed_item_count'] ?? 0));
            }

            if ($comparison === 0) {
                $comparison = strnatcasecmp(
                    (string) ($left['label'] ?? ''),
                    (string) ($right['label'] ?? ''),
                );
            }

            return $sortDirection === 'desc' ? ($comparison * -1) : $comparison;
        });

        return collect($records);
    }

    /**
     * @param  Collection<string, array<string, mixed>>  $rows
     */
    protected function paginateRows(Collection $rows, int $page, int $recordsPerPage): LengthAwarePaginator
    {
        return new LengthAwarePaginator(
            items: $rows->forPage($page, $recordsPerPage),
            total: $rows->count(),
            perPage: $recordsPerPage,
            currentPage: $page,
        );
    }

    /**
     * @return array<string, string>
     */
    protected function categoryFilterOptions(): array
    {
        return $this->coverageRows()
            ->pluck('category')
            ->filter(fn (mixed $category): bool => is_string($category) && $category !== '')
            ->unique()
            ->sort()
            ->mapWithKeys(function (string $category): array {
                return [
                    $category => TagBadgeCatalog::spec(TagBadgeDomain::PolicyCategory, $category)->label,
                ];
            })
            ->all();
    }

    /**
     * @return array<string, string>
     */
    protected function restoreFilterOptions(): array
    {
        return $this->coverageRows()
            ->pluck('restore')
            ->filter(fn (mixed $restore): bool => is_string($restore) && $restore !== '')
            ->unique()
            ->sort()
            ->mapWithKeys(function (string $restore): array {
                return [
                    $restore => BadgeCatalog::spec(BadgeDomain::PolicyRestoreMode, $restore)->label,
                ];
            })
            ->all();
    }

    /**
     * @return array<string, mixed>
     */
    public function coverageSummary(): array
    {
        $truth = $this->coverageTruth();

        if (! $truth instanceof TenantCoverageTruth) {
            return [];
        }

        return [
            'supportedTypes' => $truth->supportedTypeCount,
            'succeededTypes' => $truth->succeededTypeCount,
            'followUpTypes' => $truth->followUpTypeCount,
            'observedItems' => $truth->observedItemTotal,
            'observedTypes' => $truth->observedTypeCount(),
            'topFollowUpLabel' => $truth->topPriorityFollowUpRow()?->label,
            'topFollowUpGuidance' => $truth->topPriorityFollowUpRow()?->followUpGuidance,
            'hasCurrentCoverageResult' => $truth->hasCurrentCoverageResult,
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function basisRunSummary(): array
    {
        $truth = $this->coverageTruth();
        $tenant = static::resolveTenantContextForCurrentPanel();
        $user = auth()->user();

        if (! $truth instanceof TenantCoverageTruth || ! $tenant instanceof ManagedEnvironment) {
            return [];
        }

        if (! $truth->basisRun instanceof OperationRun) {
            return [
                'title' => 'No current coverage basis',
                'body' => $user instanceof User && $user->can(Capabilities::TENANT_INVENTORY_SYNC_RUN, $tenant)
                    ? 'Run Inventory Sync from Inventory Items to establish current tenant coverage truth.'
                    : 'A tenant operator with inventory sync permission must establish current tenant coverage truth.',
                'badgeLabel' => null,
                'badgeColor' => null,
                'runUrl' => null,
                'historyUrl' => null,
                'inventoryItemsUrl' => InventoryItemResource::getUrl('index', tenant: $tenant),
            ];
        }

        $badge = BadgeRenderer::spec(BadgeDomain::OperationRunOutcome, (string) $truth->basisRun->outcome);
        $canViewRun = $user instanceof User && $user->can('view', $truth->basisRun);

        return [
            'title' => sprintf('Latest coverage-bearing sync completed %s.', $truth->basisCompletedAtLabel() ?? 'recently'),
            'body' => $canViewRun
                ? 'Review the cited inventory sync to inspect provider or permission issues in detail.'
                : 'The coverage basis is current, but your role cannot open the cited run detail.',
            'badgeLabel' => $badge->label,
            'badgeColor' => $badge->color,
            'runUrl' => $canViewRun ? OperationRunLinks::view($truth->basisRun, $tenant) : null,
            'historyUrl' => $canViewRun ? $this->inventorySyncHistoryUrl($tenant) : null,
            'inventoryItemsUrl' => InventoryItemResource::getUrl('index', tenant: $tenant),
        ];
    }

    protected function coverageTruth(): ?TenantCoverageTruth
    {
        if ($this->cachedCoverageTruth instanceof TenantCoverageTruth) {
            return $this->cachedCoverageTruth;
        }

        $tenant = static::resolveTenantContextForCurrentPanel();

        if (! $tenant instanceof ManagedEnvironment) {
            return null;
        }

        $this->cachedCoverageTruth = app(TenantCoverageTruthResolver::class)->resolve($tenant);

        return $this->cachedCoverageTruth;
    }

    private function inventorySyncHistoryUrl(ManagedEnvironment $tenant): string
    {
        return OperationRunLinks::index($tenant, operationType: OperationRunType::InventorySync->value);
    }

    /**
     * @param  array<mixed>  $parameters
     */
    private static function resolveAdminUrlTenant(array $parameters, ?Model $tenant = null): ?ManagedEnvironment
    {
        $parameterTenant = $parameters['tenant'] ?? $parameters['environment'] ?? null;

        if ($parameterTenant instanceof ManagedEnvironment) {
            return $parameterTenant;
        }

        if ($tenant instanceof ManagedEnvironment) {
            return $tenant;
        }

        return static::resolveTenantContextForCurrentPanel();
    }

    /**
     * @param  array<mixed>  $parameters
     */
    private static function resolveAdminUrlWorkspace(ManagedEnvironment $tenant, array $parameters): Workspace|string|int|null
    {
        $workspace = $parameters['workspace'] ?? null;

        if ($workspace instanceof Workspace || is_string($workspace) || is_int($workspace)) {
            return $workspace;
        }

        $tenantWorkspace = $tenant->workspace;

        if ($tenantWorkspace instanceof Workspace) {
            return $tenantWorkspace;
        }

        return $tenant->workspace()->first();
    }
}
