<?php

namespace App\Http\Controllers\TestingCrud;

use App\Http\Controllers\Controller;
use App\Models\Bien;
use App\Models\DevolucionPrestamo;
use App\Models\DevolucionTransferencia;
use App\Models\Dotacion;
use App\Models\Item;
use App\Models\NumOperacion;
use App\Models\Operacion;
use App\Models\Prestamo;
use App\Models\RegistroStock;
use App\Models\SitioEmbarcacion;
use App\Models\Transferencia;
use App\Models\Usuario;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Illuminate\View\View;

class ModelCrudController extends Controller
{
    /**
     * @var array<string, class-string<Model>>
     */
    private array $entities = [
        'users' => Usuario::class,
        'bienes' => Bien::class,
        'devolucion-prestamos' => DevolucionPrestamo::class,
        'devolucion-transferencias' => DevolucionTransferencia::class,
        'dotaciones' => Dotacion::class,
        'items' => Item::class,
        'num-operaciones' => NumOperacion::class,
        'operaciones' => Operacion::class,
        'prestamos' => Prestamo::class,
        'registros-stock' => RegistroStock::class,
        'sitios-embarcaciones' => SitioEmbarcacion::class,
        'transferencias' => Transferencia::class,
    ];

    public function entities(): View
    {
        return view('testing-crud.entities', [
            'entities' => $this->entities,
        ]);
    }

    public function index(Request $request, string $entity): View
    {
        $model = $this->resolveModel($entity);
        $columns = $this->getColumns($model);
        $displayColumns = $columns;
        $columnLabels = [];
        $showRowNumber = false;

        if ($entity === 'users') {
            $displayColumns = ['nombre', 'cedula', 'cargo'];
            $columnLabels = [
                'nombre' => 'Nombre',
                'cedula' => 'Cedula',
                'cargo' => 'Cargo',
            ];
            $showRowNumber = true;
        }

        $search = trim((string) $request->query('q', ''));

        $query = $model::query();

        if ($search !== '') {
            $query->where(function ($builder) use ($displayColumns, $search): void {
                foreach ($displayColumns as $column) {
                    $builder->orWhere($column, 'like', '%' . $search . '%');
                }
            });
        }

        $rows = $query
            ->orderByDesc($model->getKeyName())
            ->paginate(20)
            ->withQueryString();

        return view('testing-crud.list', [
            'entity' => $entity,
            'modelClass' => $model::class,
            'columns' => $displayColumns,
            'columnLabels' => $columnLabels,
            'showRowNumber' => $showRowNumber,
            'rows' => $rows,
            'primaryKey' => $model->getKeyName(),
            'search' => $search,
        ]);
    }

    private function resolveModel(string $entity): Model
    {
        abort_unless(array_key_exists($entity, $this->entities), 404);

        $class = $this->entities[$entity];

        return new $class();
    }

    /**
     * @return array<int, string>
     */
    private function getColumns(Model $model): array
    {
        return Schema::getColumnListing($model->getTable());
    }

    /**
     * @return array<int, string>
     */
    private function getEditableColumns(Model $model): array
    {
        $excluded = [
            $model->getKeyName(),
            'created_at',
            'updated_at',
            'remember_token',
            'email_verified_at',
        ];

        return array_values(array_filter(
            $this->getColumns($model),
            static fn (string $column): bool => ! in_array($column, $excluded, true)
        ));
    }

    /**
     * @return array<string, mixed>
     */
    private function extractPayload(Request $request, Model $model, bool $isUpdate): array
    {
        $payload = [];

        foreach ($this->getEditableColumns($model) as $column) {
            if ($isUpdate && ! $request->has($column)) {
                continue;
            }

            $value = $request->input($column);
            if (is_string($value)) {
                $value = trim($value);
            }

            if ($value === '') {
                // For testing forms, blank input should map to nullable values.
                $value = null;
            }

            if ($isUpdate && $column === 'password' && $value === null) {
                continue;
            }

            $payload[$column] = $value;
        }

        return $payload;
    }
}
