<?php

declare(strict_types=1);

namespace App\Filament\Resources\DberpAuditLogs;

use App\Business\Audit\Enums\DberpAuditLogCategory;
use App\Business\Report\QueryCenterService;
use App\Filament\Concerns\InteractsWithDberpPermissions;
use App\Filament\Resources\Concerns\HasDberpExportAction;
use App\Filament\Resources\DberpAuditLogs\Pages\ListDberpAuditLogs;
use App\Filament\Resources\DberpAuditLogs\Pages\ViewDberpAuditLog;
use App\Models\DberpAuditLog;
use App\Models\User;
use App\Services\DberpPermissionService;
use App\Support\AuditLogDisplay\DberpAuditLogBatchGroupPresenter;
use App\Support\AuditLogDisplay\DberpAuditLogCategoryQuery;
use App\Support\AuditLogDisplay\DberpAuditLogCategoryResolver;
use App\Support\AuditLogDisplay\DberpAuditLogDisplayFormatter;
use App\Support\DberpAuditDisplay;
use BackedEnum;
use Carbon\CarbonInterface;
use Filament\Actions\ViewAction;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\TextEntry;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\Filter;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Grouping\Group;
use Filament\Tables\Table;
use Illuminate\Contracts\Support\Htmlable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use UnitEnum;

class DberpAuditLogResource extends Resource
{
    use HasDberpExportAction;
    use InteractsWithDberpPermissions;

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

    protected static ?int $navigationSort = 80;

    public static function form(Schema $schema): Schema
    {
        return $schema->components([]);
    }

    public static function infolist(Schema $schema): Schema
    {
        return $schema->components([
            Section::make(__('dberp.audit_display.meta_heading'))
                ->schema([
                    TextEntry::make('created_at')
                        ->label(__('dberp.fields.created_at'))
                        ->formatStateUsing(fn (?CarbonInterface $state): ?string => static::formatAuditLogTimestamp($state)),
                    TextEntry::make('user.name')
                        ->label(__('dberp.fields.user'))
                        ->formatStateUsing(fn (?string $state): string => ($state !== null && $state !== '') ? $state : (string) __('dberp.audit.operator_system')),
                    TextEntry::make('category')
                        ->label(__('dberp.audit.filter_category'))
                        ->badge()
                        ->state(fn (DberpAuditLog $record): string => app(DberpAuditLogCategoryResolver::class)->label(
                            app(DberpAuditLogCategoryResolver::class)->resolve($record->action),
                        )),
                    TextEntry::make('action')
                        ->label(__('dberp.fields.action'))
                        ->badge()
                        ->formatStateUsing(fn (?string $state): string => DberpAuditDisplay::actionLabel($state)),
                    TextEntry::make('auditable_summary')
                        ->label(__('dberp.audit.auditable_summary'))
                        ->state(fn (DberpAuditLog $record): string => DberpAuditDisplay::auditableSummary($record->auditable_type, $record->auditable_id))
                        ->columnSpanFull(),
                    TextEntry::make('correlation_id')
                        ->label(__('dberp.audit.correlation_id'))
                        ->copyable()
                        ->placeholder('-'),
                    TextEntry::make('ip_address')
                        ->label(__('dberp.fields.ip_address'))
                        ->placeholder('-'),
                    TextEntry::make('user_agent')
                        ->label(__('dberp.fields.user_agent'))
                        ->placeholder('-')
                        ->columnSpanFull(),
                ])
                ->columns(2),
            Section::make(__('dberp.audit_display.business_changes'))
                ->schema([
                    TextEntry::make('business_changes')
                        ->hiddenLabel()
                        ->listWithLineBreaks()
                        ->bulleted()
                        ->state(fn (DberpAuditLog $record): array => app(DberpAuditLogDisplayFormatter::class)->buildView($record)->businessDetailLines),
                ]),
        ]);
    }

    public static function getEloquentQuery(): Builder
    {
        return app(QueryCenterService::class)
            ->auditLogQuery()
            ->orderByDesc('created_at')
            ->orderByDesc('id');
    }

