<?php

namespace App\Modules\HR\Organization\Repositories;

use App\Support\Lists\ListQuery;
use App\Support\SelectOptionFormatter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;

abstract class OrganizationRepository
{
    /** @return class-string<Model> */
    abstract protected function modelClass(): string;

    public function list(): Collection
    {
        return $this->modelClass()::query()->with($this->relations())->orderBy('name')->get();
    }

    public function paginateForIndex(Request $request): LengthAwarePaginator
    {
        $query = $this->modelClass()::query()->with($this->relations());

        ListQuery::applySearch($query, $request->input('q'), $this->searchColumns());
        ListQuery::applyBoolean($query, 'is_active', $request->input('is_active'));
        ListQuery::applySort(
            $query,
            $request->input('sort'),
            $request->input('dir'),
            $this->sortableColumns(),
            $this->defaultSortColumn(),
            $this->defaultSortDirection(),
        );

        return ListQuery::paginate($query, $request);
    }

    /**
     * @return list<array{value: string, label: string}>
     */
    public function searchForLookup(
        string $query,
        int $limit = 30,
        ?int $onlyId = null,
        ?int $tenantId = null,
    ): array {
        $builder = $this->modelClass()::query()
            ->where('is_active', true)
            ->orderBy($this->defaultSortColumn(), $this->defaultSortDirection());

        if ($tenantId !== null && $tenantId > 0) {
            $builder->where('tenant_id', $tenantId);
        }

        if ($onlyId !== null) {
            $builder->whereKey($onlyId);
        } else {
            if ($query !== '') {
                ListQuery::applySearch($builder, $query, $this->searchColumns());
            }
            $builder->limit($limit);
        }

        return SelectOptionFormatter::mapRows(
            $builder->get(array_values(array_unique(array_merge(['id'], $this->searchColumns())))),
            'name',
            in_array('code', $this->searchColumns(), true) ? 'code' : null,
        );
    }

    /** @param array<string,mixed> $data */
    public function create(array $data): Model
    {
        return $this->modelClass()::query()->create($data);
    }

    /** @param array<string,mixed> $data */
    public function update(Model $model, array $data): Model
    {
        $model->update($data);

        return $model->fresh();
    }

    /** @return array<int,string> */
    public function listForSelect(): array
    {
        return $this->modelClass()::query()->where('is_active', true)->orderBy('name')->pluck('name', 'id')->all();
    }

    /** @return list<string> */
    protected function relations(): array
    {
        return [];
    }

    /** @return list<string> */
    protected function searchColumns(): array
    {
        return ['name', 'code'];
    }

    /** @return array<string, string> */
    protected function sortableColumns(): array
    {
        return [
            'name' => 'name',
            'code' => 'code',
            'is_active' => 'is_active',
            'created_at' => 'created_at',
        ];
    }

    protected function defaultSortColumn(): string
    {
        return 'name';
    }

    protected function defaultSortDirection(): string
    {
        return 'asc';
    }
}
