<?php

namespace App\Support;

use App\Models\CustomerOrder;
use App\Models\CustomerOrderItem;
use App\Models\FactoryProduct;
use App\Models\ProductionOrder;
use App\Models\ProductionOrderMaterial;
use Illuminate\Support\Facades\DB;

class CustomerOrderFulfillment
{
    /**
     * @param  list<array{factory_product_id: int, quantity: float|int|string}>  $items
     */
    public static function createOrder(
        int $customerId,
        string $orderedAt,
        ?string $notes,
        array $items,
    ): CustomerOrder {
        return DB::transaction(function () use ($customerId, $orderedAt, $notes, $items): CustomerOrder {
            $order = CustomerOrder::query()->create([
                'customer_id' => $customerId,
                'ordered_at' => $orderedAt,
                'status' => CustomerOrderStatus::OPEN,
                'notes' => $notes,
            ]);
            $order->order_number = CustomerOrder::buildOrderNumber((int) $order->id, $order->created_at);
            $order->save();

            foreach ($items as $row) {
                $productId = (int) $row['factory_product_id'];
                $quantity = round((float) $row['quantity'], 3);

                if ($quantity <= 0) {
                    continue;
                }

                $item = CustomerOrderItem::query()->create([
                    'customer_order_id' => $order->id,
                    'factory_product_id' => $productId,
                    'quantity' => $quantity,
                    'fulfilled_quantity' => 0,
                    'status' => CustomerOrderItemStatus::PENDING,
                ]);

                self::allocateStockAndScheduleProduction($item);
            }

            self::refreshOrderStatus($order->fresh(['items']));

            return $order->fresh(['items.product.unit', 'customer']);
        });
    }

    public static function allocateStockAndScheduleProduction(CustomerOrderItem $item): void
    {
        $quantity = round((float) $item->quantity, 3);
        $available = FinishedGoodsStock::available((int) $item->factory_product_id);
        $fromStock = round(min($available, $quantity), 3);

        if ($fromStock > 0) {
            FinishedGoodsStock::adjust((int) $item->factory_product_id, -$fromStock);
            $item->fulfilled_quantity = round((float) $item->fulfilled_quantity + $fromStock, 3);
        }

        $remaining = round($quantity - (float) $item->fulfilled_quantity, 3);

        if ($remaining > 0.001) {
            ProductionOrder::query()->create([
                'factory_product_id' => $item->factory_product_id,
                'customer_order_item_id' => $item->id,
                'quantity' => $remaining,
                'status' => ProductionOrderStatus::PENDING,
            ]);
            $item->status = CustomerOrderItemStatus::IN_PRODUCTION;
        } else {
            $item->status = CustomerOrderItemStatus::READY;
        }

        $item->save();
    }

    public static function startProductionOrder(ProductionOrder $productionOrder): void
    {
        if ($productionOrder->status !== ProductionOrderStatus::PENDING) {
            throw new \InvalidArgumentException(__('Only pending production can be started.'));
        }

        DB::transaction(function () use ($productionOrder): void {
            $productionOrder = ProductionOrder::query()
                ->whereKey($productionOrder->id)
                ->lockForUpdate()
                ->firstOrFail();

            if ($productionOrder->status !== ProductionOrderStatus::PENDING) {
                return;
            }

            $productionOrder->fill([
                'status' => ProductionOrderStatus::IN_PROCESS,
                'started_at' => now(),
            ]);
            $productionOrder->save();
        });
    }

