<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Models\Outlet;
use App\Models\OutletIngredient;
use App\Models\OutletProduct;
use App\Models\Product;
use App\Models\Table;
use App\Models\Customer;
use App\Models\Modifier;
use App\Models\Promo;
use App\Models\Transaction;
use App\Models\TransactionItem;
use App\Models\TransactionItemModifier;
use App\Services\SettingService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;

class PosController extends Controller
{
    public function __construct(
        protected SettingService $settingService
    ) {}

    /**
     * Display the POS cashier terminal interface.
     */
    public function index(Request $request): Response
    {
        $user = $request->user();
        
        // Determine selected outlet ID
        $outlets = Outlet::select('id', 'name')->get();
        
        $selectedOutletId = null;
        if ($user->outlet_id) {
            $selectedOutletId = (int)$user->outlet_id;
        } else {
            $selectedOutletId = $request->has('outlet_id') 
                ? (int)$request->input('outlet_id') 
                : ($outlets->first() ? (int)$outlets->first()->id : null);
        }

        // Fetch active products with recipes and modifiers
        $products = Product::where('is_active', true)
            ->with(['recipes', 'modifiers' => function ($query) {
                $query->where('is_active', true);
            }])
            ->get()
            ->map(function ($product) {
                return [
                    'id' => $product->id,
                    'name' => $product->name,
                    'price' => (float)$product->price,
                    'category' => $product->category,
                    'description' => $product->description,
                    'image_path' => $product->image_path,
                    'has_recipe' => $product->hasRecipe(),
                    'recipes' => $product->recipes->map(function ($recipe) {
                        return [
                            'ingredient_id' => $recipe->ingredient_id,
                            'usage_qty' => (float)$recipe->usage_qty,
                            'variant' => $recipe->variant,
                        ];
                    }),
                    'modifiers' => $product->modifiers->map(function ($m) {
                        return [
                            'id' => $m->id,
                            'name' => $m->name,
                            'price' => (float)$m->price,
                            'ingredient_id' => $m->ingredient_id,
                            'usage_qty' => (float)$m->usage_qty,
                        ];
                    }),
                ];
            });

        // Fetch current stock levels for the selected outlet
        $ingredientStock = [];
        $productStock = [];

        if ($selectedOutletId) {
            $ingredientStock = OutletIngredient::where('outlet_id', $selectedOutletId)
                ->pluck('stock_qty', 'ingredient_id')
                ->map(fn($val) => (float)$val)
                ->toArray();

            $productStock = OutletProduct::where('outlet_id', $selectedOutletId)
                ->pluck('stock_qty', 'product_id')
                ->map(fn($val) => (float)$val)
                ->toArray();
        }

        // Fetch calculations tax & service charges percentages (respecting enabled flags)
        $taxEnabled = $this->settingService->get('tax_enabled', '1') === '1';
        $taxPercentage = $taxEnabled ? (float)$this->settingService->get('tax_percentage', '0') : 0.0;

        $serviceChargeEnabled = $this->settingService->get('service_charge_enabled', '1') === '1';
        $servicePercentage = $serviceChargeEnabled ? (float)$this->settingService->get('service_charge_percentage', '0') : 0.0;

        // NEW: Fetch tables for this outlet
        $tables = Table::where('outlet_id', $selectedOutletId)
            ->whereNull('merged_to_id')
            ->orderBy('number')
            ->get();

        // NEW: Fetch customers (members list)
        $customers = Customer::orderBy('name')->get();

        // NEW: Fetch modifiers
        $modifiers = Modifier::where('is_active', true)->get();

        // NEW: Fetch active promos
        $promos = Promo::where('is_active', true)->get();

        // Fetch waiters (users in outlet)
        $waiters = \App\Models\User::select('id', 'name')
            ->when($selectedOutletId, fn($q) => $q->where('outlet_id', $selectedOutletId))
            ->get();

        // Fetch active couriers for this outlet
        $couriers = \App\Models\Courier::where('is_active', true)
            ->when($selectedOutletId, fn($q) => $q->where('outlet_id', $selectedOutletId))
            ->get();

        // NEW: Fetch recent transactions for this outlet (last 20)
        $recentTransactions = [];
        if ($selectedOutletId) {
            $recentTransactions = Transaction::where('outlet_id', $selectedOutletId)
                ->with(['outlet', 'user', 'table', 'customer', 'items.product', 'items.modifiers.modifier'])
                ->latest()
                ->take(20)
                ->get()
                ->map(fn($t) => [
                    'id' => $t->id,
                    'transaction_number' => $t->transaction_number,
                    'total_amount' => (float)$t->total_amount,
                    'tax_amount' => (float)$t->tax_amount,
                    'service_charge_amount' => (float)$t->service_charge_amount,
                    'payment_method' => $t->payment_method,
                    'status' => $t->status,
                    'order_type' => $t->order_type,
                    'queue_number' => $t->queue_number,
                    'discount_amount' => (float)$t->discount_amount,
                    'points_redeemed' => (int)$t->points_redeemed,
                    'table' => $t->table ? [
                        'id' => $t->table->id,
                        'number' => $t->table->number,
                    ] : null,
                    'customer' => $t->customer ? [
                        'id' => $t->customer->id,
                        'name' => $t->customer->name,
                    ] : null,
                    'created_at' => $t->created_at->format('d M Y H:i'),
                    'items' => $t->items->map(fn($item) => [
                        'id' => $item->id,
                        'qty' => (int)$item->qty,
                        'variant' => $item->variant,
                        'price' => (float)$item->price,
                        'subtotal' => (float)$item->subtotal,
                        'product' => $item->product ? [
                            'id' => $item->product->id,
                            'name' => $item->product->name,
                        ] : null,
                        'modifiers' => $item->modifiers->map(fn($m) => [
                            'name' => $m->modifier ? $m->modifier->name : 'Addon',
                            'price' => (float)$m->price,
                        ]),
                    ]),
                ]);
        }

        // NEW: Fetch active pending QR transactions for the selected outlet
        $pendingQrQuery = Transaction::where('status', 'pending');
        if ($selectedOutletId) {
            $pendingQrQuery->where('outlet_id', $selectedOutletId);
        }

        $pendingQrOrders = $pendingQrQuery
            ->with(['table', 'customer', 'items.product', 'items.modifiers.modifier'])
            ->latest()
            ->get()
            ->map(fn($t) => [
                'id' => $t->id,
                'transaction_number' => $t->transaction_number,
                'table_number' => $t->table?->number ?? '-',
                'customer_name' => $t->customer?->name ?? 'Pelanggan',
                'total_amount' => (float)$t->total_amount,
                'payment_method' => $t->payment_method,
                'created_at' => $t->created_at->format('H:i'),
                'items_count' => $t->items->sum('qty'),
                'items' => $t->items->map(fn($item) => [
                    'name' => $item->product?->name ?? 'Menu',
                    'qty' => (int)$item->qty,
                    'variant' => $item->variant,
                    'price' => (float)$item->price,
                    'subtotal' => (float)$item->subtotal,
                    'modifiers' => $item->modifiers->map(fn($m) => $m->modifier?->name ?? 'Addon'),
                ]),
            ]);

        return Inertia::render('CafePOS/POS', [
            'products' => $products,
            'outlets' => $outlets,
            'selectedOutletId' => $selectedOutletId,
            'ingredientStock' => (object)$ingredientStock,
            'productStock' => (object)$productStock,
            'taxPercentage' => $taxPercentage,
            'servicePercentage' => $servicePercentage,
            'tables' => $tables,
            'customers' => $customers,
            'modifiers' => $modifiers,
            'promos' => $promos,
            'recentTransactions' => $recentTransactions,
            'pendingQrOrders' => $pendingQrOrders,
            'waiters' => $waiters,
            'couriers' => $couriers,
        ]);
    }

