<?php
// app/Controllers/PosController.php

class PosController {

    // ── Helper: restore cashier context from cookie ──
    private function restoreContext() {
        if (!isset($_SESSION['selected_cashier_id']) && isset($_COOKIE['selected_cashier_id']) && isset($_COOKIE['selected_cashier_name']) && isset($_SESSION['restaurant_id'])) {
            $cId = intval($_COOKIE['selected_cashier_id']);
            $db = getDB();
            $stmt = $db->prepare('SELECT id FROM users WHERE id = ? AND restaurant_id = ? AND role = "cashier" LIMIT 1');
            $stmt->execute([$cId, $_SESSION['restaurant_id']]);
            if ($stmt->fetch()) {
                $_SESSION['selected_cashier_id'] = $cId;
                $_SESSION['selected_cashier_name'] = $_COOKIE['selected_cashier_name'];
            } else {
                // Invalid or cross-restaurant cookie, clear it
                setcookie('selected_cashier_id', '', time() - 3600, '/');
                setcookie('selected_cashier_name', '', time() - 3600, '/');
            }
        }
    }

    // ── Helper: get restaurant settings (tax/discount) ──
    private function getSettings($db, $rest_id) {
        $stmt = $db->prepare('SELECT setting_key, setting_value FROM restaurant_settings WHERE restaurant_id = ?');
        $stmt->execute([$rest_id]);
        $rows = $stmt->fetchAll();
        $s = [];
        foreach ($rows as $r) { $s[$r['setting_key']] = $r['setting_value']; }
        return [
            'tax_rate'      => floatval($s['tax_rate'] ?? 5),
            'discount_rate' => floatval($s['discount_rate'] ?? 0),
        ];
    }