    public static function completeProductionOrder(
        ProductionOrder $productionOrder,
        array $materials,
        ?float $quantityProduced = null,
    ): void {
        if ($productionOrder->status === ProductionOrderStatus::COMPLETED) {
            return;
        }

        DB::transaction(function () use ($productionOrder, $materials, $quantityProduced): void {
            $productionOrder = ProductionOrder::query()
                ->whereKey($productionOrder->id)
                ->lockForUpdate()
                ->firstOrFail();

            if ($productionOrder->status === ProductionOrderStatus::COMPLETED) {
                return;
            }

            if (! in_array($productionOrder->status, [
                ProductionOrderStatus::PENDING,
                ProductionOrderStatus::IN_PROCESS,
            ], true)) {
                throw new \InvalidArgumentException(__('Only pending or in-process production can be completed.'));
            }

            if ($productionOrder->status === ProductionOrderStatus::PENDING) {
                $productionOrder->fill([
                    'status' => ProductionOrderStatus::IN_PROCESS,
                    'started_at' => now(),
                ]);
                $productionOrder->save();
            }

            $productionOrder->loadMissing('materials');
            $orderedQuantity = round((float) $productionOrder->quantity, 3);
            $materialsAlreadyRecorded = $productionOrder->materials->isNotEmpty();

            if ($materialsAlreadyRecorded) {
                $quantityProduced = $orderedQuantity;
            } else {
                $quantityProduced = round($quantityProduced ?? $orderedQuantity, 3);
            }

            if ($quantityProduced <= 0) {
                throw new \InvalidArgumentException(__('Production quantity must be greater than zero.'));
            }

            if ($materialsAlreadyRecorded && $quantityProduced > $orderedQuantity + 0.001) {
                throw new \InvalidArgumentException(__('Cannot produce more than this job quantity after materials were already recorded.'));
            }

            if (! $materialsAlreadyRecorded) {
                $normalizedMaterials = ProductionRecipe::normalizeMaterialLines($materials);

                if ($normalizedMaterials === []) {
                    $normalizedMaterials = ProductionRecipe::scaledMaterialNeeds(
                        (int) $productionOrder->factory_product_id,
                        $quantityProduced,
                    );
                }

                if ($normalizedMaterials === []) {
                    throw new \InvalidArgumentException(__('No recipe defined for this factory product.'));
                }

                ProductionRecipe::consumeManualMaterials($normalizedMaterials, [
                    'type' => StockMovementType::PRODUCTION_MATERIAL_OUT,
                    'reference_type' => ProductionOrder::class,
                    'reference_id' => $productionOrder->id,
                ]);
                self::storeProductionMaterials($productionOrder, $normalizedMaterials);
            }

            $customerFulfillQuantity = self::customerFulfillQuantityForProduction(
                $productionOrder,
                $quantityProduced,
            );

            self::finalizeProductionOrder(
                $productionOrder,
                $quantityProduced,
                $customerFulfillQuantity,
            );
        });
    }