    /**
     * Process checkout transaction.
     */
    public function checkout(Request $request)
    {
        $request->validate([
            'outlet_id' => ['required', 'integer', 'exists:outlets,id'],
            'payment_method' => ['required', 'string', 'in:cash,transfer,qris'],
            'order_type' => ['required', 'string', 'in:dine_in,take_away,delivery'],
            'table_id' => ['nullable', 'integer', 'exists:tables,id'],
            'customer_id' => ['nullable', 'integer', 'exists:customers,id'],
            'discount_amount' => ['nullable', 'numeric', 'min:0'],
            'points_redeemed' => ['nullable', 'integer', 'min:0'],
            'waiter_id' => ['nullable', 'integer', 'exists:users,id'],
            'courier_id' => ['nullable', 'integer', 'exists:couriers,id'],
            'voucher_code' => ['nullable', 'string'],
            'items' => ['required', 'array', 'min:1'],
            'items.*.product_id' => ['required', 'integer', 'exists:products,id'],
            'items.*.qty' => ['required', 'integer', 'min:1'],
            'items.*.variant' => ['nullable', 'string', 'in:hot,ice'],
            'items.*.price' => ['required', 'numeric', 'min:0'],
            'items.*.modifiers' => ['nullable', 'array'],
            'items.*.modifiers.*' => ['integer', 'exists:modifiers,id'],
        ]);

        $user = $request->user();
        $outletId = (int)$request->outlet_id;

        // Force cashier to only checkout at their assigned outlet
        if ($user->outlet_id && (int)$user->outlet_id !== $outletId) {
            return redirect()->back()->with('error', 'Unauthorized outlet access.');
        }

        try {
            $transactionData = DB::transaction(function () use ($request, $outletId, $user) {
                $subtotal = 0;
                $itemsToProcess = [];

                // 1. Check Table availability if Dine In
                if ($request->order_type === 'dine_in' && $request->table_id) {
                    $table = Table::findOrFail($request->table_id);
                    if ($table->status === 'occupied' && !$table->merged_to_id) {
                        // Allow if it's already occupied (e.g. adding items to the same table),
                        // but generally check or set to occupied.
                    }
                }

                // 2. Loyalty Point redemption validation
                $customer = null;
                if ($request->customer_id) {
                    $customer = Customer::findOrFail($request->customer_id);
                    if ($request->points_redeemed && $request->points_redeemed > 0) {
                        if ($customer->loyalty_points < $request->points_redeemed) {
                            throw new \Exception("Customer does not have enough points.");
                        }
                    }
                }

                // 3. Validate stock levels atomically (including modifiers)
                foreach ($request->items as $item) {
                    $product = Product::findOrFail($item['product_id']);
                    $qty = (int)$item['qty'];
                    $variant = $item['variant'] ?? null;

                    // Verify base product stock
                    if (!$product->checkStock($outletId, $qty, $variant)) {
                        throw new \Exception("Stock for '{$product->name}' is insufficient.");
                    }

                    // Verify modifiers stock
                    $itemModifiers = [];
                    $modifiersPriceSum = 0;
                    if (isset($item['modifiers']) && is_array($item['modifiers'])) {
                        foreach ($item['modifiers'] as $modId) {
                            $mod = Modifier::findOrFail($modId);
                            $modifiersPriceSum += (float)$mod->price;
                            $itemModifiers[] = $mod;

                            if ($mod->ingredient_id && $mod->usage_qty) {
                                $outletIng = OutletIngredient::where('outlet_id', $outletId)
                                    ->where('ingredient_id', $mod->ingredient_id)
                                    ->first();
                                $needed = $mod->usage_qty * $qty;
                                if (!$outletIng || $outletIng->stock_qty < $needed) {
                                    throw new \Exception("Stock for modifier '{$mod->name}' ingredients is insufficient.");
                                }
                            }
                        }
                    }

                    $itemPrice = (float)$item['price'] + $modifiersPriceSum;
                    $itemSubtotal = $itemPrice * $qty;
                    $subtotal += $itemSubtotal;

                    $itemsToProcess[] = [
                        'product' => $product,
                        'qty' => $qty,
                        'variant' => $variant,
                        'price' => $itemPrice,
                        'subtotal' => $itemSubtotal,
                        'modifiers' => $itemModifiers,
                    ];
                }

                // Calculate taxes & fees (respecting enabled flags)
                $taxEnabled = $this->settingService->get('tax_enabled', '1') === '1';
                $serviceChargeEnabled = $this->settingService->get('service_charge_enabled', '1') === '1';

                $taxPercentage = $taxEnabled ? (float)$this->settingService->get('tax_percentage', '0') : 0.0;
                $servicePercentage = $serviceChargeEnabled ? (float)$this->settingService->get('service_charge_percentage', '0') : 0.0;

                $serviceChargeAmount = ($subtotal * $servicePercentage) / 100;
                $taxAmount = (($subtotal + $serviceChargeAmount) * $taxPercentage) / 100;

                // Validate and calculate voucher discount
                $voucher = null;
                $voucherDiscount = 0.0;
                if ($request->voucher_code) {
                    $voucher = \App\Models\Voucher::where('code', $request->voucher_code)->first();
                    if (!$voucher) {
                        throw new \Exception("Voucher code '{$request->voucher_code}' not found.");
                    }
                    if (!$voucher->isValidForPurchase($subtotal)) {
                        throw new \Exception("Voucher is invalid, expired, or min purchase not met.");
                    }
                    $voucherDiscount = (float)$voucher->calculateDiscount($subtotal);
                }

                // Check birthday rewards
                $birthdayDiscount = 0.0;
                if ($customer && $customer->birth_date) {
                    $birthDate = \Illuminate\Support\Carbon::parse($customer->birth_date);
                    if ($birthDate->format('m-d') === \Illuminate\Support\Carbon::today()->format('m-d')) {
                        // 10% birthday discount on subtotal
                        $birthdayDiscount = $subtotal * 0.10;
                    }
                }
                
                // Subtract discount and points redemption
                $discountAmount = (float)($request->discount_amount ?? 0);
                
                // 1 loyalty point redeemed = Rp 1.000 discount
                $pointsDiscount = (float)($request->points_redeemed ?? 0) * 1000.00;
                $discountAmount += $pointsDiscount + $voucherDiscount + $birthdayDiscount;

                $totalAmount = max(0, $subtotal + $serviceChargeAmount + $taxAmount - $discountAmount);

                // Generate Queue Number
                $todayDate = Carbon::today()->toDateString();
                $todayCount = Transaction::whereDate('created_at', $todayDate)->count();
                $queueNo = 'Q-' . str_pad((string)($todayCount + 1), 3, '0', STR_PAD_LEFT);

                // Create Transaction
                $transaction = Transaction::create([
                    'transaction_number' => "TXN-{$outletId}-" . date('Ymd-His') . '-' . rand(1000, 9999),
                    'outlet_id' => $outletId,
                    'user_id' => $user->id,
                    'total_amount' => $totalAmount,
                    'tax_amount' => $taxAmount,
                    'service_charge_amount' => $serviceChargeAmount,
                    'payment_method' => $request->payment_method,
                    'status' => 'success',
                    'order_type' => $request->order_type,
                    'table_id' => $request->table_id,
                    'customer_id' => $request->customer_id,
                    'queue_number' => $queueNo,
                    'discount_amount' => $discountAmount,
                    'points_redeemed' => $request->points_redeemed ?? 0,
                    'waiter_id' => $request->waiter_id,
                    'courier_id' => $request->courier_id,
                    'delivery_status' => $request->order_type === 'delivery' ? 'pending' : null,
                ]);

                // Create items & deduct stock
                foreach ($itemsToProcess as $proc) {
                    $product = $proc['product'];
                    $qty = $proc['qty'];
                    $variant = $proc['variant'];

                    $txnItem = TransactionItem::create([
                        'transaction_id' => $transaction->id,
                        'product_id' => $product->id,
                        'qty' => $qty,
                        'variant' => $variant,
                        'price' => $proc['price'],
                        'subtotal' => $proc['subtotal'],
                        'kitchen_status' => 'pending',
                    ]);

                    // Save selected modifiers
                    foreach ($proc['modifiers'] as $mod) {
                        TransactionItemModifier::create([
                            'transaction_item_id' => $txnItem->id,
                            'modifier_id' => $mod->id,
                            'price' => $mod->price,
                        ]);

                        // Deduct stock for modifiers
                        if ($mod->ingredient_id && $mod->usage_qty) {
                            $outletIng = OutletIngredient::where('outlet_id', $outletId)
                                ->where('ingredient_id', $mod->ingredient_id)
                                ->first();
                            if ($outletIng) {
                                $outletIng->decrement('stock_qty', $mod->usage_qty * $qty);
                            }
                        }
                    }

                    // Deduct recipe ingredients
                    if ($product->hasRecipe()) {
                        $recipeItems = $product->recipes()
                            ->where(function ($query) use ($variant) {
                                $query->whereNull('variant')
                                      ->orWhere('variant', $variant);
                            })
                            ->get();

                        foreach ($recipeItems as $recipe) {
                            $outletIngredient = OutletIngredient::where('outlet_id', $outletId)
                                ->where('ingredient_id', $recipe->ingredient_id)
                                ->first();

                            if ($outletIngredient) {
                                $deduction = $recipe->usage_qty * $qty;
                                $outletIngredient->decrement('stock_qty', $deduction);
                            }
                        }
                    } else {
                        // Deduct manual product stock
                        $outletProduct = OutletProduct::where('outlet_id', $outletId)
                            ->where('product_id', $product->id)
                            ->first();

                        if ($outletProduct) {
                            $outletProduct->decrement('stock_qty', $qty);
                        }
                    }
                }

                // If Dine In, mark table status as occupied
                if ($request->order_type === 'dine_in' && $request->table_id) {
                    Table::where('id', $request->table_id)->update(['status' => 'occupied']);
                }

                // Deduct loyalty points and reward points for members
                if ($customer) {
                    $ptsRedeemed = (int)($request->points_redeemed ?? 0);
                    if ($ptsRedeemed > 0) {
                        $customer->decrement('loyalty_points', $ptsRedeemed);
                    }

                    // Earn 1 point per Rp 10.000 spent
                    $ptsEarned = (int)floor($totalAmount / 10000);
                    if ($ptsEarned > 0) {
                        $customer->increment('loyalty_points', $ptsEarned);
                    }

                    // Extra loyalty points for birthday reward!
                    if ($birthdayDiscount > 0) {
                        $customer->increment('loyalty_points', 50);
                    }
                }

                // Increment voucher usage count
                if ($voucher) {
                    $voucher->increment('usage_count');
                }

                // Audit Log
                activity()
                    ->performedOn($transaction)
                    ->causedBy($user)
                    ->log("Completed transaction: {$transaction->transaction_number} (Rp " . number_format((float)$transaction->total_amount, 0, ',', '.') . ")");

                return [
                    'id' => $transaction->id,
                    'transaction_number' => $transaction->transaction_number,
                    'total_amount' => $transaction->total_amount,
                    'created_at' => $transaction->created_at->format('Y-m-d H:i:s'),
                ];
            });

            return redirect()->back()->with([
                'success' => 'Transaction completed successfully.',
                'last_transaction' => $transactionData
            ]);

        } catch (\Exception $e) {
            return redirect()->back()->with('error', $e->getMessage());
        }
    }

