<?php

namespace App\Modules\Sales\Services;

use App\Modules\Inventory\Stock\Repositories\InventoryStockMovementRepository;
use App\Modules\Inventory\Stock\Services\StockInService;
use App\Modules\Inventory\Stock\Support\DecimalQuantity;
use App\Modules\Sales\Exceptions\SalesReturnOperationalException;
use App\Modules\Sales\Models\SalesOrderLine;
use App\Modules\Sales\Models\SalesReturnLine;

final class SalesReturnStockCompensationService
{
    public function __construct(
        private readonly InventoryStockMovementRepository $movements,
        private readonly StockInService $stockIn,
    ) {}

    /**
     * Compensates fulfilled stock_out ledger rows using FIFO per movement row.
     *
     * @return list<int> Created compensating stock_in movement ids
     */
    public function compensateLine(
        int $tenantId,
        SalesReturnLine $returnLine,
        SalesOrderLine $orderLine,
        string $quantity,
        int $salesReturnId,
        ?int $actorUserId,
        string $correlationId,
    ): array {
        $remaining = trim($quantity);
        $created = [];
        $outs = $this->movements->listStockOutsForSalesOrderLine($tenantId, (int) $orderLine->id);
        foreach ($outs as $out) {
            if (! DecimalQuantity::gtZero($remaining)) {
                break;
            }
            $outQtyAbs = DecimalQuantity::negate(trim((string) $out->quantity));
            $compensated = $this->movements->sumStockInCompensationForStockOut($tenantId, (int) $out->id);
            $openOnOut = DecimalQuantity::add($outQtyAbs, DecimalQuantity::negate($compensated));
            if (! DecimalQuantity::gtZero($openOnOut)) {
                continue;
            }
            $take = DecimalQuantity::gte($openOnOut, $remaining) ? $remaining : $openOnOut;
            $idem = 'sr'.$salesReturnId.'-rl'.$returnLine->id.'-o'.$out->id.'-q'.$take;
            $r = $this->stockIn->apply(
                $tenantId,
                (int) $orderLine->warehouse_id,
                (int) $orderLine->item_id,
                $take,
                $actorUserId,
                $correlationId,
                $idem,
                'sales_return.compensation',
                null,
                'sales_return_line',
                (int) $returnLine->id,
                [
                    'sales_return_id' => $salesReturnId,
                    'sales_order_line_id' => (int) $orderLine->id,
                    'reversed_stock_out_movement_id' => (int) $out->id,
                ],
                (int) $out->id,
            );
            $created[] = $r->movementId;
            $remaining = DecimalQuantity::add($remaining, DecimalQuantity::negate($take));
        }
        if (DecimalQuantity::gtZero($remaining)) {
            throw new SalesReturnOperationalException('Unable to match return quantity to fulfillment movements.', 'sales_return_trace_missing');
        }

        return $created;
    }
}