    /**
     * Record a batch production run: issue materials and move jobs to in-process.
     * Finish on the Production page with Complete.
     *
     * @param  list<array{purchase_product_id: int, quantity: float|int|string}>  $materials
     * @return array{allocated_to_orders: float, surplus_in_process: float}
     */
    public static function batchRecordProductionForProduct(
        int $factoryProductId,
        float $quantityProduced,
        array $materials,
    ): array {
        $quantityProduced = round($quantityProduced, 3);

        if ($quantityProduced <= 0) {
            throw new \InvalidArgumentException(__('Production quantity must be greater than zero.'));
        }

        return DB::transaction(function () use ($factoryProductId, $quantityProduced, $materials): array {
            $pendingOrders = ProductionOrder::query()
                ->where('factory_product_id', $factoryProductId)
                ->where('status', ProductionOrderStatus::PENDING)
                ->orderBy('created_at')
                ->orderBy('id')
                ->lockForUpdate()
                ->get();

            $normalizedMaterials = ProductionRecipe::normalizeMaterialLines($materials);

            if ($normalizedMaterials === []) {
                $normalizedMaterials = ProductionRecipe::scaledMaterialNeeds(
                    $factoryProductId,
                    $quantityProduced,
                );
            }

            if ($normalizedMaterials === []) {
                throw new \InvalidArgumentException(__('No recipe defined for this factory product.'));
            }

            ProductionRecipe::consumeManualMaterials($normalizedMaterials, [
                'type' => StockMovementType::PRODUCTION_MATERIAL_OUT,
                'reference_type' => FactoryProduct::class,
                'reference_id' => $factoryProductId,
            ]);

            $remaining = $quantityProduced;
            $allocatedToOrders = 0.0;
            /** @var list<ProductionOrder> $allocatedProductionOrders */
            $allocatedProductionOrders = [];

            foreach ($pendingOrders as $productionOrder) {
                if ($remaining <= 0.001) {
                    break;
                }

                $needed = round((float) $productionOrder->quantity, 3);
                $alloc = round(min($remaining, $needed), 3);

                if ($alloc <= 0) {
                    continue;
                }

                $materialShare = self::scaleMaterialLines(
                    $normalizedMaterials,
                    $alloc / $quantityProduced,
                );

                self::markProductionInProcess(
                    $productionOrder,
                    $alloc,
                    $materialShare,
                );

                $allocatedProductionOrders[] = $productionOrder->fresh();
                $allocatedToOrders = round($allocatedToOrders + $alloc, 3);
                $remaining = round($remaining - $alloc, 3);
            }

            $surplusInProcess = $remaining;

            if (
                $surplusInProcess > 0.001
                && count($allocatedProductionOrders) === 1
                && $allocatedProductionOrders[0]->customer_order_item_id !== null
            ) {
                $linkedOrder = ProductionOrder::query()
                    ->whereKey($allocatedProductionOrders[0]->id)
                    ->lockForUpdate()
                    ->firstOrFail();

                $linkedOrder->quantity = round((float) $linkedOrder->quantity + $surplusInProcess, 3);
                $linkedOrder->save();

                self::storeProductionMaterials(
                    $linkedOrder,
                    self::scaleMaterialLines(
                        $normalizedMaterials,
                        $surplusInProcess / $quantityProduced,
                    ),
                );

                $surplusInProcess = 0.0;
            }

            if ($surplusInProcess > 0.001) {
                $surplusOrder = ProductionOrder::query()->create([
                    'factory_product_id' => $factoryProductId,
                    'customer_order_item_id' => null,
                    'quantity' => $surplusInProcess,
                    'status' => ProductionOrderStatus::IN_PROCESS,
                    'started_at' => now(),
                ]);

                self::storeProductionMaterials(
                    $surplusOrder,
                    self::scaleMaterialLines(
                        $normalizedMaterials,
                        $surplusInProcess / $quantityProduced,
                    ),
                );
            }

            return [
                'allocated_to_orders' => $allocatedToOrders,
                'surplus_in_process' => $surplusInProcess,
            ];
        });
    }

    /**
     * @param  list<array{purchase_product_id: int, quantity: float}>  $materialLines
     */
    private static function markProductionInProcess(
        ProductionOrder $productionOrder,
        float $outputQuantity,
        array $materialLines,
    ): void {
        $outputQuantity = round($outputQuantity, 3);

        if ($outputQuantity <= 0) {
            return;
        }

        $needed = round((float) $productionOrder->quantity, 3);

        if ($outputQuantity < $needed - 0.001) {
            ProductionOrder::query()->create([
                'factory_product_id' => $productionOrder->factory_product_id,
                'customer_order_item_id' => $productionOrder->customer_order_item_id,
                'quantity' => round($needed - $outputQuantity, 3),
                'status' => ProductionOrderStatus::PENDING,
            ]);
            $productionOrder->quantity = $outputQuantity;
        }

        self::storeProductionMaterials($productionOrder, $materialLines);

        $productionOrder->fill([
            'status' => ProductionOrderStatus::IN_PROCESS,
            'started_at' => now(),
        ]);
        $productionOrder->save();
    }

    private static function customerFulfillQuantityForProduction(
        ProductionOrder $productionOrder,
        float $quantityProduced,
    ): ?float {
        if ($productionOrder->customer_order_item_id === null) {
            return null;
        }

        $item = CustomerOrderItem::query()
            ->whereKey($productionOrder->customer_order_item_id)
            ->first();

        if ($item === null) {
            return null;
        }

        $orderRemaining = round(
            max(0, (float) $item->quantity - (float) $item->fulfilled_quantity),
            3,
        );

        return round(min($quantityProduced, $orderRemaining), 3);
    }

