<?php

declare(strict_types=1);

namespace Maestro\Core\DataTables\Resources;

use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Maestro\Core\DataTables\DataTableResource;
use Maestro\Core\Support\AuthUserResolver;
use Spatie\Activitylog\Models\Activity;

final class ActivityLogDataTable implements DataTableResource
{
    public function key(): string
    {
        return 'activity-log';
    }

    public function authorize(Request $request): void
    {
        if (! $request->user()?->can('activitylog.view')) {
            throw new AuthorizationException('You do not have permission to view the activity log.');
        }
    }

    public function baseQuery(Request $request): Builder
    {
        return Activity::query()->with(['causer']);
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function columns(): array
    {
        $table = (new Activity)->getTable();

        return [
            [
                'key' => 'id',
                'expr' => $table.'.id',
                'type' => 'number',
                'sortable' => true,
                'searchable' => false,
                'filterable' => false,
                'filterMode' => 'equals',
                'label' => __('maestro::datatable.columns.activity-log.id'),
                'defaultVisible' => false,
            ],
            [
                'key' => 'created_at',
                'expr' => $table.'.created_at',
                'type' => 'date',
                'sortable' => true,
                'searchable' => false,
                'filterable' => false,
                'filterMode' => 'date_equals',
                'label' => __('maestro::datatable.columns.activity-log.created_at'),
                'defaultVisible' => true,
            ],
            [
                'key' => 'event',
                'expr' => $table.'.event',
                'type' => 'text',
                'sortable' => true,
                'searchable' => true,
                'filterable' => true,
                'filterMode' => 'equals',
                'allowedOperators' => ['contains', 'equals', 'starts_with', 'ends_with'],
                'label' => __('maestro::datatable.columns.activity-log.event'),
                'defaultVisible' => true,
            ],
            [
                'key' => 'description',
                'expr' => $table.'.description',
                'type' => 'text',
                'sortable' => true,
                'searchable' => true,
                'filterable' => true,
                'filterMode' => 'contains',
                'label' => __('maestro::datatable.columns.activity-log.description'),
                'defaultVisible' => true,
            ],
            [
                'key' => 'user_label',
                'expr' => $table.'.causer_id',
                'type' => 'text',
                'sortable' => false,
                'searchable' => false,
                'filterable' => true,
                'filterHandler' => function (Builder $q, string $op, mixed $value, array $def): void {
                    $term = trim((string) $value);
                    if ($term === '') {
                        return;
                    }
                    $like = '%'.mb_strtolower($term).'%';
                    $modelClass = AuthUserResolver::eloquentModelClass();
                    $q->where(static function (Builder $sub) use ($like, $modelClass): void {
                        $sub->whereHasMorph(
                            'causer',
                            [$modelClass],
                            static function (Builder $uq) use ($like): void {
                                $t = $uq->getModel()->getTable();
                                $uq->whereRaw(sprintf('LOWER(`%s`.`name`) LIKE ?', $t), [$like])
                                    ->orWhereRaw(sprintf('LOWER(`%s`.`email`) LIKE ?', $t), [$like]);
                            }
                        );
                    });
                },
                'label' => __('maestro::datatable.columns.activity-log.user_label'),
                'defaultVisible' => true,
            ],
            [
                'key' => 'subject_line',
                'expr' => $table.'.subject_id',
                'type' => 'text',
                'sortable' => false,
                'searchable' => true,
                'filterable' => false,
                'label' => __('maestro::datatable.columns.activity-log.subject_line'),
                'defaultVisible' => true,
            ],
        ];
    }

    public function expandGlobalSearch(Builder $q, string $term): void
    {
        $like = '%'.mb_strtolower($term).'%';
        $modelClass = AuthUserResolver::eloquentModelClass();

        $q->orWhereHasMorph(
            'causer',
            [$modelClass],
            static function (Builder $uq) use ($like): void {
                $t = $uq->getModel()->getTable();
                $uq->whereRaw(sprintf('LOWER(`%s`.`name`) LIKE ?', $t), [$like])
                    ->orWhereRaw(sprintf('LOWER(`%s`.`email`) LIKE ?', $t), [$like]);
            }
        );
    }

    public function transformRow(object $model): array
    {
        $createdAt = $model->created_at ?? null;
        $causer = $model->causer;
        $userLabel = null;
        if ($causer) {
            $userLabel = $causer->name ?? $causer->email ?? (string) $causer->getKey();
        }

        $subjectType = $model->subject_type ?? null;
        $subjectId = $model->subject_id ?? null;
        $subjectLine = null;
        if ($subjectType !== null || $subjectId !== null) {
            $short = '';
            if (is_string($subjectType) && str_contains((string) $subjectType, '\\')) {
                $short = preg_replace('/^.*?\\\\([^\\\\]*)$/', '$1', $subjectType) ?? '';
            } elseif ($subjectType) {
                $short = $subjectType;
            }
            $subjectLine = $subjectId !== null ? sprintf('%s #%s', $short, $subjectId) : ($short ?: null);
            if ($subjectLine !== null && $subjectLine !== '') {
                $subjectLine = mb_strtoupper(mb_substr($subjectLine, 0, 1)).mb_substr($subjectLine, 1);
            }
        }

        return [
            'id' => $model->id,
            'created_at' => $createdAt instanceof \DateTimeInterface ? $createdAt->format(\DateTimeInterface::ATOM) : (string) $createdAt,
            'event' => $model->event ?? 'action',
            'description' => $model->description,
            'user_label' => $userLabel ?? '—',
            'subject_line' => $subjectLine ?? '—',
            'causer' => $model->relationLoaded('causer') ? $model->causer : null,
            'subject_type' => $model->subject_type,
            'subject_id' => $model->subject_id,
            'properties' => $model->properties ?? null,
        ];
    }
}
