<?php

namespace App\Support\Lists;

use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;

/**
 * Server-side list helpers: search, whitelisted sort, filters, pagination.
 */
final class ListQuery
{
    public const DEFAULT_PER_PAGE = 25;

    /** @var list<int> */
    public const PER_PAGE_OPTIONS = [25, 50, 100];

    /**
     * @param  list<string>  $columns
     */
    public static function applySearch(Builder $query, ?string $term, array $columns): Builder
    {
        $term = trim((string) $term);
        if ($term === '' || $columns === []) {
            return $query;
        }

        $like = '%'.$term.'%';

        return $query->where(function (Builder $q) use ($columns, $like): void {
            foreach ($columns as $index => $column) {
                $method = $index === 0 ? 'where' : 'orWhere';
                $q->{$method}($column, 'like', $like);
            }
        });
    }

    /**
     * @param  array<string, string>  $sortable  request key => column|expression
     */
    public static function applySort(
        Builder $query,
        ?string $sort,
        ?string $direction,
        array $sortable,
        string $defaultColumn,
        string $defaultDirection = 'asc',
    ): Builder {
        $direction = strtolower((string) $direction) === 'desc' ? 'desc' : 'asc';

        if ($sort !== null && $sort !== '' && isset($sortable[$sort])) {
            return $query->orderBy($sortable[$sort], $direction);
        }

        $defaultDirection = strtolower($defaultDirection) === 'desc' ? 'desc' : 'asc';

        return $query->orderBy($defaultColumn, $defaultDirection);
    }

    public static function applyEquals(Builder $query, string $column, mixed $value): Builder
    {
        if ($value === null || $value === '') {
            return $query;
        }

        return $query->where($column, $value);
    }

    public static function applyBoolean(Builder $query, string $column, mixed $value): Builder
    {
        if ($value === null || $value === '') {
            return $query;
        }

        $bool = filter_var($value, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
        if ($bool === null) {
            return $query;
        }

        return $query->where($column, $bool);
    }

    public static function applyDateFrom(Builder $query, string $column, mixed $from): Builder
    {
        if ($from === null || $from === '') {
            return $query;
        }

        return $query->whereDate($column, '>=', $from);
    }

    public static function applyDateTo(Builder $query, string $column, mixed $to): Builder
    {
        if ($to === null || $to === '') {
            return $query;
        }

        return $query->whereDate($column, '<=', $to);
    }

    public static function perPage(Request $request, int $default = self::DEFAULT_PER_PAGE): int
    {
        $perPage = (int) $request->integer('per_page', $default);

        return in_array($perPage, self::PER_PAGE_OPTIONS, true) ? $perPage : $default;
    }

    public static function paginate(Builder $query, Request $request, int $default = self::DEFAULT_PER_PAGE): LengthAwarePaginator
    {
        return $query->paginate(self::perPage($request, $default))->withQueryString();
    }
}
