<?php

namespace App\Services;

use App\Models\Order;
use App\Models\OrderStatusHistory;
use App\Repositories\Eloquent\OrderRepository;
use App\Repositories\Eloquent\PaymentRepository;
use App\Repositories\Eloquent\ProductRepository;
use Illuminate\Support\Facades\DB;

class OrderService
{
    public function __construct(
        protected OrderRepository $orderRepository,
        protected PaymentRepository $paymentRepository,
        protected ProductRepository $productRepository,
        protected InventoryService $inventoryService,
        protected LoggingService $loggingService,
        protected ProductionBatchService $productionBatchService
    ) {}

    public function getAll()
    {
        $this->loggingService->logInfo('Fetching all orders');
        return $this->orderRepository->all();
    }

    public function getForBranch(int $branchId, int $limit = 50)
    {
        $this->loggingService->logInfo('Fetching orders for branch', ['branch_id' => $branchId]);
        return $this->orderRepository->getForBranch($branchId, $limit);
    }

    public function getActiveForBranch(int $branchId)
    {
        $this->loggingService->logInfo('Fetching active orders for branch', ['branch_id' => $branchId]);
        return $this->orderRepository->getActiveForBranch($branchId);
    }

    public function getActiveAll()
    {
        $this->loggingService->logInfo('Fetching all active orders across all branches');
        return $this->orderRepository->getActiveAll();
    }

    public function getByStatus(int $branchId, string $status)
    {
        $this->loggingService->logInfo('Fetching orders by status', ['branch_id' => $branchId, 'status' => $status]);
        return $this->orderRepository->getByStatus($branchId, $status);
    }

    public function getTodayForBranch(int $branchId)
    {
        $this->loggingService->logInfo('Fetching today orders for branch', ['branch_id' => $branchId]);
        return $this->orderRepository->getTodayForBranch($branchId);
    }

    public function find(int $id)
    {
        $this->loggingService->logInfo('Finding order', ['id' => $id]);
        return $this->orderRepository->findWithRelations($id);
    }