    private static function finalizeProductionOrder(
        ProductionOrder $productionOrder,
        float $outputQuantity,
        ?float $customerFulfillQuantity = null,
    ): void {
        $outputQuantity = round($outputQuantity, 3);

        if ($outputQuantity <= 0) {
            return;
        }

        $factoryProductId = (int) $productionOrder->factory_product_id;

        FinishedGoodsStock::adjust($factoryProductId, $outputQuantity, [
            'type' => StockMovementType::PRODUCTION_IN,
            'reference_type' => ProductionOrder::class,
            'reference_id' => $productionOrder->id,
        ]);

        $productionOrder->fill([
            'quantity' => $outputQuantity,
            'status' => ProductionOrderStatus::COMPLETED,
            'completed_at' => now(),
        ]);
        $productionOrder->save();

        if ($productionOrder->customer_order_item_id === null) {
            return;
        }

        $item = CustomerOrderItem::query()
            ->whereKey($productionOrder->customer_order_item_id)
            ->lockForUpdate()
            ->first();

        if ($item === null) {
            return;
        }

        $fulfillQuantity = round($customerFulfillQuantity ?? $outputQuantity, 3);
        $item->fulfilled_quantity = round((float) $item->fulfilled_quantity + $fulfillQuantity, 3);

        if ((float) $item->fulfilled_quantity >= (float) $item->quantity - 0.001) {
            $item->status = CustomerOrderItemStatus::READY;
        } else {
            $item->status = CustomerOrderItemStatus::IN_PRODUCTION;
        }

        $item->save();

        $order = CustomerOrder::query()->find($item->customer_order_id);

        if ($order !== null) {
            self::refreshOrderStatus($order->fresh(['items']));
        }
    }

    /**
     * @param  list<array{purchase_product_id: int, quantity: float}>  $materialLines
     */
    private static function storeProductionMaterials(
        ProductionOrder $productionOrder,
        array $materialLines,
    ): void {
        foreach ($materialLines as $line) {
            if ($line['quantity'] <= 0) {
                continue;
            }

            ProductionOrderMaterial::query()->create([
                'production_order_id' => $productionOrder->id,
                'purchase_product_id' => $line['purchase_product_id'],
                'quantity' => $line['quantity'],
            ]);
        }
    }

    /**
     * @param  list<array{purchase_product_id: int, quantity: float}>  $lines
     * @return list<array{purchase_product_id: int, quantity: float}>
     */
    private static function scaleMaterialLines(array $lines, float $ratio): array
    {
        if ($ratio <= 0) {
            return [];
        }

        return collect($lines)
            ->map(fn (array $line): array => [
                'purchase_product_id' => $line['purchase_product_id'],
                'quantity' => round($line['quantity'] * $ratio, 3),
            ])
            ->filter(fn (array $line): bool => $line['quantity'] > 0)
            ->values()
            ->all();
    }

    public static function refreshOrderStatus(CustomerOrder $order): void
    {
        $order->loadMissing('items');

        if ($order->status === CustomerOrderStatus::CANCELLED) {
            return;
        }

        $activeItems = $order->items->reject(
            fn (CustomerOrderItem $item): bool => $item->status === CustomerOrderItemStatus::CANCELLED,
        );

        if ($activeItems->isEmpty()) {
            return;
        }

        $allDelivered = $activeItems->every(
            fn (CustomerOrderItem $item): bool => $item->status === CustomerOrderItemStatus::DELIVERED,
        );

        if ($allDelivered) {
            $order->status = CustomerOrderStatus::COMPLETED;
            $order->save();

            return;
        }

        $allReadyOrDelivered = $activeItems->every(function (CustomerOrderItem $item): bool {
            return in_array($item->status, [
                CustomerOrderItemStatus::READY,
                CustomerOrderItemStatus::DELIVERED,
            ], true);
        });

        if ($allReadyOrDelivered) {
            $order->status = CustomerOrderStatus::COMPLETED;
        } else {
            $order->status = CustomerOrderStatus::OPEN;
        }

        $order->save();
    }

