<?php

namespace App\Modules\Sales\Services;

use App\Modules\Sales\Exceptions\SalesReturnOperationalException;
use App\Modules\Sales\Models\SalesReturnLine;
use App\Modules\Sales\Repositories\SalesReturnRepository;
use App\Modules\Sales\Support\SalesReturnStatus;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

final class SalesReturnProcessService
{
    public function __construct(
        private readonly SalesReturnRepository $returns,
        private readonly SalesReturnValidationService $validator,
        private readonly SalesReturnStockCompensationService $compensation,
        private readonly SalesReturnFulfillmentAdjustmentService $fulfillment,
    ) {}

    public function process(
        int $tenantId,
        int $returnId,
        ?int $actorUserId,
        ?string $correlationId,
    ): void {
        $cid = ($correlationId !== null && $correlationId !== '') ? $correlationId : (string) Str::uuid();
        $attempts = (int) config('inventory.transaction_attempts', 5);
        DB::transaction(function () use ($tenantId, $returnId, $actorUserId, $cid): void {
            $ret = $this->returns->lockByIdForTenant($tenantId, $returnId);
            if ($ret === null) {
                throw new SalesReturnOperationalException('Return not found.', 'sales_return_not_found');
            }
            if ($ret->status === SalesReturnStatus::Processed->value) {
                return;
            }
            $this->validator->assertReturnReadyToProcess($ret);
            $ret->load(['lines.salesOrderLine', 'salesOrder']);
            $order = $ret->salesOrder;
            if ($order === null) {
                throw new SalesReturnOperationalException('Sales order missing.', 'sales_return_order_missing');
            }
            $this->validator->assertOrderAllowsReturns($order);
            foreach ($ret->lines as $rl) {
                /** @var SalesReturnLine $rl */
                $this->processLine($tenantId, $ret->id, $rl, $actorUserId, $cid);
            }
            $ret->forceFill([
                'status' => SalesReturnStatus::Processed->value,
                'processed_at' => now(),
                'correlation_id' => $cid,
                'actor_user_id' => $actorUserId,
            ]);
            $this->returns->save($ret);
        }, $attempts);
    }

    private function processLine(
        int $tenantId,
        int $salesReturnId,
        SalesReturnLine $rl,
        ?int $actorUserId,
        string $cid,
    ): void {
        $ol = $rl->salesOrderLine;
        if ($ol === null) {
            throw new SalesReturnOperationalException('Order line missing.', 'sales_return_line_missing');
        }
        $qty = trim((string) $rl->quantity);
        $this->validator->assertLineQuantity($tenantId, $ol, $qty, null);
        $this->compensation->compensateLine($tenantId, $rl, $ol, $qty, $salesReturnId, $actorUserId, $cid);
        $this->fulfillment->reduceFulfilledQuantity($ol, $qty);
    }
}