    /**
     * Display transaction histories.
     */
    public function transactions(Request $request): Response
    {
        $user = $request->user();
        $search = $request->input('search');
        $outletFilter = $request->input('outlet_id');
        $dateFilter = $request->input('date');

        // Cashier is restricted to their assigned outlet
        $outletId = $user->outlet_id ? (int)$user->outlet_id : ($outletFilter ? (int)$outletFilter : null);

        $transactions = Transaction::with(['outlet', 'user', 'table', 'customer', 'courier', 'items.product', 'items.modifiers.modifier'])
            ->when($outletId, function ($query, $outletId) {
                $query->where('outlet_id', $outletId);
            })
            ->when($search, function ($query, $search) {
                $query->where('transaction_number', 'like', "%{$search}%");
            })
            ->when($dateFilter, function ($query, $dateFilter) {
                $query->whereDate('created_at', $dateFilter);
            })
            ->latest()
            ->paginate(15)
            ->withQueryString();

        $outlets = Outlet::select('id', 'name')->get();

        return Inertia::render('CafePOS/Transactions/Index', [
            'transactions' => $transactions,
            'outlets' => $outlets,
            'isCashier' => !is_null($user->outlet_id),
            'filters' => [
                'search' => $search,
                'outlet_id' => $outletFilter,
                'date' => $dateFilter,
            ],
        ]);
    }