    public static function formatAuditLogTimestamp(?CarbonInterface $state): ?string
    {
        if ($state === null) {
            return null;
        }

        $formatted = $state->timezone((string) config('app.display_timezone'))->format('Y-m-d H:i:s');

        if ($state->micro > 0) {
            $formatted .= '.'.str_pad((string) (int) floor($state->micro / 1000), 3, '0', STR_PAD_LEFT);
        }

        return $formatted;
    }

    public static function table(Table $table): Table
    {
        $batchGroupPresenter = app(DberpAuditLogBatchGroupPresenter::class);

        return $table
            ->defaultSort('created_at', 'desc')
            ->groups([
                Group::make('correlation_batch')
                    ->column('correlation_id')
                    ->label(__('dberp.audit.batch_group_label'))
                    ->titlePrefixedWithLabel(false)
                    ->collapsible()
                    ->getKeyFromRecordUsing(fn (DberpAuditLog $record): string => $batchGroupPresenter->groupKey($record))
                    ->getTitleFromRecordUsing(fn (DberpAuditLog $record): string => $batchGroupPresenter->title($record))
                    ->getDescriptionFromRecordUsing(fn (DberpAuditLog $record): ?string => $batchGroupPresenter->description($record))
                    ->scopeQueryByKeyUsing(fn (Builder $query, ?string $key): Builder => $batchGroupPresenter->scopeQueryByKey($query, $key))
                    ->orderQueryUsing(fn (Builder $query, string $direction): Builder => $query
                        ->orderBy('correlation_id', $direction)
                        ->orderByDesc('created_at')
                        ->orderByDesc('id')),
            ])
            ->columns([
                TextColumn::make('created_at')
                    ->label(__('dberp.fields.created_at'))
                    ->formatStateUsing(fn (?CarbonInterface $state): ?string => static::formatAuditLogTimestamp($state))
                    ->sortable(),
                TextColumn::make('user.name')
                    ->label(__('dberp.fields.user'))
                    ->formatStateUsing(fn (?string $state): string => ($state !== null && $state !== '') ? $state : (string) __('dberp.audit.operator_system')),
                TextColumn::make('operation_summary')
                    ->label(__('dberp.audit_display.list_summary'))
                    ->wrap()
                    ->state(fn (DberpAuditLog $record): string => app(DberpAuditLogDisplayFormatter::class)->listOperationSummary($record)),
                TextColumn::make('category')
                    ->label(__('dberp.audit.filter_category'))
                    ->badge()
                    ->state(function (DberpAuditLog $record): string {
                        $resolver = app(DberpAuditLogCategoryResolver::class);

                        return $resolver->label($resolver->resolve($record->action));
                    })
                    ->color(function (DberpAuditLog $record): string {
                        return match (app(DberpAuditLogCategoryResolver::class)->resolve($record->action)) {
                            DberpAuditLogCategory::Business => 'success',
                            DberpAuditLogCategory::FieldChange => 'gray',
                            DberpAuditLogCategory::Security => 'warning',
                            DberpAuditLogCategory::System => 'info',
                        };
                    }),
                TextColumn::make('correlation_id')
                    ->label(__('dberp.audit.correlation_id_short'))
                    ->toggleable(isToggledHiddenByDefault: true)
                    ->formatStateUsing(fn (?string $state): string => $state !== null && $state !== '' ? substr($state, 0, 8) : '-')
                    ->tooltip(fn (DberpAuditLog $record): ?string => ($record->correlation_id !== null && $record->correlation_id !== '') ? $record->correlation_id : null)
                    ->copyable()
                    ->copyMessage(__('dberp.audit.correlation_id_copied')),
                TextColumn::make('action')
                    ->label(__('dberp.fields.action'))
                    ->badge()
                    ->formatStateUsing(fn (?string $state): string => DberpAuditDisplay::actionLabel($state))
                    ->tooltip(fn (DberpAuditLog $record): ?string => ($record->action !== null && $record->action !== '') ? $record->action : null),
                TextColumn::make('auditable_summary')
                    ->label(__('dberp.audit.auditable_summary'))
                    ->wrap()
                    ->state(fn (DberpAuditLog $record): string => DberpAuditDisplay::auditableSummary($record->auditable_type, $record->auditable_id)),
                TextColumn::make('old_values_summary')->label(__('dberp.fields.old_values_summary'))->wrap()->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('new_values_summary')->label(__('dberp.fields.new_values_summary'))->wrap()->toggleable(isToggledHiddenByDefault: true),
                TextColumn::make('ip_address')->label(__('dberp.fields.ip_address'))->toggleable(isToggledHiddenByDefault: true),
            ])
            ->filters([
                SelectFilter::make('category')
                    ->label(__('dberp.audit.filter_category'))
                    ->placeholder(__('dberp.audit.filter_category_all'))
                    ->options(fn (): array => collect(DberpAuditLogCategory::cases())
                        ->mapWithKeys(fn (DberpAuditLogCategory $category): array => [
                            $category->value => app(DberpAuditLogCategoryResolver::class)->label($category),
                        ])
                        ->all())
                    ->query(function (Builder $query, array $data): Builder {
                        $category = DberpAuditLogCategory::tryFrom((string) ($data['value'] ?? ''));

                        return $category === null
                            ? $query
                            : app(DberpAuditLogCategoryQuery::class)->apply($query, $category);
                    }),
                SelectFilter::make('user_id')->label(__('dberp.fields.user'))->options(fn (): array => User::query()->orderBy('id')->pluck('name', 'id')->all()),
                Filter::make('correlation_id')
                    ->schema([
                        TextInput::make('value')->label(__('dberp.audit.filter_correlation_id')),
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        $value = $data['value'] ?? null;

                        return filled($value)
                            ? $query->where('correlation_id', (string) $value)
                            : $query;
                    }),
                Filter::make('action')
                    ->schema([
                        TextInput::make('value')->label(__('dberp.audit.filter_action_keyword')),
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        return $query->when($data['value'] ?? null, fn (Builder $builder, $value): Builder => $builder->where('action', 'like', '%'.$value.'%'));
                    }),
                Filter::make('created_at')
                    ->schema([
                        DatePicker::make('from')->label(__('dberp.audit.filter_created_from')),
                        DatePicker::make('until')->label(__('dberp.audit.filter_created_until')),
                    ])
                    ->query(function (Builder $query, array $data): Builder {
                        return $query
                            ->when($data['from'] ?? null, fn (Builder $builder, $date): Builder => $builder->whereDate('created_at', '>=', $date))
                            ->when($data['until'] ?? null, fn (Builder $builder, $date): Builder => $builder->whereDate('created_at', '<=', $date));
                    }),
            ])
            ->headerActions([
                static::makeExportAction('audit-log'),
            ])
            ->recordActions([
                ViewAction::make(),
            ]);
    }

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

    public static function canViewAny(): bool
    {
        return static::allowsPermission(DberpPermissionService::VIEW_AUDIT_LOG);
    }

    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 getNavigationGroup(): string|UnitEnum|null
    {
        return __('dberp.nav.system');
    }

    public static function getNavigationIcon(): string|BackedEnum|Htmlable|null
    {
        return null;
    }

    public static function hasRecordTitle(): bool
    {
        return true;
    }

    public static function getRecordTitle(?Model $record): string|Htmlable|null
    {
        if (! $record instanceof DberpAuditLog) {
            return parent::getRecordTitle($record);
        }

        return app(DberpAuditLogDisplayFormatter::class)->buildView($record)->operationSummary;
    }

    public static function getModelLabel(): string
    {
        return __('dberp.resources.audit_log.label');
    }

    public static function getPluralModelLabel(): string
    {
        return __('dberp.resources.audit_log.plural');
    }
}