    // ── POS Entry Point ─────────────────────────────────
    public function index() {
        if (!isset($_SESSION['user_id'])) {
            header('Location: /login');
            exit;
        }

        FeatureGate::enforceSubscription($_SESSION['restaurant_id']);

        $db = getDB();
        $restaurant_id = $_SESSION['restaurant_id'];
        $user_id = $_SESSION['user_id'];
        $currency = FeatureGate::currency($restaurant_id);

        // Restaurant details
        $rStmt = $db->prepare('SELECT name, logo FROM restaurants WHERE id = ?');
        $rStmt->execute([$restaurant_id]);
        $restaurant = $rStmt->fetch();
        $restaurant_name = $restaurant['name'] ?? 'Restaurant';
        $restaurant_logo = $restaurant['logo'] ?? '';

        // Tax & discount settings
        $settings = $this->getSettings($db, $restaurant_id);
        $tax_rate = $settings['tax_rate'];
        $discount_rate = $settings['discount_rate'];

        // Waiters list
        $role = $_SESSION['role'] ?? '';

        // Restore context from cookie if available
        $this->restoreContext();

        // Admin / Superadmin Cashier selection logic
        if ($role === 'admin' || $role === 'superadmin') {
            if (isset($_GET['cashier_id'])) {
                if ($_GET['cashier_id'] === 'change') {
                    unset($_SESSION['selected_cashier_id']);
                    unset($_SESSION['selected_cashier_name']);
                    setcookie('selected_cashier_id', '', time() - 3600, '/');
                    setcookie('selected_cashier_name', '', time() - 3600, '/');
                    header('Location: /pos');
                    exit;
                }

                $cId = intval($_GET['cashier_id']);
                $cStmt = $db->prepare('SELECT id, name FROM users WHERE id = ? AND restaurant_id = ? AND role = "cashier" AND status = "active" LIMIT 1');
                $cStmt->execute([$cId, $restaurant_id]);
                $selected_cashier = $cStmt->fetch();
                if ($selected_cashier) {
                    $_SESSION['selected_cashier_id'] = $selected_cashier['id'];
                    $_SESSION['selected_cashier_name'] = $selected_cashier['name'];
                    setcookie('selected_cashier_id', $selected_cashier['id'], time() + (86400 * 30), '/'); // 30 days
                    setcookie('selected_cashier_name', $selected_cashier['name'], time() + (86400 * 30), '/');
                }
                header('Location: /pos');
                exit;
            }

            if (!isset($_SESSION['selected_cashier_id'])) {
                // Fetch active cashiers and their active session count (open sessions)
                $cStmt = $db->prepare('
                    SELECT u.id, u.name, u.email,
                           (SELECT COUNT(*) FROM sessions s WHERE s.user_id = u.id AND s.status = "open" AND s.restaurant_id = ?) as has_active_session
                    FROM users u
                    WHERE u.restaurant_id = ? AND u.role = "cashier" AND u.status = "active"
                    ORDER BY u.name
                ');
                $cStmt->execute([$restaurant_id, $restaurant_id]);
                $cashiers = $cStmt->fetchAll();

                $target_page = 'pos';
                require_once 'app/Views/admin/cashier_select.php';
                exit;
            }
        }

        $effective_user_id = $user_id;
        $effective_role = $role;
        $cashier_name = $_SESSION['name'] ?? 'Cashier';

        if (($role === 'admin' || $role === 'superadmin') && isset($_SESSION['selected_cashier_id'])) {
            $effective_user_id = $_SESSION['selected_cashier_id'];
            $effective_role = 'cashier';
            $cashier_name = $_SESSION['selected_cashier_name'] ?? 'Cashier';
        }

        if ($effective_role === 'admin' || $effective_role === 'superadmin') {
            $wStmt = $db->prepare('SELECT id, name FROM users WHERE restaurant_id = ? AND role = "waiter" AND status = "active" ORDER BY name');
            $wStmt->execute([$restaurant_id]);
        } else {
            $wStmt = $db->prepare('SELECT id, name FROM users WHERE restaurant_id = ? AND role = "waiter" AND status = "active" AND cashier_id = ? ORDER BY name');
            $wStmt->execute([$restaurant_id, $effective_user_id]);
        }
        $waiters = $wStmt->fetchAll();
        $waiters = array_filter($waiters, function($w) use ($restaurant_id) {
            return FeatureGate::isUserAllowed($w['id'], $restaurant_id, 'waiter');
        });

        // Check if there is an active session
        $stmt = $db->prepare('SELECT * FROM sessions WHERE user_id = ? AND restaurant_id = ? AND status = "open" LIMIT 1');
        $stmt->execute([$effective_user_id, $restaurant_id]);
        $session = $stmt->fetch();

        if (!$session) {
            require_once 'app/Views/cashier/session.php';
        } else {
            require_once 'app/Views/cashier/pos.php';
        }
    }

    // ── Open Cashier Session ────────────────────────────
    public function openSession() {
        if (!isset($_SESSION['user_id'])) {
            header('Location: /login');
            exit;
        }

        $this->restoreContext();

        $role = $_SESSION['role'] ?? '';
        $user_id = $_SESSION['user_id'];
        if (($role === 'admin' || $role === 'superadmin') && isset($_SESSION['selected_cashier_id'])) {
            $user_id = $_SESSION['selected_cashier_id'];
        }

        $opening_balance = $_POST['opening_balance'] ?? 0;
        $db = getDB();
        
        $stmt = $db->prepare('INSERT INTO sessions (restaurant_id, user_id, opening_balance) VALUES (?, ?, ?)');
        $stmt->execute([$_SESSION['restaurant_id'], $user_id, $opening_balance]);

        header('Location: /pos');
        exit;
    }

    // ── Close Cashier Session ───────────────────────────
    public function closeSession() {
        if (!isset($_SESSION['user_id'])) {
            exit;
        }

        $this->restoreContext();

        $role = $_SESSION['role'] ?? '';
        $user_id = $_SESSION['user_id'];
        if (($role === 'admin' || $role === 'superadmin') && isset($_SESSION['selected_cashier_id'])) {
            $user_id = $_SESSION['selected_cashier_id'];
        }

        $db = getDB();

        // Check if there is an active session
        $sess = $db->prepare('SELECT id FROM sessions WHERE user_id = ? AND restaurant_id = ? AND status = "open" LIMIT 1');
        $sess->execute([$user_id, $_SESSION['restaurant_id']]);
        $session = $sess->fetch();

        if ($session) {
            $session_id = $session['id'];

            // Check for pending/unpaid orders (not voided)
            $unpaidStmt = $db->prepare('SELECT COUNT(*) FROM orders WHERE session_id = ? AND payment_status = "unpaid" AND status != "voided"');
            $unpaidStmt->execute([$session_id]);
            $unpaidCount = $unpaidStmt->fetchColumn();

            // Check for void orders
            $voidStmt = $db->prepare('SELECT COUNT(*) FROM orders WHERE session_id = ? AND status = "voided"');
            $voidStmt->execute([$session_id]);
            $voidCount = $voidStmt->fetchColumn();

            if ($unpaidCount > 0 || $voidCount > 0) {
                $reasons = [];
                if ($unpaidCount > 0) {
                    $reasons[] = "$unpaidCount pending/unpaid order(s)";
                }
                if ($voidCount > 0) {
                    $reasons[] = "$voidCount void order(s)";
                }
                $errMsg = "Cannot close session while you have " . implode(" or ", $reasons) . " pending.";
                header('Location: /pos?error=' . urlencode($errMsg));
                exit;
            }
        }

        $stmt = $db->prepare('UPDATE sessions SET status = "closed", end_time = NOW() WHERE user_id = ? AND status = "open"');
        $stmt->execute([$user_id]);

        if ($role === 'admin' || $role === 'superadmin') {
            unset($_SESSION['selected_cashier_id']);
            unset($_SESSION['selected_cashier_name']);
            setcookie('selected_cashier_id', '', time() - 3600, '/');
            setcookie('selected_cashier_name', '', time() - 3600, '/');
            header('Location: /pos');
        } else {
            header('Location: /dashboard');
        }
        exit;
    }

    // ── AJAX: Get POS Data ──────────────────────────────
    public function getPosData() {
        header('Content-Type: application/json');
        if (!isset($_SESSION['user_id'])) {
            echo json_encode(['error' => 'Unauthorized']);
            exit;
        }

        $this->restoreContext();

        $db = getDB();
        $rest_id = $_SESSION['restaurant_id'];
        $user_id = $_SESSION['user_id'];
        $role = $_SESSION['role'] ?? '';
        $effective_role = $role;

        if (($role === 'admin' || $role === 'superadmin') && isset($_SESSION['selected_cashier_id'])) {
            $user_id = $_SESSION['selected_cashier_id'];
            $effective_role = 'cashier';
        }

        $currency = FeatureGate::currency($rest_id);
        $settings = $this->getSettings($db, $rest_id);

        // Restaurant details
        $rStmt = $db->prepare('SELECT name, email, phone, address, logo FROM restaurants WHERE id = ?');
        $rStmt->execute([$rest_id]);
        $restaurant = $rStmt->fetch();

        $categories = $db->prepare('SELECT * FROM categories WHERE restaurant_id = ? AND status="active"');
        $categories->execute([$rest_id]);

        $products = $db->prepare('SELECT * FROM products WHERE restaurant_id = ? AND status="active"');
        $products->execute([$rest_id]);

        if ($effective_role === 'admin' || $effective_role === 'superadmin') {
            $floors = $db->prepare('SELECT * FROM floors WHERE restaurant_id = ?');
            $floors->execute([$rest_id]);

            $tables = $db->prepare('SELECT * FROM tables WHERE restaurant_id = ?');
            $tables->execute([$rest_id]);

            // Waiters
            $wStmt = $db->prepare('SELECT id, name FROM users WHERE restaurant_id = ? AND role = "waiter" AND status = "active" ORDER BY name');
            $wStmt->execute([$rest_id]);

            // Active orders (unpaid, not voided)
            $active_orders = $db->prepare('
                SELECT o.*, t.name as table_name, f.name as floor_name, w.name as waiter_name
                FROM orders o 
                LEFT JOIN tables t ON t.id = o.table_id 
                LEFT JOIN floors f ON f.id = t.floor_id
                LEFT JOIN users w ON w.id = o.waiter_id
                WHERE o.restaurant_id = ? AND o.payment_status = "unpaid" AND o.status != "voided"
            ');
            $active_orders->execute([$rest_id]);
        } else {
            $floors = $db->prepare('SELECT * FROM floors WHERE restaurant_id = ? AND cashier_id = ?');
            $floors->execute([$rest_id, $user_id]);

            $tables = $db->prepare('SELECT t.* FROM tables t JOIN floors f ON t.floor_id = f.id WHERE t.restaurant_id = ? AND f.cashier_id = ?');
            $tables->execute([$rest_id, $user_id]);

            // Waiters
            $wStmt = $db->prepare('SELECT id, name FROM users WHERE restaurant_id = ? AND role = "waiter" AND status = "active" AND cashier_id = ? ORDER BY name');
            $wStmt->execute([$rest_id, $user_id]);

            // Active orders (unpaid, not voided) - filter by floor cashier, order cashier or waiter cashier
            $active_orders = $db->prepare('
                SELECT o.*, t.name as table_name, f.name as floor_name, w.name as waiter_name
                FROM orders o 
                LEFT JOIN tables t ON t.id = o.table_id 
                LEFT JOIN floors f ON f.id = t.floor_id
                LEFT JOIN users w ON w.id = o.waiter_id
                WHERE o.restaurant_id = ? 
                  AND o.payment_status = "unpaid" 
                  AND o.status != "voided"
                  AND (
                      f.cashier_id = ? 
                      OR o.cashier_id = ? 
                      OR w.cashier_id = ?
                  )
            ');
            $active_orders->execute([$rest_id, $user_id, $user_id, $user_id]);
        }
        $orders_res = $active_orders->fetchAll();

        // Get items for these orders (include printed_qty)
        foreach($orders_res as &$order) {
            $itemsStmt = $db->prepare('SELECT oi.*, p.name, p.image FROM order_items oi JOIN products p ON p.id = oi.product_id WHERE oi.order_id = ?');
            $itemsStmt->execute([$order['id']]);
            $order['items'] = $itemsStmt->fetchAll();
        }

        // Fetch active coupons
        $couponsStmt = $db->prepare('SELECT * FROM restaurant_coupons WHERE restaurant_id = ? AND status = "active" AND expiry_date >= DATE(NOW())');
        $couponsStmt->execute([$rest_id]);
        $couponsList = $couponsStmt->fetchAll();

        $floors_res = $floors->fetchAll();
        $tables_res = $tables->fetchAll();
        $waiters_res = $wStmt->fetchAll();

        // Filter by subscription limits
        $allowedFloorIds = FeatureGate::getAllowedFloorIds($rest_id);
        $allowedTableIds = FeatureGate::getAllowedTableIds($rest_id);

        $floors_res = array_filter($floors_res, fn($f) => in_array($f['id'], $allowedFloorIds));
        $tables_res = array_filter($tables_res, fn($t) => in_array($t['id'], $allowedTableIds));
        $waiters_res = array_filter($waiters_res, function($w) use ($rest_id) {
            return FeatureGate::isUserAllowed($w['id'], $rest_id, 'waiter');
        });

        echo json_encode([
            'categories'      => $categories->fetchAll(),
            'products'        => $products->fetchAll(),
            'floors'          => array_values($floors_res),
            'tables'          => array_values($tables_res),
            'waiters'         => array_values($waiters_res),
            'active_orders'   => $orders_res,
            'currency'        => $currency,
            'tax_rate'        => $settings['tax_rate'],
            'discount_rate'   => $settings['discount_rate'],
            'coupons'         => $couponsList,
            'restaurant_name' => $restaurant['name'] ?? 'Restaurant',
            'restaurant_logo' => $restaurant['logo'] ?? '',
            'restaurant_phone'=> $restaurant['phone'] ?? '',
            'restaurant_address'=> $restaurant['address'] ?? '',
        ]);
        exit;
    }

    // ── AJAX: Place New Order ───────────────────────────
    public function placeOrder() {
        header('Content-Type: application/json');
        $data = json_decode(file_get_contents('php://input'), true);

        if (!$data || !isset($_SESSION['user_id'])) {
            echo json_encode(['success' => false, 'message' => 'Invalid request']);
            exit;
        }

        $this->restoreContext();

        if (empty($data['waiter_id'])) {
            echo json_encode(['success' => false, 'message' => 'Waiter is required.']);
            exit;
        }

        $db = getDB();
        $rest_id = $_SESSION['restaurant_id'];
        $role = $_SESSION['role'] ?? '';
        $user_id = $_SESSION['user_id'];
        if (($role === 'admin' || $role === 'superadmin') && isset($_SESSION['selected_cashier_id'])) {
            $user_id = $_SESSION['selected_cashier_id'];
        }

        $db->beginTransaction();

        try {
            // Get active session
            $sess = $db->prepare('SELECT id FROM sessions WHERE user_id = ? AND status="open" LIMIT 1');
            $sess->execute([$user_id]);
            $session_id = $sess->fetchColumn();

            // Get settings for tax/discount
            $settings = $this->getSettings($db, $rest_id);

            // Calculate totals
            $subtotal = 0;
            foreach ($data['items'] as $item) {
                $subtotal += ($item['price'] * $item['qty']);
            }
            $coupon_discount = floatval($data['coupon_discount'] ?? 0);
            $base_discount = $subtotal * ($settings['discount_rate'] / 100);
            $discount_amount = $base_discount + $coupon_discount;
            $taxable = max(0, $subtotal - $discount_amount);
            $tax = $taxable * ($settings['tax_rate'] / 100);
            $total = $taxable + $tax;

            // Calculate next sequential order number for this restaurant
            $order_number = FeatureGate::getNextOrderNumber($rest_id, $session_id, $user_id);

            // Insert Order
            $stmt = $db->prepare('INSERT INTO orders (restaurant_id, order_number, session_id, table_id, cashier_id, waiter_id, order_type, subtotal, discount, tax, total, notes, status, coupon_code, coupon_discount) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, "accepted", ?, ?)');
            $stmt->execute([
                $rest_id,
                $order_number,
                $session_id,
                $data['table_id'] ?? null,
                $user_id,
                $data['waiter_id'] ?? null,
                $data['order_type'] ?? 'dining',
                $subtotal,
                $discount_amount,
                $tax,
                $total,
                $data['notes'] ?? '',
                $data['coupon_code'] ?? null,
                $coupon_discount
            ]);

            $order_id = $db->lastInsertId();

            // Insert Items (all printed_qty = quantity since KOT prints on place)
            $itemStmt = $db->prepare('INSERT INTO order_items (order_id, product_id, quantity, printed_qty, price, subtotal) VALUES (?, ?, ?, ?, ?, ?)');
            $kot_items = [];
            foreach ($data['items'] as $item) {
                $item_sub = $item['price'] * $item['qty'];
                $itemStmt->execute([$order_id, $item['id'], $item['qty'], $item['qty'], $item['price'], $item_sub]);
                $kot_items[] = [
                    'name' => $item['name'],
                    'qty' => $item['qty'],
                    'is_new' => true
                ];
            }

            // Update table status to occupied (for dining)
            if (!empty($data['table_id'])) {
                $tblStmt = $db->prepare('UPDATE tables SET status = "occupied" WHERE id = ?');
                $tblStmt->execute([$data['table_id']]);
            }

            $db->commit();
            echo json_encode([
                'success'   => true,
                'order_id'  => $order_id,
                'order_number' => $order_number,
                'kot_items' => $kot_items,
                'message'   => 'Order placed successfully!'
            ]);
        } catch (\Exception $e) {
            $db->rollBack();
            echo json_encode(['success' => false, 'message' => $e->getMessage()]);
        }
        exit;
    }

    // ── AJAX: Add Items to Existing Order ───────────────
    public function addItems() {
        header('Content-Type: application/json');
        $data = json_decode(file_get_contents('php://input'), true);

        if (!$data || !isset($_SESSION['user_id']) || !isset($data['order_id'])) {
            echo json_encode(['success' => false, 'message' => 'Invalid request']);
            exit;
        }

        $db = getDB();
        $rest_id = $_SESSION['restaurant_id'];
        $order_id = $data['order_id'];
        $db->beginTransaction();

        try {
            // Get existing order
            $ordStmt = $db->prepare('SELECT * FROM orders WHERE id = ? AND restaurant_id = ?');
            $ordStmt->execute([$order_id, $rest_id]);
            $order = $ordStmt->fetch();
            if (!$order) throw new \Exception('Order not found');

            // Get existing items
            $existStmt = $db->prepare('SELECT * FROM order_items WHERE order_id = ?');
            $existStmt->execute([$order_id]);
            $existingItems = $existStmt->fetchAll();
            $existingMap = [];
            foreach ($existingItems as $ei) {
                $existingMap[$ei['product_id']] = $ei;
            }

            $kot_items = [];
            $settings = $this->getSettings($db, $rest_id);

            foreach ($data['items'] as $item) {
                $pid = $item['id'];
                $newQty = $item['qty'];

                if (isset($existingMap[$pid])) {
                    // Existing item — only allow increase (never decrease below original)
                    $existing = $existingMap[$pid];
                    if ($newQty > $existing['quantity']) {
                        $delta = $newQty - $existing['printed_qty'];
                        $item_sub = $item['price'] * $newQty;
                        $db->prepare('UPDATE order_items SET quantity = ?, subtotal = ? WHERE id = ?')
                           ->execute([$newQty, $item_sub, $existing['id']]);
                        if ($delta > 0) {
                            $kot_items[] = [
                                'name' => $item['name'],
                                'qty' => $delta,
                                'is_new' => false,
                                'label' => '+' . ($newQty - $existing['quantity'])
                            ];
                        }
                    }
                    // If newQty <= existing, ignore (no decrease allowed)
                } else {
                    // New item — insert fresh
                    $item_sub = $item['price'] * $newQty;
                    $db->prepare('INSERT INTO order_items (order_id, product_id, quantity, printed_qty, price, subtotal) VALUES (?, ?, ?, 0, ?, ?)')
                       ->execute([$order_id, $pid, $newQty, $item['price'], $item_sub]);
                    $kot_items[] = [
                        'name' => $item['name'],
                        'qty' => $newQty,
                        'is_new' => true,
                        'label' => 'NEW'
                    ];
                }
            }

            // Recalculate order totals
            $totStmt = $db->prepare('SELECT SUM(subtotal) as subtotal FROM order_items WHERE order_id = ? AND status != "voided"');
            $totStmt->execute([$order_id]);
            $newSubtotal = floatval($totStmt->fetchColumn());

            // Recalculate coupon discount if order has coupon_code
            $coupon_discount = 0;
            if (!empty($order['coupon_code'])) {
                $cpn = $db->prepare('SELECT * FROM restaurant_coupons WHERE restaurant_id = ? AND code = ? LIMIT 1');
                $cpn->execute([$rest_id, $order['coupon_code']]);
                $coupon = $cpn->fetch();
                if ($coupon) {
                    // Fetch all items currently in the order (not voided)
                    $itemsStmt = $db->prepare('SELECT * FROM order_items WHERE order_id = ? AND status != "voided"');
                    $itemsStmt->execute([$order_id]);
                    $allItems = $itemsStmt->fetchAll();
                    
                    require_once 'app/Helpers/CouponHelper.php';
                    $coupon_discount = CouponHelper::calculateDiscount($coupon, $allItems, $newSubtotal);
                }
            }

            $base_discount = $newSubtotal * ($settings['discount_rate'] / 100);
            $discount_amount = $base_discount + $coupon_discount;
            $taxable = max(0, $newSubtotal - $discount_amount);
            $tax = $taxable * ($settings['tax_rate'] / 100);
            $total = $taxable + $tax;

            $db->prepare('UPDATE orders SET subtotal = ?, discount = ?, tax = ?, total = ?, coupon_discount = ? WHERE id = ?')
               ->execute([$newSubtotal, $discount_amount, $tax, $total, $coupon_discount, $order_id]);

            $db->commit();
            echo json_encode([
                'success'   => true,
                'order_id'  => $order_id,
                'order_number' => $order['order_number'],
                'kot_items' => $kot_items,
                'new_total' => $total,
                'message'   => 'Items added successfully!'
            ]);
        } catch (\Exception $e) {
            $db->rollBack();
            echo json_encode(['success' => false, 'message' => $e->getMessage()]);
        }
        exit;
    }

    // ── AJAX: Print KOT (mark items printed) ────────────
    public function printKot() {
        header('Content-Type: application/json');
        $data = json_decode(file_get_contents('php://input'), true);

        if (!$data || !isset($data['order_id'])) {
            echo json_encode(['success' => false, 'message' => 'Invalid request']);
            exit;
        }

        $db = getDB();
        $order_id = $data['order_id'];

        // Get items that have unprinted quantities (delta)
        $stmt = $db->prepare('SELECT oi.*, p.name FROM order_items oi JOIN products p ON p.id = oi.product_id WHERE oi.order_id = ? AND oi.quantity > oi.printed_qty');
        $stmt->execute([$order_id]);
        $deltaItems = $stmt->fetchAll();

        $kot_items = [];
        foreach ($deltaItems as $di) {
            $delta = $di['quantity'] - $di['printed_qty'];
            $kot_items[] = [
                'name' => $di['name'],
                'qty'  => $delta,
                'is_new' => ($di['printed_qty'] == 0),
                'label' => ($di['printed_qty'] == 0) ? 'NEW' : '+' . $delta
            ];
        }

        // Mark all as printed
        $db->prepare('UPDATE order_items SET printed_qty = quantity WHERE order_id = ?')
           ->execute([$order_id]);

        // Get table and floor name for printing
        $ordStmt = $db->prepare('SELECT t.name as table_name, f.name as floor_name FROM orders o LEFT JOIN tables t ON t.id = o.table_id LEFT JOIN floors f ON f.id = t.floor_id WHERE o.id = ?');
        $ordStmt->execute([$order_id]);
        $order = $ordStmt->fetch();

        echo json_encode([
            'success'   => true,
            'kot_items' => $kot_items,
            'table_name' => $order['table_name'] ?? '—',
            'floor_name' => $order['floor_name'] ?? '—',
            'message'   => 'KOT printed!'
        ]);
        exit;
    }

    // ── AJAX: Void Order ────────────────────────────────
    public function voidOrder() {
        header('Content-Type: application/json');
        $data = json_decode(file_get_contents('php://input'), true);

        if (!$data || !isset($data['order_id'])) {
            echo json_encode(['success' => false, 'message' => 'Invalid request']);
            exit;
        }

        $db = getDB();
        $db->beginTransaction();

        try {
            $order_id = $data['order_id'];

            // Get order
            $ordStmt = $db->prepare('SELECT table_id FROM orders WHERE id = ?');
            $ordStmt->execute([$order_id]);
            $order = $ordStmt->fetch();
            if (!$order) throw new \Exception('Order not found');

            // Void the order
            $db->prepare('UPDATE orders SET status = "voided" WHERE id = ?')
               ->execute([$order_id]);

            // Record in void_logs
            $db->prepare('INSERT INTO void_logs (restaurant_id, order_id, voided_by, reason) VALUES (?, ?, ?, ?)')
               ->execute([$_SESSION['restaurant_id'], $order_id, $_SESSION['user_id'], 'Voided from Cashier POS']);

            // Void all items
            $db->prepare('UPDATE order_items SET status = "voided" WHERE order_id = ?')
               ->execute([$order_id]);

            // Release table
            if ($order['table_id']) {
                $db->prepare('UPDATE tables SET status = "available" WHERE id = ?')
                   ->execute([$order['table_id']]);
            }

            $db->commit();
            echo json_encode(['success' => true, 'message' => 'Order voided successfully.']);
        } catch (\Exception $e) {
            $db->rollBack();
            echo json_encode(['success' => false, 'message' => $e->getMessage()]);
        }
        exit;
    }

    // ── AJAX: Process Payment ───────────────────────────
    public function processPayment() {
        header('Content-Type: application/json');
        $data = json_decode(file_get_contents('php://input'), true);

        if (!$data || !isset($_SESSION['user_id'])) {
            echo json_encode(['success' => false]);
            exit;
        }

        $db = getDB();
        $db->beginTransaction();

        try {
            $order_id = $data['order_id'];
            $method = $data['payment_method'];
            $given = $data['given_amount'];

            // Get order details
            $ordStmt = $db->prepare('SELECT o.*, t.name as table_name, f.name as floor_name, r.name as restaurant_name, r.phone as restaurant_phone, r.address as restaurant_address, r.logo as restaurant_logo, u.name as cashier_name, w.name as waiter_name FROM orders o LEFT JOIN tables t ON t.id = o.table_id LEFT JOIN floors f ON f.id = t.floor_id JOIN restaurants r ON r.id = o.restaurant_id LEFT JOIN users u ON u.id = o.cashier_id LEFT JOIN users w ON w.id = o.waiter_id WHERE o.id = ?');
            $ordStmt->execute([$order_id]);
            $order = $ordStmt->fetch();

            if (!$order) throw new \Exception('Order not found');

            $change = $given - $order['total'];
            if ($change < 0 && $method === 'cash') throw new \Exception('Insufficient amount given');

            // Record payment
            $payStmt = $db->prepare('INSERT INTO payments (order_id, restaurant_id, payment_method, amount, given_amount, change_amount) VALUES (?, ?, ?, ?, ?, ?)');
            $payStmt->execute([$order_id, $_SESSION['restaurant_id'], $method, $order['total'], $given, max(0, $change)]);

            // Update order status
            $updStmt = $db->prepare('UPDATE orders SET payment_status = "paid", status = "completed" WHERE id = ?');
            $updStmt->execute([$order_id]);

            // Mark all items as printed
            $db->prepare('UPDATE order_items SET printed_qty = quantity WHERE order_id = ?')
               ->execute([$order_id]);

            // Release table
            if ($order['table_id']) {
                $tblStmt = $db->prepare('UPDATE tables SET status = "available" WHERE id = ?');
                $tblStmt->execute([$order['table_id']]);
            }

            // Get items for invoice
            $itemsStmt = $db->prepare('SELECT oi.*, p.name FROM order_items oi JOIN products p ON p.id = oi.product_id WHERE oi.order_id = ?');
            $itemsStmt->execute([$order_id]);

            $db->commit();
            echo json_encode([
                'success' => true,
                'message' => 'Payment successful!',
                'invoice' => [
                    'order_id'       => $order['order_number'],
                    'restaurant_name'=> $order['restaurant_name'],
                    'restaurant_logo'=> $order['restaurant_logo'] ?? '',
                    'restaurant_phone'=> $order['restaurant_phone'] ?? '',
                    'restaurant_address'=> $order['restaurant_address'] ?? '',
                    'cashier_name'   => $order['cashier_name'] ?? '',
                    'waiter_name'    => $order['waiter_name'] ?? '—',
                    'table_name'     => $order['table_name'] ?? '—',
                    'floor_name'     => $order['floor_name'] ?? '—',
                    'order_type'     => $order['order_type'],
                    'subtotal'       => $order['subtotal'],
                    'discount'       => $order['discount'],
                    'tax'            => $order['tax'],
                    'total'          => $order['total'],
                    'payment_method' => $method,
                    'given_amount'   => $given,
                    'change_amount'  => max(0, $change),
                    'coupon_code'    => $order['coupon_code'],
                    'items'          => $itemsStmt->fetchAll(),
                    'created_at'     => $order['created_at'],
                ]
            ]);
        } catch (\Exception $e) {
            $db->rollBack();
            echo json_encode(['success' => false, 'message' => $e->getMessage()]);
        }
        exit;
    }

    // ── AJAX: Accept Waiter / QR Order ──────────────────
    public function acceptOrder() {
        header('Content-Type: application/json');
        $data = json_decode(file_get_contents('php://input'), true);

        if (!$data || !isset($_SESSION['user_id'])) {
            echo json_encode(['success' => false]);
            exit;
        }

        $this->restoreContext();

        $db = getDB();
        $role = $_SESSION['role'] ?? '';
        $user_id = $_SESSION['user_id'];
        if (($role === 'admin' || $role === 'superadmin') && isset($_SESSION['selected_cashier_id'])) {
            $user_id = $_SESSION['selected_cashier_id'];
        }
        
        // Get active session
        $sess = $db->prepare('SELECT id FROM sessions WHERE user_id = ? AND status="open" LIMIT 1');
        $sess->execute([$user_id]);
        $session_id = $sess->fetchColumn();

        if (!$session_id) {
            echo json_encode(['success' => false, 'message' => 'No active session.']);
            exit;
        }

        $order_id = intval($data['order_id']);
        $waiter_id = !empty($data['waiter_id']) ? intval($data['waiter_id']) : null;

        $db->beginTransaction();
        try {
            if ($waiter_id) {
                $stmt = $db->prepare('UPDATE orders SET status = "accepted", cashier_id = ?, session_id = ?, waiter_id = ? WHERE id = ?');
                $stmt->execute([$user_id, $session_id, $waiter_id, $order_id]);
            } else {
                $stmt = $db->prepare('UPDATE orders SET status = "accepted", cashier_id = ?, session_id = ? WHERE id = ?');
                $stmt->execute([$user_id, $session_id, $order_id]);
            }

            // Fetch order items to print KOT
            $itemsStmt = $db->prepare('SELECT oi.*, p.name FROM order_items oi JOIN products p ON p.id = oi.product_id WHERE oi.order_id = ?');
            $itemsStmt->execute([$order_id]);
            $items = $itemsStmt->fetchAll();

            $kot_items = [];
            foreach ($items as $item) {
                $kot_items[] = [
                    'name' => $item['name'],
                    'qty'  => $item['quantity'],
                    'is_new' => true,
                    'label' => 'NEW'
                ];
            }

            // Mark items as printed for KOT
            $db->prepare('UPDATE order_items SET printed_qty = quantity WHERE order_id = ?')
               ->execute([$order_id]);

            // Get order details for printing
            $ordStmt = $db->prepare('SELECT o.order_number, o.waiter_id, t.name as table_name, f.name as floor_name FROM orders o LEFT JOIN tables t ON t.id = o.table_id LEFT JOIN floors f ON f.id = t.floor_id WHERE o.id = ?');
            $ordStmt->execute([$order_id]);
            $order = $ordStmt->fetch();

            $db->commit();
            echo json_encode([
                'success' => true, 
                'message' => 'Order Accepted & KOT Printed!',
                'order_number' => $order['order_number'] ?? $order_id,
                'table_name' => $order['table_name'] ?? '—',
                'floor_name' => $order['floor_name'] ?? '—',
                'waiter_id' => $order['waiter_id'] ?? null,
                'kot_items' => $kot_items
            ]);
        } catch (\Exception $e) {
            $db->rollBack();
            echo json_encode(['success' => false, 'message' => $e->getMessage()]);
        }
        exit;
    }

    // ── AJAX: Assign Coupon to Order ───────────────────
    public function assignCoupon() {
        header('Content-Type: application/json');
        $data = json_decode(file_get_contents('php://input'), true);

        if (!$data || !isset($_SESSION['user_id']) || !isset($data['order_id'])) {
            echo json_encode(['success' => false, 'message' => 'Invalid request']);
            exit;
        }
        FeatureGate::enforceSubscription($_SESSION['restaurant_id']);

        $db = getDB();
        $order_id = intval($data['order_id']);
        $coupon_code = !empty($data['coupon_code']) ? strtoupper(trim($data['coupon_code'])) : null;
        $rest_id = $_SESSION['restaurant_id'];

        try {
            $db->beginTransaction();
            // Get existing order details
            $ordStmt = $db->prepare('SELECT * FROM orders WHERE id = ? AND restaurant_id = ?');
            $ordStmt->execute([$order_id, $rest_id]);
            $order = $ordStmt->fetch();
            if (!$order) throw new \Exception('Order not found');

            $settings = $this->getSettings($db, $rest_id);

            // Fetch order items to compute restricted subtotal if any
            $itemsStmt = $db->prepare('SELECT * FROM order_items WHERE order_id = ? AND status != "voided"');
            $itemsStmt->execute([$order_id]);
            $items = $itemsStmt->fetchAll();

            $coupon_discount = 0;
            if ($coupon_code) {
                $cpn = $db->prepare('SELECT * FROM restaurant_coupons WHERE restaurant_id = ? AND code = ? AND status="active" AND expiry_date >= DATE(NOW()) LIMIT 1');
                $cpn->execute([$rest_id, $coupon_code]);
                $coupon = $cpn->fetch();
                if (!$coupon) throw new \Exception('Coupon not found or expired');

                require_once 'app/Helpers/CouponHelper.php';
                $coupon_discount = CouponHelper::calculateDiscount($coupon, $items, floatval($order['subtotal']));
            }

            $base_discount = floatval($order['subtotal']) * ($settings['discount_rate'] / 100);
            $discount_amount = $base_discount + $coupon_discount;
            $taxable = max(0, floatval($order['subtotal']) - $discount_amount);
            $tax = $taxable * ($settings['tax_rate'] / 100);
            $total = $taxable + $tax;

            $db->prepare('UPDATE orders SET coupon_code = ?, coupon_discount = ?, discount = ?, tax = ?, total = ? WHERE id = ?')
               ->execute([$coupon_code, $coupon_discount, $discount_amount, $tax, $total, $order_id]);

            $db->commit();
            echo json_encode([
                'success' => true,
                'coupon_discount' => $coupon_discount,
                'new_total' => $total,
                'message' => 'Coupon assigned successfully!'
            ]);
        } catch (\Exception $e) {
            $db->rollBack();
            echo json_encode(['success' => false, 'message' => $e->getMessage()]);
        }
        exit;
    }
}