    public function updateDeliveryStatus(Request $request, Transaction $transaction): RedirectResponse
    {
        $request->validate([
            'delivery_status' => ['required', 'string', 'in:pending,preparing,delivering,delivered,failed'],
        ]);

        $transaction->update([
            'delivery_status' => $request->delivery_status,
        ]);

        return redirect()->back()->with('success', 'Status pengiriman berhasil diperbarui.');
    }

    /**
     * Confirm payment for a pending transaction (e.g., QR Self-Order paid at cashier).
     */
    public function confirmPayment(Request $request, Transaction $transaction): RedirectResponse
    {
        if ($transaction->status !== 'pending') {
            return redirect()->back()->with('error', 'Transaksi ini bukan berstatus pending.');
        }

        $paymentMethod = $request->input('payment_method', $transaction->payment_method);

        $transaction->update([
            'status' => 'success',
            'user_id' => $request->user()->id,
            'amount_paid' => $transaction->total_amount,
            'payment_method' => $paymentMethod,
        ]);

        activity()
            ->performedOn($transaction)
            ->causedBy($request->user())
            ->log("Konfirmasi pembayaran transaksi QR: {$transaction->transaction_number} (Status: Success / Lunas oleh Kasir {$request->user()->name})");

        return redirect()->back()->with('success', 'Pembayaran transaksi berhasil dikonfirmasi (Lunas) oleh kasir ' . $request->user()->name . '.');
    }