    public static function sellableQuantity(CustomerOrderItem $item): float
    {
        return round(max(0, (float) $item->fulfilled_quantity - (float) $item->sold_quantity), 3);
    }

    /**
     * When a customer buys less than ordered, detach unsold units from the order line.
     * Produced-but-unsold units remain in finished goods stock for future sales.
     */
    public static function releaseUnpurchasedCommitment(CustomerOrderItem $item): void
    {
        $item = CustomerOrderItem::query()
            ->whereKey($item->id)
            ->lockForUpdate()
            ->firstOrFail();

        $sold = round((float) $item->sold_quantity, 3);
        $quantity = round((float) $item->quantity, 3);
        $fulfilled = round((float) $item->fulfilled_quantity, 3);

        if ($sold <= 0 || $sold >= $quantity - 0.001) {
            return;
        }

        $unproducedCommitment = round(max(0, $quantity - $fulfilled), 3);

        if ($unproducedCommitment > 0.001) {
            self::reducePendingProductionForItem($item, $unproducedCommitment);
        }

        $item->quantity = $sold;
        $item->fulfilled_quantity = $sold;
        $item->status = CustomerOrderItemStatus::DELIVERED;
        $item->save();

        $order = CustomerOrder::query()->find($item->customer_order_id);

        if ($order !== null) {
            self::refreshOrderStatus($order->fresh(['items']));
        }
    }

    public static function markItemDelivered(CustomerOrderItem $item): void
    {
        DB::transaction(function () use ($item): void {
            $item = CustomerOrderItem::query()
                ->whereKey($item->id)
                ->lockForUpdate()
                ->firstOrFail();

            $sold = round((float) $item->sold_quantity, 3);
            $quantity = round((float) $item->quantity, 3);

            if ($sold > 0 && $sold < $quantity - 0.001) {
                self::releaseUnpurchasedCommitment($item);

                return;
            }

            $item->status = CustomerOrderItemStatus::DELIVERED;
            $item->save();

            $order = CustomerOrder::query()->find($item->customer_order_id);

            if ($order !== null) {
                self::refreshOrderStatus($order->fresh(['items']));
            }
        });
    }

    private static function reducePendingProductionForItem(CustomerOrderItem $item, float $quantityToCancel): void
    {
        $remaining = round($quantityToCancel, 3);

        if ($remaining <= 0.001) {
            return;
        }

        $pendingOrders = ProductionOrder::query()
            ->where('customer_order_item_id', $item->id)
            ->where('status', ProductionOrderStatus::PENDING)
            ->orderByDesc('id')
            ->lockForUpdate()
            ->get();

        foreach ($pendingOrders as $production) {
            if ($remaining <= 0.001) {
                break;
            }

            $productionQty = round((float) $production->quantity, 3);
            $reduce = round(min($remaining, $productionQty), 3);

            if ($reduce >= $productionQty - 0.001) {
                $production->delete();
            } else {
                $production->quantity = round($productionQty - $reduce, 3);
                $production->save();
            }

            $remaining = round($remaining - $reduce, 3);
        }
    }

    public static function canEditItem(CustomerOrderItem $item): bool
    {
        return self::canCancelItem($item);
    }

    public static function canCancelItem(CustomerOrderItem $item): bool
    {
        $item->loadMissing('order');

        if ($item->order?->status === CustomerOrderStatus::CANCELLED) {
            return false;
        }

        return ! in_array($item->status, [
            CustomerOrderItemStatus::DELIVERED,
            CustomerOrderItemStatus::CANCELLED,
        ], true);
    }

