<?php

namespace App\Filament\Resources;

use App\Filament\Exports\AuditLogExporter;
use App\Filament\Resources\AuditLogResource\Pages\ListAuditLogs;
use App\Filament\Resources\AuditLogResource\Pages\ViewAuditLog;
use App\Filament\Resources\Concerns\HasModulePermissions;
use App\Filament\Resources\Concerns\HasStandardTableFeatures;
use App\Models\AuditLog;
use App\Models\User;
use App\Services\AuditLog as AuditLogService;
use Filament\Actions\Action;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Enums\FiltersLayout;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;

/**
 * Ported from App\Http\Controllers\AuditLogController
 * (docs/migration-plan.md Phase 11).
 *
 * Read-only by construction: the audit trail is written exclusively by
 * App\Services\AuditLog, never by hand, so there is no create/edit/delete
 * surface — and the source exposes none either.
 *
 * The one non-obvious rule, reproduced exactly: **full admins' own activity is
 * excluded from the viewer**. `filteredQuery()` applies
 * `whereNotIn('actor_id', $fullAdminIds)`, and the actor filter's options are
 * filtered the same way, so an owner reviewing the log sees their staff's
 * actions rather than a wall of their own.
 */
class AuditLogResource extends Resource
{
    use HasModulePermissions;
    use HasStandardTableFeatures;

    protected static ?string $model = AuditLog::class;

    protected static string $permissionModuleKey = 'audit_logs';

    protected static string $exporter = AuditLogExporter::class;

    protected static string|\BackedEnum|null $navigationIcon = Heroicon::OutlinedClipboardDocumentCheck;

    protected static string|\UnitEnum|null $navigationGroup = 'Administration';

    protected static ?string $navigationLabel = 'Audit Logs';

    protected static ?int $navigationSort = 20;

    public static function canCreate(): bool
    {
        return false;
    }

    public static function canEdit(Model $record): bool
    {
        return false;
    }

    public static function canDelete(Model $record): bool
    {
        return false;
    }

    public static function canDeleteAny(): bool
    {
        return false;
    }

    /**
     * Users holding an active full-admin role — whose activity the viewer omits.
     *
     * @return Collection<int, int>
     */
    public static function fullAdminIds(): Collection
    {
        return User::query()
            ->whereHas('roles', fn ($q) => $q->where('is_full_admin', true)->where('is_active', true))
            ->pluck('id');
    }

    public static function getEloquentQuery(): Builder
    {
        return parent::getEloquentQuery()
            ->with('actor:id,name,email')
            ->whereNotIn('actor_id', static::fullAdminIds());
    }

    public static function table(Table $table): Table
    {
        return static::standardTableDefaults($table)
            ->recordUrl(static::standardRecordUrl())
            ->emptyStateHeading('No audit events recorded')
            ->emptyStateDescription('Actions taken across the system will appear here.')
            ->columns([
                TextColumn::make('created_at')->label('When')->dateTime()->sortable(),
                TextColumn::make('actor.name')->label('Actor')->placeholder('System')->searchable(),
                TextColumn::make('actor.email')->label('Email')->placeholder('—')->toggleable(),
                TextColumn::make('action')->badge()->searchable()->sortable(),
                TextColumn::make('description')
                    ->label('Description')
                    // The stored description wins; otherwise it is derived, the
                    // same fallback the source applies on every row it renders.
                    ->state(fn (AuditLog $record): string => $record->description ?: AuditLogService::describe($record))
                    ->wrap(),
                TextColumn::make('ip')->label('IP')->placeholder('—')->toggleable(),
                TextColumn::make('subject_type')->label('Subject')->placeholder('—')->toggleable(isToggledHiddenByDefault: true),
            ])
            ->defaultSort('created_at', 'desc')
            // The source paginates at 50.
            ->defaultPaginationPageOption(50)
            ->filters([
                SelectFilter::make('action')
                    ->multiple()
                    ->options(fn (): array => AuditLog::query()
                        ->select('action')->distinct()->orderBy('action')
                        ->pluck('action', 'action')->all()),
                SelectFilter::make('actor_id')
                    ->label('Actor')
                    // Same exclusion as the listing itself.
                    ->options(fn (): array => User::query()
                        ->whereNotIn('id', static::fullAdminIds())
                        ->orderBy('name')
                        ->limit(100)
                        ->pluck('name', 'id')
                        ->all()),
                Filter::make('created_at')
                    ->schema([
                        DatePicker::make('from'),
                        DatePicker::make('to'),
                    ])
                    ->query(fn (Builder $query, array $data): Builder => $query
                        ->when($data['from'] ?? null, fn (Builder $q, $from) => $q->whereDate('created_at', '>=', $from))
                        ->when($data['to'] ?? null, fn (Builder $q, $to) => $q->whereDate('created_at', '<=', $to))),
                Filter::make('email')
                    ->schema([TextInput::make('value')->label('Email')])
                    ->query(fn (Builder $query, array $data): Builder => $query->when(
                        $data['value'] ?? null,
                        fn (Builder $q, $value) => $q->whereHas('actor', fn ($qq) => $qq->where('email', 'like', "%{$value}%")),
                    )),
                Filter::make('description')
                    ->schema([TextInput::make('value')->label('Description')])
                    ->query(fn (Builder $query, array $data): Builder => $query->when(
                        $data['value'] ?? null,
                        fn (Builder $q, $value) => $q->where('description', 'like', "%{$value}%"),
                    )),
                Filter::make('ip')
                    ->schema([TextInput::make('value')->label('IP')])
                    ->query(fn (Builder $query, array $data): Builder => $query->when(
                        $data['value'] ?? null,
                        fn (Builder $q, $value) => $q->where('ip', 'like', "%{$value}%"),
                    )),
                SelectFilter::make('subject_type')
                    ->label('Subject')
                    ->options(fn (): array => AuditLog::query()
                        ->whereNotNull('subject_type')
                        ->select('subject_type')->distinct()->orderBy('subject_type')
                        ->pluck('subject_type', 'subject_type')->all()),
            ])
            ->filtersLayout(FiltersLayout::AboveContentCollapsible)
            ->filtersFormColumns(2)
            ->headerActions(static::standardExportActions())
            ->toolbarActions(static::standardExportBulkActions())
            ->recordActions([
                Action::make('view')
                    ->url(fn (AuditLog $record): string => ViewAuditLog::getUrl(['record' => $record]))
                    ->visible(fn (AuditLog $record): bool => static::canView($record)),
            ]);
    }

    public static function getPages(): array
    {
        return [
            'index' => ListAuditLogs::route('/'),
            'view' => ViewAuditLog::route('/{record}'),
        ];
    }
}