    /**
     * Void (cancel) a transaction. Restores inventory stock.
     */
    public function void(Request $request, Transaction $transaction): RedirectResponse
    {
        $user = $request->user();

        if ($user->outlet_id && (int)$user->outlet_id !== $transaction->outlet_id) {
            return redirect()->back()->with('error', 'Unauthorized access to outlet transactions.');
        }

        if ($transaction->status === 'cancelled') {
            return redirect()->back()->with('error', 'This transaction is already voided.');
        }

        try {
            DB::transaction(function () use ($transaction, $user) {
                $transaction->update(['status' => 'cancelled']);

                // Release Table
                if ($transaction->table_id) {
                    Table::where('id', $transaction->table_id)->update(['status' => 'available']);
                }

                // Revert Customer Points
                if ($transaction->customer_id) {
                    $customer = Customer::find($transaction->customer_id);
                    if ($customer) {
                        // Re-add redeemed points
                        if ($transaction->points_redeemed > 0) {
                            $customer->increment('loyalty_points', $transaction->points_redeemed);
                        }
                        // Re-deduct earned points
                        $ptsEarned = (int)floor($transaction->total_amount / 10000);
                        if ($ptsEarned > 0) {
                            $customer->decrement('loyalty_points', min($customer->loyalty_points, $ptsEarned));
                        }
                    }
                }

                // Restore stock levels
                foreach ($transaction->items as $item) {
                    $product = $item->product;
                    $qty = (int)$item->qty;
                    $variant = $item->variant;

                    // Revert modifiers stock
                    foreach ($item->modifiers as $itemMod) {
                        $mod = $itemMod->modifier;
                        if ($mod && $mod->ingredient_id && $mod->usage_qty) {
                            $outletIng = OutletIngredient::where('outlet_id', $transaction->outlet_id)
                                ->where('ingredient_id', $mod->ingredient_id)
                                ->first();
                            if ($outletIng) {
                                $outletIng->increment('stock_qty', $mod->usage_qty * $qty);
                            }
                        }
                    }

                    if ($product->hasRecipe()) {
                        $recipeItems = $product->recipes()
                            ->where(function ($query) use ($variant) {
                                $query->whereNull('variant')
                                      ->orWhere('variant', $variant);
                            })
                            ->get();

                        foreach ($recipeItems as $recipe) {
                            $outletIngredient = OutletIngredient::where('outlet_id', $transaction->outlet_id)
                                ->where('ingredient_id', $recipe->ingredient_id)
                                ->first();

                            if ($outletIngredient) {
                                $restoreAmount = $recipe->usage_qty * $qty;
                                $outletIngredient->increment('stock_qty', $restoreAmount);
                            }
                        }
                    } else {
                        // Restore direct stock
                        $outletProduct = OutletProduct::where('outlet_id', $transaction->outlet_id)
                            ->where('product_id', $product->id)
                            ->first();

                        if ($outletProduct) {
                            $outletProduct->increment('stock_qty', $qty);
                        }
                    }
                }

                activity()
                    ->performedOn($transaction)
                    ->causedBy($user)
                    ->log("Voided transaction: {$transaction->transaction_number}");
            });

            return redirect()->back()->with('success', 'Transaction voided and stock levels restored successfully.');

        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'Failed to void transaction: ' . $e->getMessage());
        }
    }

    /**
     * Delete (destroy) a transaction. Reverts inventory stock if the transaction is still active.
     */
    public function destroy(Request $request, Transaction $transaction): RedirectResponse
    {
        $user = $request->user();

        if ($user->outlet_id && (int)$user->outlet_id !== $transaction->outlet_id) {
            return redirect()->back()->with('error', 'Unauthorized access to outlet transactions.');
        }

        if ($user->hasRole('Kasir')) {
            return redirect()->back()->with('error', 'Unauthorized to delete transactions.');
        }

        try {
            DB::transaction(function () use ($transaction, $user) {
                if ($transaction->status !== 'cancelled') {
                    // Release Table
                    if ($transaction->table_id) {
                        Table::where('id', $transaction->table_id)->update(['status' => 'available']);
                    }

                    // Revert Customer Points
                    if ($transaction->customer_id) {
                        $customer = Customer::find($transaction->customer_id);
                        if ($customer) {
                            if ($transaction->points_redeemed > 0) {
                                $customer->increment('loyalty_points', $transaction->points_redeemed);
                            }
                            $ptsEarned = (int)floor($transaction->total_amount / 10000);
                            if ($ptsEarned > 0) {
                                $customer->decrement('loyalty_points', min($customer->loyalty_points, $ptsEarned));
                            }
                        }
                    }

                    // Restore stock
                    foreach ($transaction->items as $item) {
                        $product = $item->product;
                        $qty = (int)$item->qty;
                        $variant = $item->variant;

                        foreach ($item->modifiers as $itemMod) {
                            $mod = $itemMod->modifier;
                            if ($mod && $mod->ingredient_id && $mod->usage_qty) {
                                $outletIng = OutletIngredient::where('outlet_id', $transaction->outlet_id)
                                    ->where('ingredient_id', $mod->ingredient_id)
                                    ->first();
                                if ($outletIng) {
                                    $outletIng->increment('stock_qty', $mod->usage_qty * $qty);
                                }
                            }
                        }

                        if ($product) {
                            if ($product->hasRecipe()) {
                                $recipeItems = $product->recipes()
                                    ->where(function ($query) use ($variant) {
                                        $query->whereNull('variant')
                                              ->orWhere('variant', $variant);
                                    })
                                    ->get();

                                foreach ($recipeItems as $recipe) {
                                    $outletIngredient = OutletIngredient::where('outlet_id', $transaction->outlet_id)
                                        ->where('ingredient_id', $recipe->ingredient_id)
                                        ->first();

                                    if ($outletIngredient) {
                                        $restoreAmount = $recipe->usage_qty * $qty;
                                        $outletIngredient->increment('stock_qty', $restoreAmount);
                                    }
                                }
                            } else {
                                $outletProduct = OutletProduct::where('outlet_id', $transaction->outlet_id)
                                    ->where('product_id', $product->id)
                                    ->first();

                                if ($outletProduct) {
                                    $outletProduct->increment('stock_qty', $qty);
                                }
                            }
                        }
                    }
                }

                activity()
                    ->performedOn($transaction)
                    ->causedBy($user)
                    ->log("Deleted transaction: {$transaction->transaction_number}");

                $transaction->items()->delete();
                $transaction->delete();
            });

            return redirect()->back()->with('success', 'Transaction deleted and stock levels reverted successfully.');

        } catch (\Exception $e) {
            return redirect()->back()->with('error', 'Failed to delete transaction: ' . $e->getMessage());
        }
    }

    /**
     * Print POS receipt layout for a transaction.
     */
    public function printReceipt(Transaction $transaction): Response
    {
        $transaction->load(['outlet', 'user', 'table', 'customer', 'items.product', 'items.modifiers.modifier']);
        $appName = $this->settingService->get('app_name', config('app.name'));

        return Inertia::render('CafePOS/Transactions/Print', [
            'transaction' => [
                'id' => $transaction->id,
                'transaction_number' => $transaction->transaction_number,
                'total_amount' => (float)$transaction->total_amount,
                'tax_amount' => (float)$transaction->tax_amount,
                'service_charge_amount' => (float)$transaction->service_charge_amount,
                'payment_method' => $transaction->payment_method,
                'status' => $transaction->status,
                'order_type' => $transaction->order_type,
                'queue_number' => $transaction->queue_number,
                'discount_amount' => (float)$transaction->discount_amount,
                'points_redeemed' => (int)$transaction->points_redeemed,
                'table' => $transaction->table ? [
                    'id' => $transaction->table->id,
                    'number' => $transaction->table->number,
                ] : null,
                'customer' => $transaction->customer ? [
                    'id' => $transaction->customer->id,
                    'name' => $transaction->customer->name,
                ] : null,
                'created_at' => $transaction->created_at->format('d M Y H:i:s'),
                'outlet' => $transaction->outlet ? [
                    'id' => $transaction->outlet->id,
                    'name' => $transaction->outlet->name,
                ] : null,
                'user' => $transaction->user ? [
                    'id' => $transaction->user->id,
                    'name' => $transaction->user->name,
                ] : null,
                'items' => $transaction->items->map(fn($item) => [
                    'id' => $item->id,
                    'qty' => (int)$item->qty,
                    'variant' => $item->variant,
                    'price' => (float)$item->price,
                    'subtotal' => (float)$item->subtotal,
                    'product' => $item->product ? [
                        'id' => $item->product->id,
                        'name' => $item->product->name,
                    ] : null,
                    'modifiers' => $item->modifiers->map(fn($m) => [
                        'name' => $m->modifier ? $m->modifier->name : 'Addon',
                        'price' => (float)$m->price,
                    ]),
                ]),
            ],
            'appName' => $appName,
            'thermalLogo' => $this->settingService->get('thermal_logo', '/images/logo-thermal.png'),
        ]);
    }

    /**
     * Print Kitchen Ticket layout for a transaction.
     */
    public function printKitchenTicket(Transaction $transaction): Response
    {
        $transaction->load(['outlet', 'table', 'items.product', 'items.modifiers.modifier']);
        $appName = $this->settingService->get('app_name', config('app.name'));

        return Inertia::render('CafePOS/Transactions/KitchenPrint', [
            'transaction' => [
                'id' => $transaction->id,
                'transaction_number' => $transaction->transaction_number,
                'order_type' => $transaction->order_type,
                'queue_number' => $transaction->queue_number,
                'table' => $transaction->table ? [
                    'id' => $transaction->table->id,
                    'number' => $transaction->table->number,
                ] : null,
                'created_at' => $transaction->created_at->format('d M Y H:i:s'),
                'outlet' => $transaction->outlet ? [
                    'id' => $transaction->outlet->id,
                    'name' => $transaction->outlet->name,
                ] : null,
                'items' => $transaction->items->map(fn($item) => [
                    'id' => $item->id,
                    'qty' => (int)$item->qty,
                    'variant' => $item->variant,
                    'product' => $item->product ? [
                        'id' => $item->product->id,
                        'name' => $item->product->name,
                    ] : null,
                    'modifiers' => $item->modifiers->map(fn($m) => [
                        'name' => $m->modifier ? $m->modifier->name : 'Addon',
                    ]),
                ]),
            ],
            'appName' => $appName,
        ]);
    }

    public function qrMenu(Outlet $outlet, Table $table): Response
    {
        // Check if table belongs to this outlet
        if ((int)$table->outlet_id !== (int)$outlet->id) {
            abort(404, 'Meja tidak ditemukan di outlet ini.');
        }

        // Fetch active products with recipes and modifiers
        $products = Product::where('is_active', true)
            ->with(['recipes', 'modifiers' => function ($query) {
                $query->where('is_active', true);
            }])
            ->get()
            ->map(function ($product) {
                return [
                    'id' => $product->id,
                    'name' => $product->name,
                    'price' => (float)$product->price,
                    'category' => $product->category,
                    'description' => $product->description,
                    'image_path' => $product->image_path,
                    'has_recipe' => $product->hasRecipe(),
                    'recipes' => $product->recipes->map(function ($recipe) {
                        return [
                            'ingredient_id' => $recipe->ingredient_id,
                            'usage_qty' => (float)$recipe->usage_qty,
                            'variant' => $recipe->variant,
                        ];
                    }),
                    'modifiers' => $product->modifiers->map(function ($m) {
                        return [
                            'id' => $m->id,
                            'name' => $m->name,
                            'price' => (float)$m->price,
                        ];
                    }),
                ];
            });

        // Fetch current stock levels for the selected outlet
        $ingredientStock = OutletIngredient::where('outlet_id', $outlet->id)
            ->pluck('stock_qty', 'ingredient_id')
            ->map(fn($val) => (float)$val)
            ->toArray();

        $productStock = OutletProduct::where('outlet_id', $outlet->id)
            ->pluck('stock_qty', 'product_id')
            ->map(fn($val) => (float)$val)
            ->toArray();

        // Modifiers list
        $modifiers = Modifier::where('is_active', true)->get()->map(fn($m) => [
            'id' => $m->id,
            'name' => $m->name,
            'price' => (float)$m->price,
            'ingredient_id' => $m->ingredient_id,
            'usage_qty' => (float)$m->usage_qty,
        ]);

        // Fetch active/recent orders placed at this table in last 24 hours
        $tableOrders = Transaction::with(['items.product', 'items.modifiers.modifier', 'customer'])
            ->where('outlet_id', $outlet->id)
            ->where('table_id', $table->id)
            ->where('created_at', '>=', \Illuminate\Support\Carbon::now()->subHours(24))
            ->latest()
            ->get()
            ->map(function ($t) {
                return [
                    'id' => $t->id,
                    'transaction_number' => $t->transaction_number,
                    'customer_name' => $t->customer?->name ?? 'Pelanggan',
                    'status' => $t->status,
                    'payment_method' => $t->payment_method,
                    'total_amount' => (float)$t->total_amount,
                    'created_at' => $t->created_at->format('H:i'),
                    'items' => $t->items->map(function ($item) {
                        return [
                            'id' => $item->id,
                            'name' => $item->product?->name ?? 'Menu',
                            'qty' => (int)$item->qty,
                            'variant' => $item->variant,
                            'price' => (float)$item->price,
                            'subtotal' => (float)$item->subtotal,
                            'kitchen_status' => $item->kitchen_status,
                            'modifiers' => $item->modifiers->map(fn($m) => [
                                'name' => $m->modifier?->name ?? 'Addon',
                                'price' => (float)$m->price,
                            ]),
                        ];
                    }),
                ];
            });

        return Inertia::render('CafePOS/SelfOrder/Index', [
            'outlet' => $outlet,
            'table' => $table,
            'products' => $products,
            'ingredientStock' => $ingredientStock,
            'productStock' => $productStock,
            'modifiers' => $modifiers,
            'tableOrders' => $tableOrders,
            'taxPercentage' => ($this->settingService->get('tax_enabled', '1') === '1') ? (float)$this->settingService->get('tax_percentage', '0') : 0.0,
            'servicePercentage' => ($this->settingService->get('service_charge_enabled', '1') === '1') ? (float)$this->settingService->get('service_charge_percentage', '0') : 0.0,
        ]);
    }

    public function qrCheckout(Request $request, Outlet $outlet, Table $table)
    {
        if ((int)$table->outlet_id !== (int)$outlet->id) {
            return redirect()->back()->with('error', 'Outlet mismatch.');
        }

        $request->validate([
            'payment_method' => ['required', 'string', 'in:cashier,qris'],
            'customer_name' => ['required', 'string', 'max:255'],
            'items' => ['required', 'array', 'min:1'],
            'items.*.product_id' => ['required', 'integer', 'exists:products,id'],
            'items.*.qty' => ['required', 'integer', 'min:1'],
            'items.*.variant' => ['nullable', 'string', 'in:hot,ice'],
            'items.*.price' => ['required', 'numeric', 'min:0'],
            'items.*.modifiers' => ['nullable', 'array'],
            'items.*.modifiers.*' => ['integer', 'exists:modifiers,id'],
        ]);

        try {
            $transaction = DB::transaction(function () use ($request, $outlet, $table) {
                $subtotal = 0;
                $itemsToProcess = [];

                // 1. Mark table occupied
                $table->update(['status' => 'occupied']);

                // 2. Validate stock levels atomically
                foreach ($request->items as $item) {
                    $product = Product::findOrFail($item['product_id']);
                    $qty = (int)$item['qty'];
                    $variant = $item['variant'] ?? null;

                    // Verify base product stock
                    if (!$product->checkStock($outlet->id, $qty, $variant)) {
                        throw new \Exception("Stock for '{$product->name}' is insufficient.");
                    }

                    // Verify modifiers stock
                    $itemModifiers = [];
                    $modifiersPriceSum = 0;
                    if (isset($item['modifiers']) && is_array($item['modifiers'])) {
                        foreach ($item['modifiers'] as $modId) {
                            $mod = Modifier::findOrFail($modId);
                            $modifiersPriceSum += (float)$mod->price;
                            $itemModifiers[] = $mod;

                            if ($mod->ingredient_id && $mod->usage_qty) {
                                $outletIng = OutletIngredient::where('outlet_id', $outlet->id)
                                    ->where('ingredient_id', $mod->ingredient_id)
                                    ->first();
                                $needed = $mod->usage_qty * $qty;
                                if (!$outletIng || $outletIng->stock_qty < $needed) {
                                    throw new \Exception("Stock for modifier '{$mod->name}' ingredients is insufficient.");
                                }
                            }
                        }
                    }

                    $itemPrice = (float)$item['price'] + $modifiersPriceSum;
                    $itemSubtotal = $itemPrice * $qty;
                    $subtotal += $itemSubtotal;

                    $itemsToProcess[] = [
                        'product' => $product,
                        'qty' => $qty,
                        'variant' => $variant,
                        'price' => $itemPrice,
                        'subtotal' => $itemSubtotal,
                        'modifiers' => $itemModifiers,
                    ];
                }

                // Calculate taxes & fees (respecting enabled flags)
                $taxEnabled = $this->settingService->get('tax_enabled', '1') === '1';
                $serviceChargeEnabled = $this->settingService->get('service_charge_enabled', '1') === '1';

                $taxPercentage = $taxEnabled ? (float)$this->settingService->get('tax_percentage', '0') : 0.0;
                $servicePercentage = $serviceChargeEnabled ? (float)$this->settingService->get('service_charge_percentage', '0') : 0.0;

                $serviceChargeAmount = ($subtotal * $servicePercentage) / 100;
                $taxAmount = (($subtotal + $serviceChargeAmount) * $taxPercentage) / 100;

                // Final Total
                $totalAmount = $subtotal + $serviceChargeAmount + $taxAmount;

                // Create customer record on the fly
                $customer = Customer::firstOrCreate(
                    ['name' => $request->customer_name],
                    ['loyalty_points' => 0]
                );

                // Queue Number
                $todayCount = Transaction::whereDate('created_at', \Illuminate\Support\Carbon::today())->count();
                $queueNumber = $todayCount + 1;

                // Create transaction
                $transaction = Transaction::create([
                    'transaction_number' => 'TXN-QR-' . time() . '-' . rand(100, 999),
                    'outlet_id' => $outlet->id,
                    'user_id' => null,
                    'customer_id' => $customer->id,
                    'table_id' => $table->id,
                    'order_type' => 'dine_in',
                    'payment_method' => $request->payment_method === 'qris' ? 'qris' : 'cash',
                    'subtotal_amount' => $subtotal,
                    'discount_amount' => 0,
                    'points_redeemed' => 0,
                    'tax_amount' => $taxAmount,
                    'service_charge_amount' => $serviceChargeAmount,
                    'total_amount' => $totalAmount,
                    'amount_paid' => 0,
                    'status' => 'pending',
                    'queue_number' => $queueNumber,
                ]);

                // Create transaction items & deduct stock
                foreach ($itemsToProcess as $proc) {
                    $prod = $proc['product'];
                    $qty = $proc['qty'];
                    $variant = $proc['variant'];

                    $txnItem = $transaction->items()->create([
                        'product_id' => $prod->id,
                        'qty' => $qty,
                        'variant' => $variant,
                        'price' => $proc['price'],
                        'subtotal' => $proc['subtotal'],
                        'kitchen_status' => 'pending',
                    ]);

                    // Modifier items
                    foreach ($proc['modifiers'] as $mod) {
                        TransactionItemModifier::create([
                            'transaction_item_id' => $txnItem->id,
                            'modifier_id' => $mod->id,
                            'price' => (float)$mod->price,
                        ]);

                        // Deduct Modifier stock
                        if ($mod->ingredient_id && $mod->usage_qty) {
                            $outletIng = OutletIngredient::where('outlet_id', $outlet->id)
                                ->where('ingredient_id', $mod->ingredient_id)
                                ->first();
                            if ($outletIng) {
                                $outletIng->decrement('stock_qty', $mod->usage_qty * $qty);
                            }
                        }
                    }

                    // Deduct base product / recipe stock
                    if ($prod->hasRecipe()) {
                        $recipeItems = $prod->recipes()
                            ->where(function ($query) use ($variant) {
                                $query->whereNull('variant')
                                      ->orWhere('variant', $variant);
                            })
                            ->get();

                        if ($recipeItems->isEmpty()) {
                            $recipeItems = $prod->recipes()->whereNull('variant')->get();
                        }
                        if ($recipeItems->isEmpty()) {
                            $recipeItems = $prod->recipes()->get();
                        }

                        foreach ($recipeItems as $recipe) {
                            $outletIngredient = OutletIngredient::where('outlet_id', $outlet->id)
                                ->where('ingredient_id', $recipe->ingredient_id)
                                ->first();

                            if ($outletIngredient) {
                                $outletIngredient->decrement('stock_qty', $recipe->usage_qty * $qty);
                            }
                        }
                    } else {
                        $outletProduct = OutletProduct::where('outlet_id', $outlet->id)
                            ->where('product_id', $prod->id)
                            ->first();

                        if ($outletProduct) {
                            $outletProduct->decrement('stock_qty', $qty);
                        }
                    }
                }

                return $transaction;
            });

            return redirect()->route('pos.qr-menu', [
                'outlet' => $outlet->id,
                'table' => $table->id
            ])->with([
                'success' => 'Pesanan QR Anda berhasil dikirim! (Status: Pending - Silakan bayar / konfirmasi di kasir)',
                'latest_txn_number' => $transaction->transaction_number,
            ]);

        } catch (\Exception $e) {
            return redirect()->back()->with('error', $e->getMessage());
        }
    }
}
