<?php
declare(strict_types=1);

require_once __DIR__ . '/../config/bootstrap.php';

require_permission($conn, 'quick_service');
process_post_only_or_redirect('../pages/quick_service.php');

ensure_post_csrf_or_redirect('quick_service_save', '../pages/quick_service.php');

ensure_order_branch_column($conn);
ensure_order_bill_number_column($conn);
ensure_order_source_column($conn);
ensure_order_detail_options_column($conn);
ensure_order_detail_item_note_column($conn);
ensure_payment_tender_columns($conn);
ensure_payment_slip_columns($conn);

$quickOrderId = filter_input(INPUT_POST, 'quick_order_id', FILTER_VALIDATE_INT);
$customerIdInput = filter_input(INPUT_POST, 'customer_id', FILTER_VALIDATE_INT);
$carIdInput = filter_input(INPUT_POST, 'car_id', FILTER_VALIDATE_INT);
$submitAction = trim((string) ($_POST['submit_action'] ?? ''));
$branchName = normalize_branch_name((string) ($_POST['branch_name'] ?? ''));
$paymentMethod = trim((string) ($_POST['payment_method'] ?? 'cash'));
$customerMode = trim((string) ($_POST['customer_mode'] ?? ''));
$lookupQuery = trim((string) ($_POST['lookup_phone'] ?? ($_POST['lookup_query'] ?? '')));
$customerNameInput = trim((string) ($_POST['customer_name'] ?? ''));
$phoneInput = normalize_phone((string) ($_POST['phone'] ?? ''));
$plateNumberInput = normalize_plate((string) ($_POST['plate_number'] ?? ''));
$provinceInput = trim((string) ($_POST['province'] ?? ''));
$brandInput = trim((string) ($_POST['brand'] ?? ''));
$modelInput = trim((string) ($_POST['model'] ?? ''));
$colorInput = trim((string) ($_POST['color'] ?? ''));
$totalRaw = str_replace(',', '', trim((string) ($_POST['total_price'] ?? '')));
$receivedRaw = str_replace(',', '', trim((string) ($_POST['received_amount'] ?? '')));
$notes = trim((string) ($_POST['notes'] ?? ''));
$slipFile = $_FILES['slip_file'] ?? null;
$serviceIds = array_values(array_unique(array_filter(array_map('intval', (array) ($_POST['service_ids'] ?? array())))));
$serviceNotesInput = array();
foreach ((array) ($_POST['service_notes'] ?? array()) as $serviceId => $serviceNote) {
    $serviceId = (int) $serviceId;
    $serviceNote = trim((string) $serviceNote);

    if ($serviceId > 0) {
        $serviceNotesInput[$serviceId] = $serviceNote;
    }
}

$oldInput = array(
    'lookup_phone' => $lookupQuery,
    'customer_mode' => $customerMode,
    'customer_id' => $customerIdInput ?: '',
    'car_id' => $carIdInput ?: '',
    'customer_name' => $customerNameInput,
    'phone' => $phoneInput,
    'plate_number' => $plateNumberInput,
    'province' => $provinceInput,
    'brand' => $brandInput,
    'model' => $modelInput,
    'color' => $colorInput,
    'branch_name' => $branchName,
    'payment_method' => $paymentMethod,
    'total_price' => $totalRaw,
    'received_amount' => $receivedRaw,
    'notes' => $notes,
    'service_ids' => $serviceIds,
    'service_notes' => $serviceNotesInput,
);

$redirectPath = '../pages/quick_service.php';
if ($quickOrderId) {
    $redirectPath .= '?id=' . $quickOrderId;
}

if (!in_array($submitAction, array('hold', 'pay'), true)) {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('กรุณาใช้ปุ่มพักบิลหรือชำระเงินเพื่อทำรายการต่อ', 'Use the Hold bill or Payment button to continue.'), $oldInput);
}

if ($submitAction === 'pay' && !can_access($conn, 'quick_service_payment')) {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('คุณไม่มีสิทธิ์รับชำระเงิน Quick Service', 'You do not have permission to receive Quick Service payment.'), $oldInput);
}

