<?php

namespace App\Services\Audit;

use App\Models\SuperAdminActionLog;
use App\Models\User;
use Filament\Facades\Filament;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;

class SuperAdminAuditLogger
{
    /**
     * @param  array<string, array<int, string>>  $metadata
     */
    public function record(
        string $action,
        ?Model $subject = null,
        array $metadata = [],
        ?User $actor = null,
        ?string $source = null
    ): ?SuperAdminActionLog {
        $actor = $actor ?? Filament::auth()->user();

        if (! $this->shouldLog($actor)) {
            return null;
        }

        $metadata = $this->sanitizeMetadata($metadata);

        return SuperAdminActionLog::create([
            'actor_id' => $actor?->getKey(),
            'action' => $action,
            'subject_type' => $subject?->getMorphClass(),
            'subject_id' => $subject?->getKey(),
            'source' => $source,
            'metadata' => $metadata ?: null,
            'occurred_at' => now(),
        ]);
    }

    /**
     * @param  array<string, array<int, string>>  $metadata
     */
    public function recordModelMutation(
        string $operation,
        Model $record,
        array $metadata = [],
        ?string $source = null,
        ?User $actor = null
    ): ?SuperAdminActionLog {
        if ($operation === 'updated') {
            $changed = array_keys($record->getChanges());

            if ($changed === []) {
                return null;
            }

            $metadata = self::fieldsMetadata($changed);
        }

        $action = $this->formatAction($record, $operation);

        return $this->record(
            $action,
            $record,
            $metadata,
            $actor,
            $source
        );
    }

    /**
     * @param  array<int, string>|array<string, mixed>  $data
     * @return array<string, array<int, string>>
     */
    public static function fieldsMetadata(array $data): array
    {
        $fields = array_is_list($data) ? $data : array_keys($data);

        return ['fields' => array_values(array_unique($fields))];
    }

    private function shouldLog(?User $actor): bool
    {
        if (! $actor || ! $actor->isSuperAdmin()) {
            return false;
        }

        $panel = Filament::getCurrentPanel();

        if ($panel) {
            return $panel->getId() === 'superadmin';
        }

        if (app()->runningInConsole()) {
            return false;
        }

        return request()->is('super-admin*');
    }

    /**
     * @param  array<string, array<int, string>>  $metadata
     * @return array<string, array<int, string>>
     */
    private function sanitizeMetadata(array $metadata): array
    {
        $sanitized = [];

        foreach ($metadata as $key => $value) {
            if (! is_array($value)) {
                continue;
            }

            $values = array_values(array_filter($value, fn ($item) => is_string($item) && $item !== ''));

            if ($values === []) {
                continue;
            }

            $sanitized[$key] = $values;
        }

        return $sanitized;
    }

    private function formatAction(Model $record, string $operation): string
    {
        return Str::kebab(class_basename($record)).'.'.$operation;
    }
}