    public static function cancelItem(CustomerOrderItem $item): void
    {
        DB::transaction(function () use ($item): void {
            $item = CustomerOrderItem::query()
                ->whereKey($item->id)
                ->lockForUpdate()
                ->firstOrFail();

            $item->load('productionOrders', 'order');

            if (! self::canCancelItem($item)) {
                throw new \InvalidArgumentException(__('This line cannot be cancelled.'));
            }

            self::reverseItemFulfillment($item);
            $item->status = CustomerOrderItemStatus::CANCELLED;
            $item->save();

            $order = $item->order?->fresh(['items']);

            if ($order === null) {
                return;
            }

            $activeItems = $order->items->reject(
                fn (CustomerOrderItem $row): bool => $row->status === CustomerOrderItemStatus::CANCELLED,
            );

            if ($activeItems->isEmpty()) {
                $order->status = CustomerOrderStatus::CANCELLED;
                $order->save();

                return;
            }

            self::refreshOrderStatus($order);
        });
    }

    public static function updateItem(
        CustomerOrderItem $item,
        int $factoryProductId,
        float $quantity,
    ): CustomerOrderItem {
        return DB::transaction(function () use ($item, $factoryProductId, $quantity): CustomerOrderItem {
            $item = CustomerOrderItem::query()
                ->whereKey($item->id)
                ->lockForUpdate()
                ->firstOrFail();

            $item->load('productionOrders', 'order');

            if (! self::canEditItem($item)) {
                throw new \InvalidArgumentException(__('This line cannot be edited.'));
            }

            self::reverseItemFulfillment($item);

            $item->fill([
                'factory_product_id' => $factoryProductId,
                'quantity' => round($quantity, 3),
                'fulfilled_quantity' => 0,
                'status' => CustomerOrderItemStatus::PENDING,
            ]);
            $item->save();

            self::allocateStockAndScheduleProduction($item);

            $order = $item->order;

            if ($order !== null) {
                self::refreshOrderStatus($order->fresh(['items']));
            }

            return $item->fresh(['product.unit']);
        });
    }

    public static function canEdit(CustomerOrder $order): bool
    {
        $order->loadMissing('items');

        if ($order->status === CustomerOrderStatus::CANCELLED) {
            return false;
        }

        if ($order->items->isEmpty()) {
            return false;
        }

        return $order->items->every(
            fn (CustomerOrderItem $item): bool => $item->status !== CustomerOrderItemStatus::DELIVERED,
        );
    }

    public static function canCancel(CustomerOrder $order): bool
    {
        $order->loadMissing('items');

        if ($order->status === CustomerOrderStatus::CANCELLED) {
            return false;
        }

        return $order->items->contains(
            fn (CustomerOrderItem $item): bool => ! in_array($item->status, [
                CustomerOrderItemStatus::DELIVERED,
                CustomerOrderItemStatus::CANCELLED,
            ], true),
        );
    }

    /** @deprecated Use {@see canEdit()} */
    public static function canModify(CustomerOrder $order): bool
    {
        return self::canEdit($order);
    }

    public static function cancelOrder(CustomerOrder $order): void
    {
        DB::transaction(function () use ($order): void {
            $order = CustomerOrder::query()
                ->whereKey($order->id)
                ->lockForUpdate()
                ->firstOrFail();

            $order->load('items.productionOrders');

            if (! self::canCancel($order)) {
                throw new \InvalidArgumentException(__('This order cannot be cancelled.'));
            }

            foreach ($order->items as $item) {
                if (in_array($item->status, [
                    CustomerOrderItemStatus::DELIVERED,
                    CustomerOrderItemStatus::CANCELLED,
                ], true)) {
                    continue;
                }

                self::reverseItemFulfillment($item);
                $item->status = CustomerOrderItemStatus::CANCELLED;
                $item->save();
            }

            $order->refresh()->load('items');

            $activeItems = $order->items->reject(
                fn (CustomerOrderItem $item): bool => $item->status === CustomerOrderItemStatus::CANCELLED,
            );

            if ($activeItems->isEmpty()) {
                $order->status = CustomerOrderStatus::CANCELLED;
                $order->save();

                return;
            }

            self::refreshOrderStatus($order);
        });
    }

