<?php

namespace App\Modules\Inventory\Stock\Repositories;

use App\Modules\Inventory\Stock\Models\InventoryStockProjection;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class InventoryStockProjectionRepository
{
    public function lockForWarehouseItem(int $tenantId, int $warehouseId, int $itemId): InventoryStockProjection
    {
        $row = $this->lockedRow($tenantId, $warehouseId, $itemId);
        if ($row !== null) {
            return $row;
        }

        try {
            InventoryStockProjection::withoutGlobalScopes()->create([
                'tenant_id' => $tenantId,
                'warehouse_id' => $warehouseId,
                'item_id' => $itemId,
                'on_hand_quantity' => '0',
                'reserved_quantity' => '0',
                'available_quantity' => '0',
                'row_version' => 0,
            ]);
        } catch (\Illuminate\Database\QueryException) {
            // concurrent create — fall through to lock
        }

        $row = $this->lockedRow($tenantId, $warehouseId, $itemId);
        if ($row === null) {
            throw new \RuntimeException('Failed to acquire inventory_stock_projections row.');
        }

        return $row;
    }

    private function lockedRow(int $tenantId, int $warehouseId, int $itemId): ?InventoryStockProjection
    {
        return InventoryStockProjection::withoutGlobalScopes()
            ->where('tenant_id', $tenantId)
            ->where('warehouse_id', $warehouseId)
            ->where('item_id', $itemId)
            ->lockForUpdate()
            ->first();
    }

    public function save(InventoryStockProjection $projection): void
    {
        $projection->save();
    }

    public function deleteForTenant(int $tenantId): int
    {
        return InventoryStockProjection::withoutGlobalScopes()->where('tenant_id', $tenantId)->delete();
    }

    /**
     * @param  array<int, array{warehouse_id:int,item_id:int,on_hand:string,reserved:string,available:string,last_movement_id:?int}>  $rows
     */
    public function bulkReplaceForTenant(int $tenantId, array $rows): void
    {
        $this->deleteForTenant($tenantId);
        foreach ($rows as $r) {
            InventoryStockProjection::withoutGlobalScopes()->create([
                'tenant_id' => $tenantId,
                'warehouse_id' => $r['warehouse_id'],
                'item_id' => $r['item_id'],
                'on_hand_quantity' => $r['on_hand'],
                'reserved_quantity' => $r['reserved'],
                'available_quantity' => $r['available'],
                'row_version' => 0,
                'last_movement_id' => $r['last_movement_id'],
                'last_rebuilt_at' => now(),
            ]);
        }
    }

    /**
     * @return LengthAwarePaginator<int, InventoryStockProjection>
     */
    public function paginatedForTenantReadOnly(int $tenantId, int $perPage = 30): LengthAwarePaginator
    {
        return InventoryStockProjection::query()
            ->where('tenant_id', $tenantId)
            ->with(['warehouse:id,code,name', 'item:id,sku,name'])
            ->orderBy('warehouse_id')
            ->orderBy('item_id')
            ->paginate($perPage);
    }
}