$paymentMethods = quick_service_payment_methods();
if (!array_key_exists($paymentMethod, $paymentMethods)) {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('วิธีชำระเงินไม่ถูกต้อง', 'Payment method is invalid.'), $oldInput);
}

$allowedBranches = authorized_order_branch_options($conn);

if ($quickOrderId) {
    $existingBranchStmt = db_prepare($conn, "
        SELECT branch_name
        FROM orders
        WHERE order_id = ?
            AND order_source = 'quick_service'
        LIMIT 1
    ");
    $existingBranchStmt->bind_param('i', $quickOrderId);
    $existingBranchStmt->execute();
    $existingBranchRow = $existingBranchStmt->get_result()->fetch_assoc();
    $existingBranchStmt->close();

    $existingBranchName = normalize_branch_name((string) ($existingBranchRow['branch_name'] ?? ''));
    if ($existingBranchName !== '' && !in_array($existingBranchName, $allowedBranches, true)) {
        $allowedBranches[] = $existingBranchName;
    }
}

if ($branchName === '' || !in_array($branchName, $allowedBranches, true)) {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('กรุณาเลือกสาขาที่ให้บริการ', 'Service branch is required.'), $oldInput);
}

if (empty($serviceIds)) {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('กรุณาเลือกรายการ Quick Service อย่างน้อย 1 รายการ', 'Select at least one quick service item.'), $oldInput);
}

$services = service_catalog_rows($conn, array(
    'service_ids' => $serviceIds,
    'service_types' => array('main_service', 'sub_service'),
    'active_only' => true,
    'quick_service_only' => true,
));

if (count($services) !== count($serviceIds)) {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('มีรายการที่เลือกอย่างน้อย 1 รายการไม่พร้อมใช้งานใน Quick Service', 'One or more selected items are not available in Quick Service.'), $oldInput);
}
$serviceNames = array_values(array_map(static function (array $service): string {
    return (string) ($service['service_name'] ?? '');
}, $services));