    /**
     * @param  list<array{factory_product_id: int, quantity: float|int|string}>  $items
     */
    public static function updateOrder(
        CustomerOrder $order,
        int $customerId,
        string $orderedAt,
        ?string $notes,
        array $items,
    ): CustomerOrder {
        return DB::transaction(function () use ($order, $customerId, $orderedAt, $notes, $items): CustomerOrder {
            $order = CustomerOrder::query()
                ->whereKey($order->id)
                ->lockForUpdate()
                ->firstOrFail();

            $order->load('items.productionOrders');

            if (! self::canEdit($order)) {
                throw new \InvalidArgumentException(__('This order cannot be edited.'));
            }

            foreach ($order->items as $item) {
                self::reverseItemFulfillment($item);
                $item->delete();
            }

            $order->fill([
                'customer_id' => $customerId,
                'ordered_at' => $orderedAt,
                'notes' => $notes,
                'status' => CustomerOrderStatus::OPEN,
            ]);
            $order->save();

            foreach ($items as $row) {
                $productId = (int) $row['factory_product_id'];
                $quantity = round((float) $row['quantity'], 3);

                if ($quantity <= 0) {
                    continue;
                }

                $item = CustomerOrderItem::query()->create([
                    'customer_order_id' => $order->id,
                    'factory_product_id' => $productId,
                    'quantity' => $quantity,
                    'fulfilled_quantity' => 0,
                    'status' => CustomerOrderItemStatus::PENDING,
                ]);

                self::allocateStockAndScheduleProduction($item);
            }

            self::refreshOrderStatus($order->fresh(['items']));

            return $order->fresh(['items.product.unit', 'customer']);
        });
    }

    public static function reverseItemFulfillment(CustomerOrderItem $item): void
    {
        $item->loadMissing('productionOrders');

        $completedQuantity = round(
            (float) $item->productionOrders
                ->where('status', ProductionOrderStatus::COMPLETED)
                ->sum(fn (ProductionOrder $production): float => (float) $production->quantity),
            3,
        );

        $fulfilled = round((float) $item->fulfilled_quantity, 3);
        $stockAdjustment = round($fulfilled - (2 * $completedQuantity), 3);

        if (abs($stockAdjustment) > 0.001) {
            FinishedGoodsStock::adjust((int) $item->factory_product_id, $stockAdjustment);
        }

        foreach ($item->productionOrders->where('status', ProductionOrderStatus::COMPLETED) as $production) {
            $production->loadMissing('materials');

            if ($production->materials->isNotEmpty()) {
                ProductionRecipe::restoreManualMaterials(
                    $production->materials->map(fn (ProductionOrderMaterial $material): array => [
                        'purchase_product_id' => (int) $material->purchase_product_id,
                        'quantity' => (float) $material->quantity,
                    ])->all(),
                    [
                        'type' => StockMovementType::PRODUCTION_MATERIAL_RESTORE,
                        'reference_type' => ProductionOrder::class,
                        'reference_id' => $production->id,
                    ],
                );
            } else {
                ProductionRecipe::restoreForProduction(
                    (int) $production->factory_product_id,
                    (float) $production->quantity,
                    [
                        'type' => StockMovementType::PRODUCTION_MATERIAL_RESTORE,
                        'reference_type' => ProductionOrder::class,
                        'reference_id' => $production->id,
                    ],
                );
            }
        }

        foreach ($item->productionOrders->where('status', ProductionOrderStatus::IN_PROCESS) as $production) {
            $production->loadMissing('materials');

            if ($production->materials->isNotEmpty()) {
                ProductionRecipe::restoreManualMaterials(
                    $production->materials->map(fn (ProductionOrderMaterial $material): array => [
                        'purchase_product_id' => (int) $material->purchase_product_id,
                        'quantity' => (float) $material->quantity,
                    ])->all(),
                    [
                        'type' => StockMovementType::PRODUCTION_MATERIAL_RESTORE,
                        'reference_type' => ProductionOrder::class,
                        'reference_id' => $production->id,
                    ],
                );
            }
        }

        $item->productionOrders()
            ->whereIn('status', [
                ProductionOrderStatus::PENDING,
                ProductionOrderStatus::IN_PROCESS,
            ])
            ->delete();
    }
}
