<?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 Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Maestro\Core\DataTables\DataTableResource;
use Maestro\Core\Support\PermissionGuardName;
use Spatie\Permission\Models\Role;

final class RolesDataTable implements DataTableResource
{
    public function key(): string
    {
        return 'roles';
    }

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

    public function baseQuery(Request $request): Builder
    {
        $guardName = PermissionGuardName::forApp();
        $pivotTable = config('permission.table_names.model_has_roles', 'model_has_roles');

        $query = Role::query()
            ->where('roles.guard_name', $guardName)
            ->select(['roles.id', 'roles.name', 'roles.guard_name'])
            ->withCount('permissions')
            ->with(['permissions:id,name'])
            ->selectSub(
                DB::table($pivotTable)
                    ->selectRaw('count(0)')
                    ->whereColumn($pivotTable.'.role_id', 'roles.id'),
                'users_count'
            );

        if (Schema::hasColumn('roles', 'created_at')) {
            $query->addSelect('roles.created_at');
        }
        if (Schema::hasColumn('roles', 'updated_at')) {
            $query->addSelect('roles.updated_at');
        }

        return $query;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function columns(): array
    {
        $cols = [
            [
                'key' => 'id',
                'expr' => 'roles.id',
                'type' => 'number',
                'sortable' => true,
                'searchable' => false,
                'filterable' => false,
                'filterMode' => 'equals',
                'label' => __('maestro::datatable.columns.roles.id'),
                'defaultVisible' => false,
            ],
            [
                'key' => 'name',
                'expr' => 'roles.name',
                'type' => 'text',
                'sortable' => true,
                'searchable' => true,
                'filterable' => true,
                'filterMode' => 'contains',
                'label' => __('maestro::datatable.columns.roles.name'),
                'defaultVisible' => true,
            ],
            [
                'key' => 'guard_name',
                'expr' => 'roles.guard_name',
                'type' => 'text',
                'sortable' => true,
                'searchable' => true,
                'filterable' => true,
                'filterMode' => 'contains',
                'label' => __('maestro::datatable.columns.roles.guard_name'),
                'defaultVisible' => false,
            ],
            [
                'key' => 'permissions',
                'expr' => 'roles.id',
                'type' => 'text',
                'sortable' => false,
                'searchable' => true,
                'filterable' => true,
                'filterMode' => 'contains',
                'filterHandler' => function (Builder $q, string $op, mixed $value, array $def): void {
                    $term = mb_strtolower(trim((string) $value));
                    if ($term === '') {
                        return;
                    }
                    $like = '%'.$term.'%';
                    $q->whereHas(
                        'permissions',
                        fn (Builder $pq) => $pq->whereRaw('LOWER(name) LIKE ?', [$like])
                    );
                },
                'label' => __('maestro::datatable.columns.roles.permissions'),
                'defaultVisible' => true,
            ],
            [
                'key' => 'permissions_count',
                'expr' => 'permissions_count',
                'type' => 'number',
                'sortable' => true,
                'searchable' => false,
                'filterable' => true,
                'filterMode' => 'equals',
                'label' => __('maestro::datatable.columns.roles.permissions_count'),
                'defaultVisible' => false,
            ],
            [
                'key' => 'users_count',
                'expr' => 'users_count',
                'type' => 'number',
                'sortable' => true,
                'searchable' => false,
                'filterable' => true,
                'filterMode' => 'equals',
                'label' => __('maestro::datatable.columns.roles.users_count'),
                'defaultVisible' => true,
            ],
        ];

        if (Schema::hasColumn('roles', 'created_at')) {
            $cols[] = [
                'key' => 'created_at',
                'expr' => 'roles.created_at',
                'type' => 'date',
                'sortable' => true,
                'searchable' => false,
                'filterable' => true,
                'filterMode' => 'date_equals',
                'label' => __('maestro::datatable.columns.roles.created_at'),
                'defaultVisible' => false,
            ];
        }
        if (Schema::hasColumn('roles', 'updated_at')) {
            $cols[] = [
                'key' => 'updated_at',
                'expr' => 'roles.updated_at',
                'type' => 'date',
                'sortable' => true,
                'searchable' => false,
                'filterable' => true,
                'filterMode' => 'date_equals',
                'label' => __('maestro::datatable.columns.roles.updated_at'),
                'defaultVisible' => false,
            ];
        }

        return $cols;
    }

    public function expandGlobalSearch(Builder $q, string $term): void
    {
        $like = '%'.mb_strtolower($term).'%';
        $q->orWhereHas(
            'permissions',
            fn (Builder $pq) => $pq->whereRaw('LOWER(name) LIKE ?', [$like])
        );
    }

    public function transformRow(object $model): array
    {
        $perms = [];
        foreach ($model->permissions ?? [] as $p) {
            if (isset($p->name)) {
                $perms[] = $p->name;
            }
        }

        $row = [
            'id' => $model->id,
            'name' => $model->name ?? '',
            'guard_name' => $model->guard_name ?? '',
            'permissions' => $perms,
            'perm_search' => implode(' ', $perms),
            'permissions_count' => (int) ($model->permissions_count ?? count($perms)),
            'users_count' => (int) ($model->users_count ?? 0),
        ];

        foreach (['created_at', 'updated_at'] as $ts) {
            if (isset($model->{$ts})) {
                $dt = $model->{$ts};
                $row[$ts] = $dt instanceof \DateTimeInterface ? $dt->format(\DateTimeInterface::ATOM) : (string) $dt;
            }
        }

        return $row;
    }
}