    public function create(array $data)
    {
        $this->loggingService->logInfo('Creating order', ['data' => $data]);

        return DB::transaction(function () use ($data) {
            $items = $data['items'] ?? [];
            unset($data['items']);

            $data['order_number'] = Order::generateOrderNumber($data['branch_id']);
            $data['status'] = $data['status'] ?? Order::STATUS_PENDING;

            $order = $this->orderRepository->create($data);

            foreach ($items as $item) {
                $product = $this->productRepository->find($item['product_id']);

                $order->items()->create([
                    'product_id' => $item['product_id'],
                    'product_name' => $product->name,
                    'quantity' => $item['quantity'],
                    'unit_price' => $item['unit_price'] ?? $product->base_price,
                    'total_price' => ($item['unit_price'] ?? $product->base_price) * $item['quantity'],
                    'options' => $item['options'] ?? null,
                    'inner_options' => $item['inner_options'] ?? null,
                    'special_instructions' => $item['special_instructions'] ?? null,
                    'cup_size_id' => $item['cup_size_id'] ?? null,
                    'flavors' => $item['flavors'] ?? null,
                    'total_weight_grams' => $item['total_weight_grams'] ?? null,
                ]);
            }

            $order->calculateTotals();

            // Record status history
            OrderStatusHistory::create([
                'order_id' => $order->id,
                'user_id' => $data['user_id'],
                'from_status' => null,
                'to_status' => $order->status,
                'notes' => 'Order created',
            ]);

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

    public function update(int $id, array $data)
    {
        $this->loggingService->logInfo('Updating order', ['id' => $id, 'data' => $data]);

        return DB::transaction(function () use ($id, $data) {
            $items = $data['items'] ?? null;
            unset($data['items']);

            $order = $this->orderRepository->update($id, $data);

            if ($items !== null) {
                // Delete existing items and recreate
                $order->items()->delete();

                foreach ($items as $item) {
                    $product = $this->productRepository->find($item['product_id']);

                    $order->items()->create([
                        'product_id' => $item['product_id'],
                        'product_name' => $product->name,
                        'quantity' => $item['quantity'],
                        'unit_price' => $item['unit_price'] ?? $product->base_price,
                        'total_price' => ($item['unit_price'] ?? $product->base_price) * $item['quantity'],
                        'options' => $item['options'] ?? null,
                        'inner_options' => $item['inner_options'] ?? null,
                        'special_instructions' => $item['special_instructions'] ?? null,
                    ]);
                }

                $order->calculateTotals();
            }

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

    public function updateStatus(int $id, string $status, ?int $userId = null, ?string $notes = null)
    {
        $this->loggingService->logInfo('Updating order status', ['id' => $id, 'status' => $status]);

        return DB::transaction(function () use ($id, $status, $userId, $notes) {
            $order = $this->orderRepository->find($id);
            if (!$order) {
                return null;
            }

            $fromStatus = $order->status;

            $updateData = ['status' => $status];

            if ($status === Order::STATUS_COMPLETED) {
                $updateData['completed_at'] = now();
                // Deduct inventory when order is completed
                $this->deductInventoryForOrder($order);
            }

            if ($status === Order::STATUS_CANCELLED) {
                $updateData['cancelled_at'] = now();
                $updateData['cancellation_reason'] = $notes;
            }

            $order = $this->orderRepository->update($id, $updateData);

            // Record status history
            OrderStatusHistory::create([
                'order_id' => $order->id,
                'user_id' => $userId,
                'from_status' => $fromStatus,
                'to_status' => $status,
                'notes' => $notes,
            ]);

            return $order->fresh(['items.product', 'user', 'branch', 'statusHistory']);
        });
    }

    public function cancel(int $id, ?int $userId = null, ?string $reason = null)
    {
        return $this->updateStatus($id, Order::STATUS_CANCELLED, $userId, $reason);
    }

    public function delete(int $id)
    {
        $this->loggingService->logInfo('Deleting order', ['id' => $id]);
        return $this->orderRepository->delete($id);
    }

    // Payment Methods
    public function addPayment(int $orderId, array $data)
    {
        $this->loggingService->logInfo('Adding payment to order', ['order_id' => $orderId, 'data' => $data]);

        $order = $this->orderRepository->find($orderId);
        if (!$order) {
            return null;
        }

        $data['order_id'] = $orderId;

        // Calculate change for cash payments
        if ($data['method'] === 'cash' && isset($data['tendered'])) {
            $data['change'] = max(0, $data['tendered'] - $data['amount']);
        }

        $payment = $this->paymentRepository->create($data);

        // Update cash register if payment method is cash
        if ($data['method'] === 'cash') {
            $this->updateCashRegisterForPayment($order, $data['tendered'] ?? $data['amount'], $data['change'] ?? 0);
        }

        return $payment->fresh('user');
    }

    /**
     * Update cash register with payment and change
     */
    private function updateCashRegisterForPayment(Order $order, float $received, float $change): void
    {
        $cashRegister = \App\Models\CashRegister::where('branch_id', $order->branch_id)
            ->where('status', 'open')
            ->latest()
            ->first();

        if (!$cashRegister) {
            // No open cash register - skip update (payment still recorded)
            $this->loggingService->logWarning('No open cash register found for payment', [
                'branch_id' => $order->branch_id,
                'order_id' => $order->id
            ]);
            return;
        }

        // Get CashRegisterService
        $cashRegisterService = app(\App\Services\CashRegisterService::class);

        // Record transaction: Cash received (tendered amount added to drawer)
        $cashRegisterService->recordTransaction(
            $cashRegister->id,
            \App\Models\CashRegisterTransaction::TYPE_BILL_PAYMENT,
            $received,
            auth()->id() ?? $order->user_id,
            'Order',
            $order->id,
            "Cash received for Order #{$order->order_number}"
        );

        // Record transaction: Change given (money removed from drawer)
        if ($change > 0) {
            $cashRegisterService->recordTransaction(
                $cashRegister->id,
                \App\Models\CashRegisterTransaction::TYPE_CHANGE_GIVEN,
                $change,
                auth()->id() ?? $order->user_id,
                'Order',
                $order->id,
                "Change given for Order #{$order->order_number}"
            );
        }
    }

    public function getPayments(int $orderId)
    {
        $this->loggingService->logInfo('Fetching payments for order', ['order_id' => $orderId]);
        return $this->paymentRepository->getForOrder($orderId);
    }

    public function refundPayment(int $paymentId, ?int $userId = null, ?string $notes = null)
    {
        $this->loggingService->logInfo('Refunding payment', ['payment_id' => $paymentId]);
        return $this->paymentRepository->update($paymentId, [
            'status' => 'refunded',
            'notes' => $notes,
        ]);
    }

    // Add discount to order
    public function applyDiscount(int $orderId, float $amount, ?string $reason = null)
    {
        $this->loggingService->logInfo('Applying discount', ['order_id' => $orderId, 'amount' => $amount]);

        $order = $this->orderRepository->find($orderId);
        if (!$order) {
            return null;
        }

        $order = $this->orderRepository->update($orderId, [
            'discount_amount' => $amount,
            'discount_reason' => $reason,
        ]);

        $order->calculateTotals();

        return $order->fresh();
    }

    protected function deductInventoryForOrder(Order $order): void
    {
        $this->loggingService->logInfo('Deducting inventory for order', ['order_id' => $order->id]);

        foreach ($order->items as $item) {
            // Try recipe-based hybrid deduction first (inventory + production batch)
            if ($this->deductRecipeBasedInventory($order, $item)) {
                // still fall through to topping deduction below
            } elseif ($item->cup_size_id && $item->flavors && is_array($item->flavors)) {
                // Fallback: weight-based deduction for multi-flavor cups
                $this->deductWeightBasedInventory($order, $item);
            } else {
                // Legacy scoop-based deduction
                $this->deductScoopBasedInventory($order, $item);
            }

            // Always deduct toppings regardless of which main-product strategy was used
            $this->deductToppingInventory($order, $item);
        }
    }

    /**
     * Deduct inventory for any toppings (inner options) selected on an order item.
     *
     * Each InnerOptionItem can be linked to:
     *   - ingredient_id  → deduct from stock_ledger  (sprinkles, nuts, cherries…)
     *   - source_product_id → deduct from production batch (Lotus syrup, brownie pieces…)
     *
     * deduction_grams × quantity = total grams to deduct.
     */
    protected function deductToppingInventory(Order $order, $item): void
    {
        $innerOptions = $item->inner_options;
        if (empty($innerOptions)) {
            return;
        }

        $decoded = is_array($innerOptions) ? $innerOptions : json_decode($innerOptions, true);
        if (!is_array($decoded) || empty($decoded)) {
            return;
        }

        // Collect inner_option_item IDs from the order snapshot
        $optionItemIds = array_filter(array_map(
            fn($o) => $o['option_id'] ?? $o['id'] ?? null,
            $decoded
        ));

        if (empty($optionItemIds)) {
            return;
        }

        // Load the actual InnerOptionItem records with their inventory links
        $toppingItems = \App\Models\InnerOptionItem::with(['ingredient', 'sourceProduct'])
            ->whereIn('id', $optionItemIds)
            ->get();

        foreach ($toppingItems as $topping) {
            if (!$topping->hasInventoryLink()) {
                continue; // Topping not linked to inventory — skip
            }

            $totalGrams = $topping->deduction_grams * $item->quantity;

            // --- Raw-stock topping (sprinkles, nuts, wafer, cherry…) ---
            if ($topping->ingredient_id) {
                try {
                    $this->inventoryService->deductStock(
                        $order->branch_id,
                        $topping->ingredient_id,
                        $totalGrams,
                        $order->user_id,
                        "Topping [{$topping->name}] for Order #{$order->order_number}",
                        'order',
                        $order->id
                    );

                    $this->loggingService->logInfo('Topping ingredient deducted', [
                        'order_id'      => $order->id,
                        'topping'       => $topping->name,
                        'ingredient_id' => $topping->ingredient_id,
                        'grams'         => $totalGrams,
                    ]);
                } catch (\Exception $e) {
                    $this->loggingService->logError('Failed to deduct topping ingredient', [
                        'order_id'  => $order->id,
                        'topping'   => $topping->name,
                        'error'     => $e->getMessage(),
                    ]);
                }
            }

            // --- Daily-production topping (Lotus syrup, Nutella, brownie…) ---
            if ($topping->source_product_id) {
                $batch = $this->productionBatchService->findAvailableBatchForProduct(
                    $order->branch_id,
                    $topping->source_product_id
                );

                if (!$batch) {
                    $this->loggingService->logWarning('No active batch for topping', [
                        'order_id'          => $order->id,
                        'topping'           => $topping->name,
                        'source_product_id' => $topping->source_product_id,
                    ]);
                    continue;
                }

                try {
                    $this->productionBatchService->recordWeightDeduction(
                        $batch->id,
                        $order->id,
                        $item->id,
                        $totalGrams,
                        "Topping [{$topping->name}] for Order #{$order->order_number}"
                    );

                    $this->loggingService->logInfo('Topping batch deducted', [
                        'order_id'  => $order->id,
                        'topping'   => $topping->name,
                        'batch_id'  => $batch->id,
                        'grams'     => $totalGrams,
                    ]);
                } catch (\Exception $e) {
                    $this->loggingService->logError('Failed to deduct topping batch', [
                        'order_id'  => $order->id,
                        'topping'   => $topping->name,
                        'error'     => $e->getMessage(),
                    ]);
                }
            }
        }
    }

    /**
     * Hybrid deduction: deduct inventory ingredients AND production batch grams
     * using the recipe attached to the product.
     *
     * Returns true if a recipe was found and deduction was handled.
     * Returns false if no recipe exists (caller should try the next strategy).
     */
    protected function deductRecipeBasedInventory(Order $order, $item): bool
    {
        // Load the product with its recipe and production items
        $product = $this->productRepository->find($item->product_id);
        if (!$product) {
            return false;
        }

        // Get the recipe for this product
        $recipe = \App\Models\Recipe::with([
            'recipeIngredients.ingredient',
            'recipeIngredients.sizeOption',
            'productionItems.sourceProduct',
            'productionItems.sizeOption',
        ])->where('product_id', $product->id)
          ->where('is_active', true)
          ->first();

        if (!$recipe) {
            return false; // No recipe → use legacy deduction
        }

        // Only apply recipe-based deduction if the recipe has production items OR recipe ingredients
        $hasProductionItems  = $recipe->productionItems->isNotEmpty();
        $hasRecipeIngredients = $recipe->recipeIngredients->isNotEmpty();

        if (!$hasProductionItems && !$hasRecipeIngredients) {
            return false;
        }

        // Extract the selected option_set_item_id from the order item options
        // options JSON: [{"option_id": <id>, "name": "Large", "price_modifier": "2.00"}, ...]
        $selectedOptionIds = $this->extractOptionSetItemIds($item->options);

        $quantity = $item->quantity;

        // --- 1. Deduct inventory ingredients ---
        if ($hasRecipeIngredients) {
            foreach ($recipe->recipeIngredients as $ri) {
                // Find the best-matching row: prefer size-specific, fall back to base (null)
                $effectiveRow = $this->resolveRecipeIngredientRow($recipe->recipeIngredients, $ri->ingredient_id, $selectedOptionIds);

                if (!$effectiveRow) {
                    continue;
                }

                $quantityNeeded = $effectiveRow->quantity * $quantity;

                try {
                    $this->inventoryService->deductStock(
                        $order->branch_id,
                        $effectiveRow->ingredient_id,
                        $quantityNeeded,
                        $order->user_id,
                        "Order #{$order->order_number} - {$product->name}",
                        'order',
                        $order->id
                    );

                    $this->loggingService->logInfo('Recipe inventory deducted', [
                        'order_id'      => $order->id,
                        'ingredient_id' => $effectiveRow->ingredient_id,
                        'quantity'      => $quantityNeeded,
                    ]);
                } catch (\Exception $e) {
                    $this->loggingService->logError('Failed to deduct recipe ingredient', [
                        'order_id'      => $order->id,
                        'ingredient_id' => $effectiveRow->ingredient_id,
                        'error'         => $e->getMessage(),
                    ]);
                }
            }
        }

        // --- 2. Deduct from production batches ---
        if ($hasProductionItems) {
            // Group by source_product_id so we don't look up the same batch twice
            $deductionsByProduct = [];

            foreach ($recipe->productionItems as $pi) {
                // Skip rows for other size options unless they match the selected option
                $isDefaultRow  = $pi->option_set_item_id === null;
                $matchesOption = in_array($pi->option_set_item_id, $selectedOptionIds);

                if (!$isDefaultRow && !$matchesOption) {
                    continue;
                }

                $productId = $pi->source_product_id;

                // If we have a size-specific row, prefer it over the default
                if (!isset($deductionsByProduct[$productId]) || $matchesOption) {
                    $deductionsByProduct[$productId] = $pi->grams * $quantity;
                }
            }

            foreach ($deductionsByProduct as $sourceProductId => $totalGrams) {
                $batch = $this->productionBatchService->findAvailableBatchForProduct(
                    $order->branch_id,
                    $sourceProductId
                );

                if (!$batch) {
                    $this->loggingService->logWarning('No active production batch for recipe production item', [
                        'order_id'          => $order->id,
                        'source_product_id' => $sourceProductId,
                        'branch_id'         => $order->branch_id,
                    ]);
                    continue;
                }

                try {
                    $this->productionBatchService->recordWeightDeduction(
                        $batch->id,
                        $order->id,
                        $item->id,
                        $totalGrams,
                        "Order #{$order->order_number} - {$product->name} ({$totalGrams}g)"
                    );

                    $this->loggingService->logInfo('Production batch deducted via recipe', [
                        'order_id'          => $order->id,
                        'batch_id'          => $batch->id,
                        'source_product_id' => $sourceProductId,
                        'grams'             => $totalGrams,
                    ]);
                } catch (\Exception $e) {
                    $this->loggingService->logError('Failed to deduct production batch via recipe', [
                        'order_id'          => $order->id,
                        'source_product_id' => $sourceProductId,
                        'error'             => $e->getMessage(),
                    ]);
                }
            }
        }

        return true;
    }

    /**
     * Extract option_set_item IDs from the stored options JSON on an order item.
     * options is stored as: [{"option_id": 5, "name": "Large", ...}, ...]
     */
    protected function extractOptionSetItemIds($options): array
    {
        if (!$options) {
            return [];
        }

        $decoded = is_array($options) ? $options : json_decode($options, true);
        if (!is_array($decoded)) {
            return [];
        }

        return array_values(array_filter(array_map(
            fn ($o) => $o['option_id'] ?? null,
            $decoded
        )));
    }

    /**
     * Given a collection of recipe ingredient rows for a recipe, find the best row
     * for a given ingredient: prefer the size-specific row (option_set_item_id in $selectedOptionIds)
     * over the base row (option_set_item_id = null).
     */
    protected function resolveRecipeIngredientRow($recipeIngredients, int $ingredientId, array $selectedOptionIds)
    {
        $rows = $recipeIngredients->where('ingredient_id', $ingredientId);

        // Look for a size-specific match first
        foreach ($rows as $row) {
            if ($row->option_set_item_id && in_array($row->option_set_item_id, $selectedOptionIds)) {
                return $row;
            }
        }

        // Fall back to base (null) row
        return $rows->firstWhere('option_set_item_id', null);
    }

    /**
     * Deduct inventory using weight-based tracking (new system)
     */
    protected function deductWeightBasedInventory(Order $order, $item): void
    {
        foreach ($item->flavors as $flavor) {
            $productId = $flavor['product_id'];
            $weightGrams = $flavor['weight_grams'];

            // Find an active batch for this flavor's product at this branch
            $batch = $this->productionBatchService->findAvailableBatchForProduct(
                $order->branch_id,
                $productId
            );

            if ($batch && $batch->hasRemainingWeight()) {
                try {
                    // Deduct weight from batch and create batch deduction record
                    $this->productionBatchService->recordWeightDeduction(
                        $batch->id,
                        $order->id,
                        $item->id,
                        $weightGrams,
                        "Order #{$order->order_number} - {$flavor['name']}"
                    );

                    $this->loggingService->logInfo('Weight deduction recorded', [
                        'order_id' => $order->id,
                        'product_id' => $productId,
                        'flavor' => $flavor['name'],
                        'batch_id' => $batch->id,
                        'weight_grams' => $weightGrams,
                    ]);
                } catch (\Exception $e) {
                    $this->loggingService->logError('Failed to deduct weight from batch', [
                        'order_id' => $order->id,
                        'product_id' => $productId,
                        'flavor' => $flavor['name'],
                        'error' => $e->getMessage(),
                    ]);
                }
            } else {
                $this->loggingService->logWarning('No active batch with weight found for product', [
                    'order_id' => $order->id,
                    'product_id' => $productId,
                    'flavor' => $flavor['name'],
                    'branch_id' => $order->branch_id,
                ]);
            }
        }
    }

    /**
     * Deduct inventory using scoop-based tracking (legacy system)
     */
    protected function deductScoopBasedInventory(Order $order, $item): void
    {
        // Find an active batch for this product at this branch
        $batch = $this->productionBatchService->findAvailableBatchForProduct(
            $order->branch_id,
            $item->product_id
        );

        if ($batch) {
            try {
                // Calculate scoops based on quantity (1 item = 1 scoop by default)
                $scoopCount = $item->quantity;

                // Check if there's a size option that affects scoop count
                if ($item->options) {
                    $options = is_array($item->options) ? $item->options : json_decode($item->options, true);
                    foreach ($options as $option) {
                        // If large size, count as 2 scoops, medium as 1.5, etc.
                        if (isset($option['name'])) {
                            $sizeName = strtolower($option['name']);
                            if ($sizeName === 'large') {
                                $scoopCount = $item->quantity * 2;
                            } elseif ($sizeName === 'medium') {
                                $scoopCount = (int) ceil($item->quantity * 1.5);
                            }
                        }
                    }
                }

                // Record scoop sale and deduct from batch
                $this->productionBatchService->recordScoopSale(
                    $batch->id,
                    $order->id,
                    $item->id,
                    $item->product_id,
                    $order->branch_id,
                    $scoopCount,
                    $item->total_price
                );

                $this->loggingService->logInfo('Scoop sale recorded', [
                    'order_id' => $order->id,
                    'product_id' => $item->product_id,
                    'batch_id' => $batch->id,
                    'scoop_count' => $scoopCount,
                ]);
            } catch (\Exception $e) {
                $this->loggingService->logError('Failed to deduct scoops from batch', [
                    'order_id' => $order->id,
                    'product_id' => $item->product_id,
                    'error' => $e->getMessage(),
                ]);
            }
        } else {
            $this->loggingService->logInfo('No active batch found for product', [
                'order_id' => $order->id,
                'product_id' => $item->product_id,
                'branch_id' => $order->branch_id,
            ]);
        }
    }

    // Reporting
    public function getOrdersByDateRange(int $branchId, $startDate, $endDate)
    {
        $this->loggingService->logInfo('Fetching orders by date range', [
            'branch_id' => $branchId,
            'start_date' => $startDate,
            'end_date' => $endDate
        ]);
        return $this->orderRepository->getByDateRange($branchId, $startDate, $endDate);
    }

    public function getDailySummary(int $branchId, $date = null)
    {
        $date = $date ?? today();

        $orders = $this->orderRepository->getByDateRange($branchId, $date->copy()->startOfDay(), $date->copy()->endOfDay());

        $completed = $orders->where('status', Order::STATUS_COMPLETED);

        return [
            'date' => $date->toDateString(),
            'total_orders' => $orders->count(),
            'completed_orders' => $completed->count(),
            'cancelled_orders' => $orders->where('status', Order::STATUS_CANCELLED)->count(),
            'total_sales' => $completed->sum('total_amount'),
            'average_order_value' => $completed->count() > 0 ? $completed->avg('total_amount') : 0,
        ];
    }

    // Temporary Bill Methods
    public function createTempBill(array $data)
    {
        $this->loggingService->logInfo('Creating temp bill', ['data' => $data]);

        return DB::transaction(function () use ($data) {
            $items = $data['items'] ?? [];
            unset($data['items']);

            $data['order_number'] = Order::generateOrderNumber($data['branch_id']);
            $data['is_temp_bill'] = true;
            $data['status'] = Order::STATUS_TEMP_BILL;

            $order = $this->orderRepository->create($data);

            foreach ($items as $item) {
                $product = $this->productRepository->find($item['product_id']);

                $order->items()->create([
                    'product_id' => $item['product_id'],
                    'product_name' => $product->name,
                    'quantity' => $item['quantity'],
                    'unit_price' => $item['unit_price'] ?? $product->base_price,
                    'total_price' => ($item['unit_price'] ?? $product->base_price) * $item['quantity'],
                    'options' => $item['options'] ?? null,
                    'inner_options' => $item['inner_options'] ?? null,
                    'special_instructions' => $item['special_instructions'] ?? null,
                    'cup_size_id' => $item['cup_size_id'] ?? null,
                    'flavors' => $item['flavors'] ?? null,
                    'total_weight_grams' => $item['total_weight_grams'] ?? null,
                ]);
            }

            $order->calculateTotals();

            // Record status history
            OrderStatusHistory::create([
                'order_id' => $order->id,
                'user_id' => $data['user_id'],
                'from_status' => null,
                'to_status' => Order::STATUS_TEMP_BILL,
                'notes' => 'Temporary bill created',
            ]);

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

    public function getTempBillsForBranch(int $branchId)
    {
        $this->loggingService->logInfo('Fetching temp bills for branch', ['branch_id' => $branchId]);
        return Order::tempBills()->forBranch($branchId)->with(['items.product', 'user'])->latest()->get();
    }

    public function updateTempBill(int $id, array $data, ?int $userId = null)
    {
        $this->loggingService->logInfo('Updating temp bill', ['id' => $id, 'data' => $data]);

        return DB::transaction(function () use ($id, $data, $userId) {
            $order = $this->orderRepository->find($id);

            if (!$order || !$order->isTempBill()) {
                throw new \Exception('Order is not a temporary bill or does not exist');
            }

            $items = $data['items'] ?? null;
            unset($data['items']);

            // Update order details
            if (!empty($data)) {
                $order = $this->orderRepository->update($id, $data);
            }

            // Update items if provided
            if ($items !== null) {
                // Delete existing items and recreate
                $order->items()->delete();

                foreach ($items as $item) {
                    $product = $this->productRepository->find($item['product_id']);

                    $order->items()->create([
                        'product_id' => $item['product_id'],
                        'product_name' => $product->name,
                        'quantity' => $item['quantity'],
                        'unit_price' => $item['unit_price'] ?? $product->base_price,
                        'total_price' => ($item['unit_price'] ?? $product->base_price) * $item['quantity'],
                        'options' => $item['options'] ?? null,
                        'inner_options' => $item['inner_options'] ?? null,
                        'special_instructions' => $item['special_instructions'] ?? null,
                    ]);
                }

                $order->calculateTotals();
            }

            // Record modification in status history
            OrderStatusHistory::create([
                'order_id' => $order->id,
                'user_id' => $userId,
                'from_status' => Order::STATUS_TEMP_BILL,
                'to_status' => Order::STATUS_TEMP_BILL,
                'notes' => 'Temporary bill updated',
            ]);

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

    public function finalizeTempBill(int $id, array $paymentData, ?int $userId = null)
    {
        $this->loggingService->logInfo('Finalizing temp bill', ['id' => $id, 'payment_data' => $paymentData]);

        return DB::transaction(function () use ($id, $paymentData, $userId) {
            $order = $this->orderRepository->find($id);

            if (!$order || !$order->isTempBill()) {
                throw new \Exception('Order is not a temporary bill or does not exist');
            }

            // Generate bill number
            $billNumber = Order::generateBillNumber($order->branch_id);

            // Update order to finalized state
            $order = $this->orderRepository->update($id, [
                'bill_number' => $billNumber,
                'status' => Order::STATUS_PENDING,
                'finalized_at' => now(),
            ]);

            // Add payment
            $paymentData['order_id'] = $id;
            $paymentData['user_id'] = $userId ?? $order->user_id;

            // Calculate change for cash payments
            if ($paymentData['method'] === 'cash' && isset($paymentData['tendered'])) {
                $paymentData['change'] = max(0, $paymentData['tendered'] - $paymentData['amount']);
            }

            $this->paymentRepository->create($paymentData);

            // Record status history
            OrderStatusHistory::create([
                'order_id' => $order->id,
                'user_id' => $userId,
                'from_status' => Order::STATUS_TEMP_BILL,
                'to_status' => Order::STATUS_PENDING,
                'notes' => 'Temporary bill finalized and payment received',
            ]);

            // Deduct inventory immediately since payment is complete
            $this->deductInventoryForOrder($order);

            // Mark as completed
            $this->updateStatus($id, Order::STATUS_COMPLETED, $userId, 'Order completed from temp bill');

            return $order->fresh(['items.product', 'user', 'branch', 'payments']);
        });
    }

    public function getDuplicateBillData(int $id)
    {
        $this->loggingService->logInfo('Fetching duplicate bill data', ['id' => $id]);

        $order = $this->orderRepository->findWithRelations($id);

        if (!$order) {
            return null;
        }

        // Increment print count
        $order->incrementPrintCount();

        return $order;
    }
}
