<?php

namespace App\Modules\Inventory\Repositories;

use App\Modules\Inventory\Models\Item;
use App\Support\Lists\ListQuery;
use App\Support\SelectOptionFormatter;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;

class ItemRepository
{
    public function findBySku(string $sku): ?Item
    {
        return Item::query()->where('sku', $sku)->first();
    }

    /**
     * @return Builder<Item>
     */
    public function queryWithCatalogRelations(): Builder
    {
        return Item::query()->with([
            'category',
            'defaultUnit',
            'parent',
            'prices.priceType',
        ]);
    }

    /**
     * @return Builder<Item>
     */
    public function queryWithOperationalCatalog(): Builder
    {
        return $this->queryWithCatalogRelations()->with([
            'variants' => static fn ($q) => $q->orderBy('sku'),
            'attributeValues.attribute.options',
        ]);
    }

    /**
     * @return LengthAwarePaginator<int, Item>
     */
    public function paginatedCatalog(?int $categoryId, int $perPage = 25): LengthAwarePaginator
    {
        $q = Item::query()->with(['category', 'defaultUnit', 'parent']);
        if ($categoryId !== null) {
            $q->where('inventory_category_id', $categoryId);
        }

        return $q->orderBy('sku')->paginate($perPage)->withQueryString();
    }

    public function paginateForIndex(?int $categoryId, Request $request): LengthAwarePaginator
    {
        $query = Item::query()->with(['category', 'defaultUnit', 'parent']);

        if ($categoryId !== null) {
            $query->where('inventory_category_id', $categoryId);
        }

        ListQuery::applySearch($query, $request->input('q'), ['name', 'sku', 'barcode']);
        ListQuery::applyBoolean($query, 'is_active', $request->input('is_active'));
        ListQuery::applySort(
            $query,
            $request->input('sort'),
            $request->input('dir'),
            [
                'sku' => 'sku',
                'name' => 'name',
                'is_active' => 'is_active',
            ],
            'sku',
        );

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

    public function findOperationalCatalog(int $itemId): ?Item
    {
        return $this->queryWithOperationalCatalog()->whereKey($itemId)->first();
    }

    /**
     * @param  array<string, mixed>  $attributes
     */
    public function create(int $tenantId, array $attributes): Item
    {
        return Item::query()->create(array_merge(['tenant_id' => $tenantId], $attributes));
    }

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

        if ($onlyId !== null) {
            $builder->whereKey($onlyId);
        } else {
            if ($query !== '') {
                $builder->where(function ($q) use ($query): void {
                    $q->where('name', 'like', '%'.$query.'%')
                        ->orWhere('sku', 'like', '%'.$query.'%')
                        ->orWhere('barcode', 'like', '%'.$query.'%');
                });
            }
            $builder->limit($limit);
        }

        return SelectOptionFormatter::mapRows(
            $builder->get(['id', 'name', 'sku']),
            'name',
            'sku',
        );
    }
}