$syncQuickServiceQueue = static function (mysqli $conn, int $orderId, string $status): int {
    if ($orderId <= 0) {
        return 0;
    }

    $existingQueueStmt = db_prepare($conn, "
        SELECT queue_id, queue_number
        FROM queue
        WHERE order_id = ?
        ORDER BY queue_id DESC
        LIMIT 1
    ");
    $existingQueueStmt->bind_param('i', $orderId);
    $existingQueueStmt->execute();
    $existingQueue = $existingQueueStmt->get_result()->fetch_assoc();
    $existingQueueStmt->close();

    if ($existingQueue) {
        $queueNumber = (int) ($existingQueue['queue_number'] ?? 0);

        if ($status === 'done') {
            $updateQueueStmt = db_prepare($conn, "
                UPDATE queue
                SET status = 'done',
                    called_at = COALESCE(called_at, NOW()),
                    completed_at = COALESCE(completed_at, NOW())
                WHERE order_id = ?
            ");
        } elseif ($status === 'washing') {
            $updateQueueStmt = db_prepare($conn, "
                UPDATE queue
                SET status = 'washing',
                    called_at = COALESCE(called_at, NOW()),
                    completed_at = NULL
                WHERE order_id = ?
            ");
        } else {
            $updateQueueStmt = db_prepare($conn, "
                UPDATE queue
                SET status = 'waiting',
                    called_at = NULL,
                    completed_at = NULL
                WHERE order_id = ?
            ");
        }

        $updateQueueStmt->bind_param('i', $orderId);
        $updateQueueStmt->execute();
        $updateQueueStmt->close();

        return $queueNumber;
    }

    $queueNumber = 1;
    $queueResult = $conn->query("
        SELECT COALESCE(MAX(queue_number), 0) + 1 AS next_queue
        FROM queue
        WHERE DATE(created_at) = CURDATE()
    ");
    if ($queueResult instanceof mysqli_result) {
        $queueRow = $queueResult->fetch_assoc();
        $queueNumber = (int) ($queueRow['next_queue'] ?? 1);
    }

    $calledAt = ($status === 'washing' || $status === 'done') ? date('Y-m-d H:i:s') : null;
    $completedAt = $status === 'done' ? date('Y-m-d H:i:s') : null;
    $queueStatus = in_array($status, array('waiting', 'washing', 'done', 'cancelled'), true) ? $status : 'waiting';

    $insertQueueStmt = db_prepare($conn, "
        INSERT INTO queue (order_id, queue_number, status, called_at, completed_at)
        VALUES (?, ?, ?, ?, ?)
    ");
    $insertQueueStmt->bind_param('iisss', $orderId, $queueNumber, $queueStatus, $calledAt, $completedAt);
    $insertQueueStmt->execute();
    $insertQueueStmt->close();

    return $queueNumber;
};

$moneyToCents = static function ($value): int {
    if (!is_numeric($value)) {
        return 0;
    }

    return max(0, (int) round((float) $value * 100));
};

$centsToMoney = static function (int $cents): float {
    return $cents / 100;
};

$serviceTotalCents = 0;
foreach ($services as $service) {
    $serviceTotalCents += $moneyToCents($service['price'] ?? 0);
}

if ($totalRaw === '') {
    $totalPriceCents = $serviceTotalCents;
} elseif (is_numeric($totalRaw)) {
    $totalPriceCents = $moneyToCents($totalRaw);
} else {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('ยอดรวมบิลไม่ถูกต้อง', 'Bill total is invalid.'), $oldInput);
}

if ($receivedRaw === '') {
    $receivedAmountCents = 0;
} elseif (is_numeric($receivedRaw)) {
    $receivedAmountCents = $moneyToCents($receivedRaw);
} else {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('จำนวนเงินที่รับมาไม่ถูกต้อง', 'Received amount is invalid.'), $oldInput);
}

$totalPrice = $centsToMoney($totalPriceCents);
$receivedAmount = $centsToMoney($receivedAmountCents);

$hasSlipUpload = is_array($slipFile)
    && (int) ($slipFile['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE;

if ($hasSlipUpload && !payment_method_supports_slip($paymentMethod)) {
    redirect_with_flash($redirectPath, 'error', localized_flash_message('แนบสลิปได้เฉพาะการโอน, QR หรือบัตร', 'Slip upload is available only for Transfer, QR or Card payments.'), $oldInput);
}

$conn->begin_transaction();
$storedSlip = null;

try {
    $currentUser = auth_current_user($conn);
    $employeeIdValue = (int) ($currentUser['employee_id'] ?? 0);
    $orderId = 0;
    $customerId = 0;
    $carId = 0;
    $resolvedCustomerName = '';
    $resolvedPhone = '';
    $resolvedPlateNumber = '';
    $resolvedCustomerGroup = 'member';
    $orderDate = date('Y-m-d H:i:s');
    $paidBeforeAmountCents = 0;
    $existingOrderBefore = null;
    $isNewOrder = $quickOrderId <= 0;

    if ($quickOrderId) {
        $orderStmt = db_prepare($conn, "
            SELECT
                o.order_id,
                o.customer_id,
                o.car_id,
                o.bill_number,
                o.order_date,
                o.status,
                o.payment_status,
                o.branch_name,
                o.total_price,
                o.notes
            FROM orders o
            WHERE o.order_id = ?
                AND o.order_source = 'quick_service'
            LIMIT 1
        ");
        $orderStmt->bind_param('i', $quickOrderId);
        $orderStmt->execute();
        $existingOrder = $orderStmt->get_result()->fetch_assoc();
        $orderStmt->close();

        if (!$existingOrder) {
            throw new RuntimeException(localized_flash_message_string('ไม่พบบิลที่พักไว้', 'Held bill was not found.'));
        }

        if (!auth_can_access_branch($conn, (string) ($existingOrder['branch_name'] ?? ''))) {
            throw new RuntimeException(localized_flash_message_string('คุณไม่มีสิทธิ์เข้าถึงสาขานี้', 'Branch access denied.'));
        }

        if ((string) ($existingOrder['status'] ?? '') === 'cancelled') {
            throw new RuntimeException(localized_flash_message_string('บิลที่ยกเลิกแล้วไม่สามารถแก้ไขได้', 'Cancelled bill cannot be updated.'));
        }

        $orderId = (int) ($existingOrder['order_id'] ?? 0);
        $orderDate = (string) ($existingOrder['order_date'] ?? $orderDate);
        $existingOrderBefore = array(
            'branch_name' => (string) ($existingOrder['branch_name'] ?? ''),
            'bill_number' => (string) ($existingOrder['bill_number'] ?? ''),
            'status' => (string) ($existingOrder['status'] ?? ''),
            'payment_status' => (string) ($existingOrder['payment_status'] ?? ''),
            'total_price' => (float) ($existingOrder['total_price'] ?? 0),
            'notes' => (string) ($existingOrder['notes'] ?? ''),
        );

        $paidBeforeResult = $conn->query("
            SELECT COALESCE(SUM(amount), 0) AS paid_amount
            FROM payments
            WHERE order_id = " . $orderId
        );
        $paidBeforeRow = $paidBeforeResult instanceof mysqli_result ? $paidBeforeResult->fetch_assoc() : array('paid_amount' => 0);
        $paidBeforeAmountCents = $moneyToCents($paidBeforeRow['paid_amount'] ?? 0);
    }

    if (!in_array($customerMode, array('matched', 'guest'), true)) {
        throw new RuntimeException(localized_flash_message_string('กรุณาค้นหาลูกค้าเดิมหรือเลือก Guest ก่อนทำรายการต่อ', 'Search for an existing customer or use Guest before continuing.'));
    }

    if ($customerMode === 'matched') {
        $customerId = max(0, (int) $customerIdInput);
        $carId = max(0, (int) $carIdInput);

        if ($customerId <= 0 || $carId <= 0) {
            throw new RuntimeException(localized_flash_message_string('กรุณาเลือกลูกค้าเดิมก่อนทำรายการต่อ', 'Select an existing customer record before continuing.'));
        }

        $customerLookupStmt = db_prepare($conn, "
            SELECT
                c.customer_id,
                c.name,
                c.phone,
                c.customer_group,
                car.car_id,
                car.plate_number,
                car.province,
                car.brand,
                car.model,
                car.color
            FROM customers c
            INNER JOIN cars car ON car.customer_id = c.customer_id
            WHERE c.customer_id = ?
                AND car.car_id = ?
            LIMIT 1
        ");
        $customerLookupStmt->bind_param('ii', $customerId, $carId);
        $customerLookupStmt->execute();
        $customerRecord = $customerLookupStmt->get_result()->fetch_assoc();
        $customerLookupStmt->close();

        if (!$customerRecord) {
            throw new RuntimeException(localized_flash_message_string('ไม่พบข้อมูลลูกค้าที่เลือก', 'The selected customer record was not found.'));
        }

        $customerId = (int) ($customerRecord['customer_id'] ?? 0);
        $carId = (int) ($customerRecord['car_id'] ?? 0);
        $resolvedCustomerName = trim((string) ($customerRecord['name'] ?? ''));
        $resolvedPhone = normalize_phone((string) ($customerRecord['phone'] ?? ''));
        $resolvedPlateNumber = normalize_plate((string) ($customerRecord['plate_number'] ?? ''));
        $resolvedCustomerGroup = customer_group_key((string) ($customerRecord['customer_group'] ?? 'member'));
    } else {
        if ($plateNumberInput === '') {
            throw new RuntimeException(localized_flash_message_string('กรุณาระบุเลขทะเบียนรถของลูกค้า Guest ก่อนทำรายการต่อ', 'Enter the plate number for the guest customer before continuing.'));
        }

        $guestCustomerName = $customerNameInput !== ''
            ? $customerNameInput
            : 'Guest';

        $carLookupStmt = db_prepare($conn, "
            SELECT car_id, customer_id
            FROM cars
            WHERE plate_number = ?
            LIMIT 1
        ");
        $carLookupStmt->bind_param('s', $plateNumberInput);
        $carLookupStmt->execute();
        $existingCar = $carLookupStmt->get_result()->fetch_assoc();
        $carLookupStmt->close();

        if ($existingCar) {
            $customerId = (int) ($existingCar['customer_id'] ?? 0);
            $carId = (int) ($existingCar['car_id'] ?? 0);

            $existingCustomerStmt = db_prepare($conn, "
                SELECT c.name, c.phone, c.customer_group, car.plate_number
                FROM customers c
                INNER JOIN cars car ON car.customer_id = c.customer_id
                WHERE c.customer_id = ?
                    AND car.car_id = ?
                LIMIT 1
            ");
            $existingCustomerStmt->bind_param('ii', $customerId, $carId);
            $existingCustomerStmt->execute();
            $existingCustomer = $existingCustomerStmt->get_result()->fetch_assoc();
            $existingCustomerStmt->close();

            if (!$existingCustomer) {
                throw new RuntimeException(localized_flash_message_string('ไม่พบรถลูกค้า Guest ที่เลือก', 'The selected guest vehicle was not found.'));
            }

            $resolvedCustomerName = trim((string) ($existingCustomer['name'] ?? ''));
            $resolvedPhone = normalize_phone((string) ($existingCustomer['phone'] ?? ''));
            $resolvedPlateNumber = normalize_plate((string) ($existingCustomer['plate_number'] ?? ''));
            $resolvedCustomerGroup = customer_group_key((string) ($existingCustomer['customer_group'] ?? 'guest'));
        } else {
            $insertGuestCustomerStmt = db_prepare($conn, "
                INSERT INTO customers (name, phone, normalized_phone, customer_group)
                VALUES (?, '', '', 'guest')
            ");
            $insertGuestCustomerStmt->bind_param('s', $guestCustomerName);
            $insertGuestCustomerStmt->execute();
            $customerId = (int) $conn->insert_id;
            $insertGuestCustomerStmt->close();

            if ($customerId <= 0) {
                throw new RuntimeException(localized_flash_message_string('ไม่สามารถเตรียมข้อมูลลูกค้า Guest ได้', 'Guest profile could not be prepared.'));
            }

            $vehicleType = '';
            $guestBrand = $brandInput;
            $guestModel = $modelInput;
            $guestColor = $colorInput;

            $insertGuestCarStmt = db_prepare($conn, "
                INSERT INTO cars (customer_id, plate_number, province, vehicle_type, brand, model, color)
                VALUES (?, ?, ?, ?, ?, ?, ?)
            ");
            $insertGuestCarStmt->bind_param(
                'issssss',
                $customerId,
                $plateNumberInput,
                $provinceInput,
                $vehicleType,
                $guestBrand,
                $guestModel,
                $guestColor
            );
            $insertGuestCarStmt->execute();
            $carId = (int) $conn->insert_id;
            $insertGuestCarStmt->close();

            $resolvedCustomerName = $guestCustomerName;
            $resolvedPhone = '';
            $resolvedPlateNumber = $plateNumberInput;
            $resolvedCustomerGroup = 'guest';
        }
    }

    $balanceBeforePaymentCents = max(0, $totalPriceCents - $paidBeforeAmountCents);

    if (
        $submitAction === 'pay'
        && $balanceBeforePaymentCents > 0
        && $receivedAmountCents <= 0
    ) {
        throw new RuntimeException(localized_flash_message_string('กรุณาระบุจำนวนเงินที่รับมา หรือใช้พักบิลหากยังไม่ต้องการบันทึกการชำระเงิน', 'Enter the amount received, or use Hold bill to keep this bill open without adding a payment.'));
    }

    if (
        $submitAction === 'pay'
        && $paymentMethod !== 'cash'
        && $receivedAmountCents > $balanceBeforePaymentCents
    ) {
        throw new RuntimeException(localized_flash_message_string('จำนวนเงินที่รับมาสูงกว่ายอดค้างชำระ กรุณาปรับให้ตรงก่อนปิดบิล', 'Received amount is higher than the amount due. Adjust it to match before closing the bill.'));
    }

    $shouldRecordPayment = $submitAction === 'pay';

    if (!$shouldRecordPayment && $hasSlipUpload) {
        throw new RuntimeException(localized_flash_message_string('แนบสลิปได้เฉพาะตอนใช้ปุ่มชำระเงินเพื่อบันทึกรับเงิน', 'Attach a payment slip only when using Payment to record received money.'));
    }

    $effectiveReceivedAmountCents = $shouldRecordPayment
        ? $receivedAmountCents
        : 0;

    $recognizedPaymentCents = $shouldRecordPayment
        ? min($effectiveReceivedAmountCents, $balanceBeforePaymentCents)
        : 0;
    $changeAmountCents = ($shouldRecordPayment && $paymentMethod === 'cash')
        ? max(0, $effectiveReceivedAmountCents - $balanceBeforePaymentCents)
        : 0;

    $recognizedPayment = $centsToMoney($recognizedPaymentCents);
    $changeAmount = $centsToMoney($changeAmountCents);
    $effectiveReceivedAmount = $centsToMoney($effectiveReceivedAmountCents);
    $paidAfterAmountCents = $paidBeforeAmountCents + $recognizedPaymentCents;
    $remainingBalanceCents = max(0, $totalPriceCents - $paidAfterAmountCents);
    $paidAfterAmount = $centsToMoney($paidAfterAmountCents);
    $remainingBalance = $centsToMoney($remainingBalanceCents);

    if ($hasSlipUpload && $recognizedPaymentCents <= 0) {
        throw new RuntimeException(localized_flash_message_string('แนบสลิปได้เมื่อมีการบันทึกยอดชำระเงินเท่านั้น', 'Attach a payment slip only when recording a payment amount.'));
    }

    if ($hasSlipUpload) {
        $storedSlip = store_payment_slip_upload(is_array($slipFile) ? $slipFile : null, 'quick-service-payment');
    }

    $slipFilePath = trim((string) ($storedSlip['file_path'] ?? ''));
    $slipFileName = trim((string) ($storedSlip['file_name'] ?? ''));
    $slipMimeType = trim((string) ($storedSlip['mime_type'] ?? ''));
    $slipUploadedAt = trim((string) ($storedSlip['uploaded_at'] ?? ''));

    $quickServiceState = quick_service_order_state_from_amounts($totalPrice, $paidAfterAmount);
    $paymentStatus = (string) ($quickServiceState['payment_status'] ?? 'unpaid');
    $status = (string) ($quickServiceState['status'] ?? 'waiting');
    $finishedAt = $quickServiceState['finished_at'] !== null ? (string) $quickServiceState['finished_at'] : null;

    if ($orderId > 0) {
        $updateOrderStmt = db_prepare($conn, "
            UPDATE orders
            SET customer_id = ?,
                car_id = ?,
                employee_id = ?,
                branch_name = ?,
                status = ?,
                payment_status = ?,
                total_price = ?,
                deposit_amount = 0.00,
                deposit_method = NULL,
                notes = ?,
                finished_at = ?
            WHERE order_id = ?
        ");
        $updateOrderStmt->bind_param(
            'iiisssdssi',
            $customerId,
            $carId,
            $employeeIdValue,
            $branchName,
            $status,
            $paymentStatus,
            $totalPrice,
            $notes,
            $finishedAt,
            $orderId
        );
        $updateOrderStmt->execute();
        $updateOrderStmt->close();

        $deleteDetailStmt = db_prepare($conn, "
            DELETE FROM order_details
            WHERE order_id = ?
        ");
        $deleteDetailStmt->bind_param('i', $orderId);
        $deleteDetailStmt->execute();
        $deleteDetailStmt->close();
    } else {
        $insertOrderStmt = db_prepare($conn, "
            INSERT INTO orders (
                customer_id,
                car_id,
                employee_id,
                branch_name,
                scheduled_at,
                return_at,
                order_source,
                status,
                payment_status,
                total_price,
                deposit_amount,
                deposit_method,
                notes,
                finished_at
            )
            VALUES (?, ?, ?, ?, NULL, NULL, 'quick_service', ?, ?, ?, 0.00, NULL, ?, ?)
        ");
        $insertOrderStmt->bind_param(
            'iiisssdss',
            $customerId,
            $carId,
            $employeeIdValue,
            $branchName,
            $status,
            $paymentStatus,
            $totalPrice,
            $notes,
            $finishedAt
        );
        $insertOrderStmt->execute();
        $orderId = (int) $conn->insert_id;
        $insertOrderStmt->close();

        $orderDate = date('Y-m-d H:i:s');
    }

    $billNumber = build_order_bill_number($branchName, $orderDate, $orderId);
    $updateBillStmt = db_prepare($conn, "
        UPDATE orders
        SET bill_number = ?,
            order_date = ?,
            finished_at = ?
        WHERE order_id = ?
    ");
    $updateBillStmt->bind_param('sssi', $billNumber, $orderDate, $finishedAt, $orderId);
    $updateBillStmt->execute();
    $updateBillStmt->close();

    $queueNumber = $syncQuickServiceQueue($conn, $orderId, $status);

    $insertDetailStmt = db_prepare($conn, "
        INSERT INTO order_details (order_id, service_id, service_name_snapshot, price, duration_minutes, service_options, item_note)
        VALUES (?, ?, ?, ?, 0, NULL, NULLIF(?, ''))
    ");

    foreach ($services as $service) {
        $serviceId = (int) ($service['service_id'] ?? 0);
        $serviceName = (string) ($service['service_name'] ?? '');
        $servicePrice = (float) ($service['price'] ?? 0);
        $itemNote = trim((string) ($serviceNotesInput[$serviceId] ?? ''));
        $insertDetailStmt->bind_param('iisds', $orderId, $serviceId, $serviceName, $servicePrice, $itemNote);
        $insertDetailStmt->execute();
    }

    $insertDetailStmt->close();

    $paymentId = 0;
    if ($recognizedPayment > 0.0) {
        $paymentNote = $submitAction === 'hold'
            ? 'Quick Service hold payment'
            : 'Quick Service payment';

        $paymentStmt = db_prepare($conn, "
            INSERT INTO payments (
                order_id,
                payment_method,
                amount,
                received_amount,
                change_amount,
                note,
                slip_file_path,
                slip_file_name,
                slip_mime_type,
                slip_uploaded_at
            )
            VALUES (?, ?, ?, ?, ?, ?, NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''), NULLIF(?, ''))
        ");
        $paymentStmt->bind_param(
            'isdddsssss',
            $orderId,
            $paymentMethod,
            $recognizedPayment,
            $effectiveReceivedAmount,
            $changeAmount,
            $paymentNote,
            $slipFilePath,
            $slipFileName,
            $slipMimeType,
            $slipUploadedAt
        );
        $paymentStmt->execute();
        $paymentId = (int) $conn->insert_id;
        $paymentStmt->close();
    }

    audit_log($conn, array(
        'module_key' => 'bill',
        'action_key' => $isNewOrder ? 'create' : ($submitAction === 'hold' ? 'hold' : 'update'),
        'entity_type' => 'order',
        'entity_id' => $orderId,
        'order_id' => $orderId,
        'customer_id' => $customerId,
        'car_id' => $carId,
        'branch_name' => $branchName,
        'bill_number' => $billNumber,
        'amount' => $totalPrice,
        'message' => $isNewOrder
            ? ($submitAction === 'hold' ? 'Quick Service bill created and held.' : 'Quick Service bill created.')
            : ($submitAction === 'hold' ? 'Quick Service held bill updated.' : 'Quick Service bill updated.'),
        'before' => $existingOrderBefore,
        'after' => array(
            'order_source' => 'quick_service',
            'customer_mode' => $customerMode,
            'customer_name' => $resolvedCustomerName,
            'customer_group' => $resolvedCustomerGroup,
            'phone' => $resolvedPhone,
            'plate_number' => $resolvedPlateNumber,
            'status' => $status,
            'payment_status' => $paymentStatus,
            'queue_number' => $queueNumber,
            'total_price' => $totalPrice,
            'paid_before' => $centsToMoney($paidBeforeAmountCents),
            'paid_after' => $paidAfterAmount,
            'balance_amount' => $remainingBalance,
            'service_count' => count($serviceNames),
            'service_names' => $serviceNames,
            'notes' => $notes,
        ),
        'meta' => array(
            'submit_action' => $submitAction,
            'payment_method' => $paymentMethod,
            'slip_file_name' => $slipFileName,
        ),
    ));

    if ($paymentId > 0) {
        audit_log($conn, array(
            'module_key' => 'payment',
            'action_key' => 'receive',
            'entity_type' => 'payment',
            'entity_id' => $paymentId,
            'order_id' => $orderId,
            'payment_id' => $paymentId,
            'customer_id' => $customerId,
            'car_id' => $carId,
            'branch_name' => $branchName,
            'bill_number' => $billNumber,
            'amount' => $recognizedPayment,
            'message' => 'Quick Service payment received.',
            'before' => array(
                'payment_status' => $existingOrderBefore['payment_status'] ?? 'unpaid',
                'paid_amount' => $centsToMoney($paidBeforeAmountCents),
                'balance_amount' => $centsToMoney($balanceBeforePaymentCents),
            ),
            'after' => array(
                'payment_method' => $paymentMethod,
                'recognized_amount' => $recognizedPayment,
                'received_amount' => $effectiveReceivedAmount,
                'change_amount' => $changeAmount,
                'has_slip' => $slipFilePath !== '',
                'payment_status' => $paymentStatus,
                'paid_amount' => $paidAfterAmount,
                'balance_amount' => $remainingBalance,
            ),
            'meta' => array(
                'order_source' => 'quick_service',
                'submit_action' => $submitAction,
                'slip_file_name' => $slipFileName,
            ),
        ));
    }

    $conn->commit();
    $_SESSION['booking_branch_name'] = $branchName;
    $_SESSION['booking_payment_method'] = $paymentMethod;

    if ($submitAction === 'pay' && $paymentStatus === 'paid') {
        redirect_with_flash(
            '../pages/quick_receipt.php?id=' . $orderId,
            'success',
            localized_flash_message(
                'ชำระเงิน Quick Service เรียบร้อยแล้ว',
                'Quick Service payment completed.'
            )
        );
    }

    if ($submitAction === 'pay') {
        redirect_with_flash(
            '../pages/quick_receipt.php?id=' . $orderId,
            'success',
            localized_flash_message(
                'บันทึกการชำระเงินแล้ว ยังคงเหลือ {remaining_balance} บาท',
                'Payment recorded. Outstanding {remaining_balance} THB remains.',
                array('remaining_balance' => format_money($remainingBalance))
            )
        );
    }

    redirect_with_flash(
        '../pages/quick_service.php?id=' . $orderId,
        'success',
        localized_flash_message('พักบิล Quick Service เรียบร้อยแล้ว', 'Quick Service bill held.')
    );
} catch (Throwable $exception) {
    process_handle_exception_and_redirect(
        $conn,
        $exception,
        'save_quick_service',
        $redirectPath,
        localized_flash_message(
            'เธเธฑเธเธ—เธถเธเธเธดเธฅ Quick Service เนเธกเนเธชเธณเน€เธฃเนเธ',
            'Quick Service bill was not saved.'
        ),
        array(
            'quick_order_id' => $quickOrderId,
            'submit_action' => $submitAction,
            'customer_mode' => $customerMode,
            'branch_name' => $branchName,
            'service_ids' => $serviceIds,
            'payment_method' => $paymentMethod,
            'has_slip_upload' => $hasSlipUpload ? 1 : 0,
        ),
        $oldInput,
        true,
        static function () use ($storedSlip): void {
            if (is_array($storedSlip) && !empty($storedSlip['absolute_path']) && is_string($storedSlip['absolute_path'])) {
                @unlink($storedSlip['absolute_path']);
            }
        }
    );
    return;
}
