<?php

namespace App\Modules\Inventory\Stock\Services;

use App\Modules\Inventory\Stock\Exceptions\InventoryTransferOperationalException;
use App\Modules\Inventory\Stock\Models\InventoryStockTransfer;
use App\Modules\Inventory\Stock\Repositories\InventoryStockTransferRepository;
use App\Modules\Inventory\Stock\Support\InventoryStockTransferLifecycleRules;
use App\Modules\Inventory\Stock\Support\InventoryStockTransferStatus;
use App\Support\SystemReferenceAssigner;
use App\Support\SystemReferenceGenerator;
use Illuminate\Support\Facades\DB;

final class InventoryStockTransferLifecycleService
{
    public function __construct(
        private readonly InventoryStockTransferRepository $transfers,
        private readonly SystemReferenceAssigner $references,
    ) {}

    public function createDraft(
        int $tenantId,
        int $sourceWarehouseId,
        int $destinationWarehouseId,
        int $itemId,
        string $quantity,
    ): InventoryStockTransfer {
        $transfer = $this->transfers->createDraft([
            'tenant_id' => $tenantId,
            'status' => InventoryStockTransferStatus::Draft->value,
            'source_warehouse_id' => $sourceWarehouseId,
            'destination_warehouse_id' => $destinationWarehouseId,
            'item_id' => $itemId,
            'quantity' => trim($quantity),
        ]);

        return $this->references->assign($transfer, SystemReferenceGenerator::TRANSFER, 'transfer_number');
    }

    public function approve(int $tenantId, int $transferId): void
    {
        DB::transaction(function () use ($tenantId, $transferId): void {
            $t = $this->lockedOrFail($tenantId, $transferId);
            $s = $this->statusEnum($t);
            if (! InventoryStockTransferLifecycleRules::allowsApprove($s)) {
                throw new InventoryTransferOperationalException('Approve not allowed.', 'transfer_transition_invalid');
            }
            $t->forceFill(['status' => InventoryStockTransferStatus::Approved->value]);
            $this->transfers->save($t);
        });
    }

    public function cancel(int $tenantId, int $transferId): void
    {
        DB::transaction(function () use ($tenantId, $transferId): void {
            $t = $this->lockedOrFail($tenantId, $transferId);
            $statusVal = $this->statusEnum($t);
            if (! in_array($statusVal, InventoryStockTransferLifecycleRules::cancellableStates(), true)) {
                throw new InventoryTransferOperationalException('Cancel not allowed.', 'transfer_transition_invalid');
            }
            $t->forceFill(['status' => InventoryStockTransferStatus::Cancelled->value]);
            $this->transfers->save($t);
        });
    }

    private function statusEnum(InventoryStockTransfer $t): InventoryStockTransferStatus
    {
        $s = $t->status instanceof InventoryStockTransferStatus
            ? $t->status
            : InventoryStockTransferStatus::tryFrom((string) $t->status);
        if ($s === null) {
            throw new InventoryTransferOperationalException('Unknown transfer status.', 'transfer_bad_status');
        }

        return $s;
    }

    private function lockedOrFail(int $tenantId, int $transferId): InventoryStockTransfer
    {
        $t = $this->transfers->lockByIdForTenant($tenantId, $transferId);
        if ($t === null) {
            throw new InventoryTransferOperationalException('Transfer not found.', 'transfer_not_found');
        }

        return $t;
    }
}
