package com.unibuy.smartdevice.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.os.PowerManager;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

import com.unibuy.smartdevice.AppEntry;
import com.unibuy.smartdevice.R;
import com.unibuy.smartdevice.MyApp;
import com.unibuy.smartdevice.devices.SlotField;
import com.unibuy.smartdevice.exception.LogsEmptyException;
import com.unibuy.smartdevice.external.mqtt.MqttManager;
import com.unibuy.smartdevice.structure.BuyStructure;
import com.unibuy.smartdevice.structure.ReportFlowInfoStructure;
import com.unibuy.smartdevice.structure.SlotStructure;
import com.unibuy.smartdevice.structure.ProductStructure;
import com.unibuy.smartdevice.structure.mqtt.TransactionFinalizePayload;
import com.unibuy.smartdevice.tools.TransactionFinalizeBuilder;
import com.unibuy.smartdevice.ui.tools.ImageGlide;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;

/**
 * Foreground Service，負責持有 WakeLock 並管理 MQTT 連線。
 * 確保販賣機台在關閉螢幕時也能維持通訊。
 * 斷線偵測統一由 MQTT LWT（遺囑）機制處理，不另設其他偵測邏輯。
 */
public class MqttService extends Service {
    private static final String TAG = "MqttService";
    private static final String CHANNEL_ID = "MqttServiceChannel";
    private PowerManager.WakeLock wakeLock;
    private com.unibuy.smartdevice.exception.Logs logs;

    @Override
    public void onCreate() {
        super.onCreate();
        logs = new com.unibuy.smartdevice.exception.Logs(getClass());
        logs.info("MqttService Created");

        // 1. 建立 Notification Channel
        createNotificationChannel();

        // 2. 啟動為前台服務（確保不被系統殺掉）
        Intent notificationIntent = new Intent(this, AppEntry.initialActivity());
        PendingIntent pendingIntent = PendingIntent.getActivity(this,
                0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("StarCloud MQTT Service")
                .setContentText("機台通訊監控中...")
                .setSmallIcon(R.drawable.cpu)
                .setContentIntent(pendingIntent)
                .build();

        startForeground(1, notification);

        // 3. 取得 WakeLock（確保關螢幕時 CPU 不休眠，心跳不中斷）
        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        if (powerManager != null) {
            wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StarCloud:MqttWakeLock");
            wakeLock.acquire();
            logs.info("WakeLock acquired");
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String machineId = intent != null ? intent.getStringExtra("machineId") : null;
        String apiToken = intent != null ? intent.getStringExtra("apiToken") : null;

        logs.info("MqttService onStartCommand with machineId: " + machineId);

        if (machineId != null && apiToken != null) {
            MqttManager mqttManager = MqttManager.getInstance();
            mqttManager.setCommandListener(command -> {
                String cmd = command.getCommand();
                String cmdId = command.getCommand_id();
                logs.info("Received MQTT command: " + cmd + " (ID: " + cmdId + ")");

                try {
                    if ("reboot".equals(cmd) || "reboot_force".equals(cmd)) {
                        logs.info("Executing remote reboot command...");

                        long idle = MyApp.getInstance().getIdleSeconds();
                        String busyReason = MyApp.getInstance().getBusyReason();

                        // 🚩 強制重啟旗標：指令為 reboot_force，或 reboot 的 payload 帶 {"force":true}。
                        // 用途：現場卡死（例如對話框被 HID 誤觸卡住 → [Busy Dialog Open]）時，
                        // 操作員仍能「無視忙碌」遠端強制重啟，作為最後逃生口。
                        boolean force = "reboot_force".equals(cmd);
                        try {
                            com.google.gson.JsonElement p = command.getPayload();
                            if (!force && p != null && p.isJsonObject()) {
                                com.google.gson.JsonObject o = p.getAsJsonObject();
                                force = o.has("force") && o.get("force").getAsBoolean();
                            }
                        } catch (Exception ignore) {
                            // payload 非預期格式時，視為非強制
                        }

                        // 🚩 若機台忙碌（付款/出貨對話框、NFC 交易中）則拒絕；force 時略過此保護。
                        if (!busyReason.isEmpty() && !force) {
                            logs.info("Remote reboot rejected: " + busyReason);
                            mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + busyReason);
                            return;
                        }
                        if (!busyReason.isEmpty()) {
                            logs.info("Remote reboot FORCED despite busy: " + busyReason);
                        }

                        // 🚩 若不忙碌但閒置時間不足，僅記錄警告但允許重啟（遠端控制優先）
                        if (idle < 60) {
                            logs.info("Remote reboot accepted despite low idle (" + idle + "s), as machine is not busy.");
                        }

                        mqttManager.publishCommandAck(cmdId, "success", "Rebooting soon...");
                        // 🚩 優雅重啟：ACK 發出後先 gracefulDisconnect（不觸發 LWT），再 killProcess
                        new android.os.Handler(android.os.Looper.getMainLooper())
                                .postDelayed(() -> mqttManager.gracefulDisconnect(() -> {
                                    new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
                                        Intent restartIntent = new Intent(getApplicationContext(),
                                                AppEntry.initialActivity());
                                        restartIntent.addFlags(
                                                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                        startActivity(restartIntent);
                                        android.os.Process.killProcess(android.os.Process.myPid());
                                        System.exit(0);
                                    });
                                }), 500); // 0.5 秒確保 ACK 先發出
                    } else if ("lock".equals(cmd)) {
                        logs.info("Executing remote lock command...");
                        com.unibuy.smartdevice.database.SettingsDao settingsDao = new com.unibuy.smartdevice.database.SettingsDao(
                                getApplicationContext());
                        settingsDao.setMachineLocked(true);
                        settingsDao.close();

                        mqttManager.publishCommandAck(cmdId, "success", "Machine locked");

                        new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
                            Intent lockIntent = new Intent(getApplicationContext(),
                                    com.unibuy.smartdevice.ui.MaintenanceActivity.class);
                            lockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                            startActivity(lockIntent);
                        });
                    } else if ("unlock".equals(cmd)) {
                        logs.info("Executing remote unlock command...");

                        String busyReason = MyApp.getInstance().getBusyReason();

                        if (!busyReason.isEmpty()) {
                            logs.info("Remote unlock rejected: " + busyReason);
                            mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + busyReason);
                            return;
                        }

                        com.unibuy.smartdevice.database.SettingsDao settingsDao = new com.unibuy.smartdevice.database.SettingsDao(
                                getApplicationContext());
                        settingsDao.setMachineLocked(false);
                        settingsDao.close();

                        mqttManager.publishCommandAck(cmdId, "success", "Machine unlocked, restarting...");

                        // 🚩 優雅重啟：先 gracefulDisconnect（不觸發 LWT），再 killProcess
                        // 解鎖後重啟到目前版本的初始畫面
                        new android.os.Handler(android.os.Looper.getMainLooper())
                                .postDelayed(() -> mqttManager.gracefulDisconnect(() -> {
                                    new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
                                        Intent restartIntent = new Intent(getApplicationContext(),
                                                AppEntry.initialActivity());
                                        restartIntent.addFlags(
                                                Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                        startActivity(restartIntent);
                                        android.os.Process.killProcess(android.os.Process.myPid());
                                        System.exit(0);
                                    });
                                }), 500);
                    } else if ("nfc_reset".equals(cmd) || "reboot_card".equals(cmd)) {
                        // 後台送 reboot_card、App 舊名 nfc_reset，兩者皆相容
                        logs.info("Executing remote NFC reset command...");
                        String busyReason = com.unibuy.smartdevice.MyApp.getInstance().getBusyReason();
                        if (!busyReason.isEmpty()) {
                            // 現場正在交易：拒收，避免與現場刷卡撞 comPort2（撞埠會觸發 Rs232Dev mock）
                            logs.info("NFC reset rejected, machine busy: " + busyReason);
                            mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + busyReason);
                        } else {
                            com.unibuy.smartdevice.devices.Rs232Dev comPort2 = null;
                            try {
                                comPort2 = new com.unibuy.smartdevice.devices.Rs232Dev(
                                        com.unibuy.smartdevice.MyApp.getInstance().getComPort2ByNFCPay());
                                com.unibuy.smartdevice.devices.DevNFCPay devNFCPay = new com.unibuy.smartdevice.devices.DevNFCPay(
                                        getApplicationContext());
                                comPort2.send(devNFCPay.reset());
                                mqttManager.publishCommandAck(cmdId, "success", "NFC reset command sent");
                            } catch (Exception e) {
                                logs.info("NFC reset failed: " + e.getMessage());
                                mqttManager.publishCommandAck(cmdId, "failed", "NFC reset failed: " + e.getMessage());
                            } finally {
                                // 送完務必關埠，避免序列埠洩漏導致下一筆現場交易開不了埠
                                if (comPort2 != null) {
                                    try { comPort2.closePort(); } catch (Exception ignore) {}
                                }
                            }
                        }
                    } else if ("nfc_checkout".equals(cmd) || "checkout".equals(cmd)) {
                        // 後台送 checkout、App 舊名 nfc_checkout，兩者皆相容
                        logs.info("Executing remote NFC checkout command...");
                        String busyReason = com.unibuy.smartdevice.MyApp.getInstance().getBusyReason();
                        if (!busyReason.isEmpty()) {
                            logs.info("NFC checkout rejected, machine busy: " + busyReason);
                            mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + busyReason);
                        } else {
                            com.unibuy.smartdevice.devices.Rs232Dev comPort2 = null;
                            try {
                                comPort2 = new com.unibuy.smartdevice.devices.Rs232Dev(
                                        com.unibuy.smartdevice.MyApp.getInstance().getComPort2ByNFCPay());
                                com.unibuy.smartdevice.devices.DevNFCPay devNFCPay = new com.unibuy.smartdevice.devices.DevNFCPay(
                                        getApplicationContext());
                                comPort2.send(devNFCPay.checkout());
                                mqttManager.publishCommandAck(cmdId, "success", "NFC checkout command sent");
                            } catch (Exception e) {
                                logs.info("NFC checkout failed: " + e.getMessage());
                                mqttManager.publishCommandAck(cmdId, "failed", "NFC checkout failed: " + e.getMessage());
                            } finally {
                                if (comPort2 != null) {
                                    try { comPort2.closePort(); } catch (Exception ignore) {}
                                }
                            }
                        }
                    } else if ("change".equals(cmd)) {
                        logs.info("Executing remote change command...");
                        try {
                            int amount = 0;
                            if (command.getPayload() != null && command.getPayload().isJsonObject()) {
                                com.google.gson.JsonObject payloadObj = command.getPayload().getAsJsonObject();
                                if (payloadObj.has("amount")) {
                                    amount = payloadObj.get("amount").getAsInt();
                                }
                            }

                            if (amount <= 0) {
                                throw new Exception("Invalid or missing amount: " + amount);
                            }

                            int finalAmount = amount;
                            com.unibuy.smartdevice.tools.CashPayoutManager payoutManager = new com.unibuy.smartdevice.tools.CashPayoutManager(
                                    getApplicationContext(),
                                    new com.unibuy.smartdevice.tools.CashPayoutManager.PayoutListener() {
                                        @Override
                                        public void onProgress(String message) {
                                            logs.debug("Payout Progress: " + message);
                                        }

                                        @Override
                                        public void onSuccess() {
                                            logs.info("Remote payout success for amount: " + finalAmount);
                                            mqttManager.publishCommandAck(cmdId, "success",
                                                    "Payout completed: " + finalAmount);
                                        }

                                        @Override
                                        public void onFailure(String error) {
                                            logs.info("Remote payout failed: " + error);
                                            mqttManager.publishCommandAck(cmdId, "failed", "Payout failed: " + error);
                                        }
                                    });

                            payoutManager.startPayout(amount);
                            // 注意：ACK 會在 PayoutListener 的回調中發送，不在此處發送。
                        } catch (Exception e) {
                            logs.info("Failed to parse or start change command: " + e.getMessage());
                            mqttManager.publishCommandAck(cmdId, "failed", "Error: " + e.getMessage());
                        }
                    } else if ("update_ads".equals(cmd)) {
                        logs.info("Executing remote advertisement sync command...");

                        // 📢 在主執行緒顯示 Toast 提示使用者
                        new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                android.widget.Toast.makeText(getApplicationContext(),
                                        "📢 廣告更新中...", android.widget.Toast.LENGTH_SHORT).show());

                        com.unibuy.smartdevice.tools.HandlerMain adHandler = new com.unibuy.smartdevice.tools.HandlerMain(
                                getApplicationContext(), logs) {
                            @Override
                            protected void execute(Context context, int code, String message) {
                                logs.info("Ad sync event: [" + code + "] " + message);
                                if (code == 200 && "廣告同步完成".equals(message)) {
                                    mqttManager.publishCommandAck(cmdId, "success",
                                            "Advertisement sync completed successfully");

                                    // ✅ Toast 提示完成
                                    new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                            android.widget.Toast.makeText(context,
                                                    "✅ 廣告更新完成", android.widget.Toast.LENGTH_SHORT).show());
                                    // 注意：廣播 ACTION_AD_UPDATED 已在 StarCloudAPI.saveAds() 中統一發送
                                } else if (code >= 400) {
                                    mqttManager.publishCommandAck(cmdId, "failed",
                                            "Advertisement sync failed: " + message);

                                    new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                            android.widget.Toast.makeText(context,
                                                    "❌ 廣告更新失敗: " + message, android.widget.Toast.LENGTH_LONG).show());
                                }
                            }
                        };
                        // 使用新架構 StarCloudAPI 執行同步
                        new com.unibuy.smartdevice.external.StarCloudAPI(adHandler, adHandler).downloadAdvertisement();
                    } else if ("update_products".equals(cmd)) {
                        logs.info("Executing remote product sync command...");

                        // 📢 在主執行緒顯示 Toast 提示使用者
                        new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                android.widget.Toast.makeText(getApplicationContext(),
                                        "📢 商品更新中...", android.widget.Toast.LENGTH_SHORT).show());

                        com.unibuy.smartdevice.tools.HandlerMain productHandler = new com.unibuy.smartdevice.tools.HandlerMain(
                                getApplicationContext(), logs) {
                            @Override
                            protected void execute(Context context, int code, String message) {
                                logs.info("Product sync event: [" + code + "] " + message);
                                if (code == 200 && "商品下載成功".equals(message)) {
                                    // 背景預載圖片
                                    new Thread(() -> {
                                        try {
                                            List<ProductStructure> products = MyApp.getInstance().getProductList();
                                            int total = products.size();
                                            logs.info("Starting image preloading for " + total + " products...");

                                            for (int i = 0; i < total; i++) {
                                                ProductStructure p = products.get(i);
                                                if (p.getProductImg() != null && !p.getProductImg().isEmpty()) {
                                                    try {
                                                        new ImageGlide(getApplicationContext()).preload(p.getProductImg());
                                                    } catch (Exception e) {
                                                        logs.info("Failed to preload image for product " + p.getProductID() + ": " + e.getMessage());
                                                    }
                                                }
                                            }

                                            logs.info("Product sync and image preloading completed.");
                                            mqttManager.publishCommandAck(cmdId, "success", "Product sync completed successfully");

                                            new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                                    android.widget.Toast.makeText(context,
                                                            "✅ 商品更新完成", android.widget.Toast.LENGTH_SHORT).show());
                                        } catch (Exception e) {
                                            logs.error(com.unibuy.smartdevice.exception.ErrorCode.UNKNOWN_ERROR, "Error during image preloading: " + e.getMessage());
                                            mqttManager.publishCommandAck(cmdId, "failed", "Image preloading error: " + e.getMessage());
                                        }
                                    }).start();
                                } else if (code >= 400) {
                                    mqttManager.publishCommandAck(cmdId, "failed", "Product sync failed: " + message);
                                    new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                            android.widget.Toast.makeText(context,
                                                    "❌ 商品更新失敗: " + message, android.widget.Toast.LENGTH_LONG).show());
                                }
                            }
                        };

                        // 1. 清除舊圖片快取
                        ImageGlide.clearProductCache(getApplicationContext());
                        // 2. 開始下載資料
                        new com.unibuy.smartdevice.external.StarCloudAPI(productHandler, productHandler).downloadProductInfo();
                    } else if ("update_inventory".equals(cmd)) {
                        logs.info("Executing update_inventory command...");
                        try {
                            int slotNo = -1;
                            int stock = -1;
                            String expiryDate = null;
                            boolean hasExpiryKey = false;
                            String batchNo = null;
                            boolean hasBatchKey = false;
                            boolean isLocked = false;
                            boolean hasLock = false;
                            int lockRev = 0;
                            boolean hasLockRev = false;
                            int expiryRev = 0;
                            boolean hasExpiryRev = false;

                            if (command.getPayload() != null && command.getPayload().isJsonObject()) {
                                com.google.gson.JsonObject payloadObj = command.getPayload().getAsJsonObject();
                                if (payloadObj.has("slot_no"))
                                    slotNo = payloadObj.get("slot_no").getAsInt();
                                if (payloadObj.has("stock"))
                                    stock = payloadObj.get("stock").getAsInt();
                                if (payloadObj.has("expiry_date")) {
                                    hasExpiryKey = true;
                                    if (!payloadObj.get("expiry_date").isJsonNull())
                                        expiryDate = payloadObj.get("expiry_date").getAsString();
                                }
                                if (payloadObj.has("batch_no")) {
                                    hasBatchKey = true;
                                    if (!payloadObj.get("batch_no").isJsonNull())
                                        batchNo = payloadObj.get("batch_no").getAsString();
                                }
                                if (payloadObj.has("is_locked") && !payloadObj.get("is_locked").isJsonNull()) {
                                    hasLock = true;
                                    isLocked = payloadObj.get("is_locked").getAsBoolean();
                                }
                                if (payloadObj.has("lock_rev") && !payloadObj.get("lock_rev").isJsonNull()) {
                                    hasLockRev = true;
                                    lockRev = payloadObj.get("lock_rev").getAsInt();
                                }
                                if (payloadObj.has("expiry_rev") && !payloadObj.get("expiry_rev").isJsonNull()) {
                                    hasExpiryRev = true;
                                    expiryRev = payloadObj.get("expiry_rev").getAsInt();
                                }
                            }

                            if (slotNo <= 0 || stock < 0) {
                                throw new Exception("Invalid parameters: slot_no=" + slotNo + ", stock=" + stock);
                            }

                            // 更新本地資料庫
                            int field = com.unibuy.smartdevice.devices.SlotField.VMC.getField();
                            com.unibuy.smartdevice.structure.SlotStructure slotData = MyApp.getInstance()
                                    .getSlotData(field, slotNo);
                            if (slotData != null) {
                                slotData.setCount(stock);

                                org.json.JSONObject otherData = slotData.getOtherData();
                                if (otherData == null)
                                    otherData = new org.json.JSONObject();

                                // 鎖定 LWW：本地 rev>synced_rev 表示有未上報的現場編輯 → 拒絕被下行覆蓋
                                if (hasLock) {
                                    if (slotData.getLockRev() <= slotData.getLockSyncedRev()) {
                                        slotData.setLock(isLocked);
                                        int r = hasLockRev ? lockRev : slotData.getLockSyncedRev();
                                        slotData.setLockRev(r);
                                        slotData.setLockSyncedRev(r);
                                    } else {
                                        logs.info("update_inventory: 本地有未上報鎖定編輯，略過下行 is_locked slot=" + slotNo);
                                    }
                                }

                                // 效期/批號 LWW（只動「下行確實帶了該鍵」的欄位，避免缺鍵時誤清本地效期）
                                if (hasExpiryRev) {
                                    if (slotData.getExpiryRev() <= slotData.getExpirySyncedRev()) {
                                        try {
                                            if (hasExpiryKey) {
                                                if (expiryDate != null) otherData.put("expiry_date", expiryDate);
                                                else otherData.remove("expiry_date");
                                            }
                                            if (hasBatchKey) {
                                                if (batchNo != null) otherData.put("batch_no", batchNo);
                                                else otherData.remove("batch_no");
                                            }
                                        } catch (org.json.JSONException e) {
                                            logs.info("Failed to update JSON otherData: " + e.getMessage());
                                        }
                                        slotData.setExpiryRev(expiryRev);
                                        slotData.setExpirySyncedRev(expiryRev);
                                    } else {
                                        logs.info("update_inventory: 本地有未上報效期編輯，略過下行 expiry slot=" + slotNo);
                                    }
                                } else {
                                    // 舊版相容：無 expiry_rev，沿用原本「有給就寫」行為
                                    try {
                                        if (expiryDate != null) otherData.put("expiry_date", expiryDate);
                                        if (batchNo != null) otherData.put("batch_no", batchNo);
                                    } catch (org.json.JSONException e) {
                                        logs.info("Failed to update JSON otherData: " + e.getMessage());
                                    }
                                }
                                slotData.setOtherData(otherData);

                                // 單行更新，避免 deleteAll()+insertAll() 全表重寫與 UI 存檔 race
                                com.unibuy.smartdevice.database.SlotsDao slotsDao = new com.unibuy.smartdevice.database.SlotsDao(
                                        getApplicationContext());
                                slotsDao.updateSlot(slotData);
                                slotsDao.close();

                                logs.info("Inventory updated: slot=" + slotNo + ", stock=" + stock);
                                mqttManager
                                        .publishCommandAck(new com.unibuy.smartdevice.structure.mqtt.CommandAckPayload(
                                                cmdId, "success", "Inventory updated successfully",
                                                String.valueOf(stock)));

                                // ✅ 通知前台銷售平台刷新商品列表（輕量級，不重建 Fragment）
                                Intent refreshIntent = new Intent("com.unibuy.smartdevice.ACTION_SLOT_UPDATED");
                                refreshIntent.setPackage(getPackageName());
                                sendBroadcast(refreshIntent);
                            } else {
                                throw new Exception("Slot not found: " + slotNo);
                            }
                        } catch (Exception e) {
                            logs.info("Failed to update inventory: " + e.getMessage());
                            mqttManager.publishCommandAck(cmdId, "failed",
                                    "Update inventory failed: " + e.getMessage());
                        }
                    } else if ("dispense".equals(cmd)) {
                        logs.info("Executing remote dispense command...");
                        try {
                            int slot = -1;
                            if (command.getPayload() != null && command.getPayload().isJsonObject()) {
                                com.google.gson.JsonObject payloadObj = command.getPayload().getAsJsonObject();
                                if (payloadObj.has("slot_no")) {
                                    slot = payloadObj.get("slot_no").getAsInt();
                                } else if (payloadObj.has("slot")) {
                                    slot = payloadObj.get("slot").getAsInt();
                                }
                            }

                            if (slot <= 0) {
                                throw new Exception("Invalid or missing slot_no: " + slot);
                            }

                            int finalSlot = slot;
                            com.unibuy.smartdevice.tools.ProductDispenseManager dispenseManager = new com.unibuy.smartdevice.tools.ProductDispenseManager(
                                    getApplicationContext(),
                                    logs,
                                    new com.unibuy.smartdevice.tools.ProductDispenseManager.DispenseListener() {
                                        @Override
                                        public void onProgress(String message) {
                                            logs.debug("Dispense Progress: " + message);
                                        }

                                        @Override
                                        public void onSuccess() {
                                            if (logs != null) logs.info("Remote dispense success for slot: " + finalSlot + " (cmdId: " + cmdId + ")");
                                            // 依據使用者測試結果：回傳簡潔的 ACK (僅含 command_id 與 result) 即可成功更改狀態
                                            mqttManager.publishCommandAck(new com.unibuy.smartdevice.structure.mqtt.CommandAckPayload(cmdId, "success"));
                                        }

                                        @Override
                                        public void onFailure(String error) {
                                            if (logs != null) logs.info("Remote dispense failed: " + error);
                                            mqttManager.publishCommandAck(new com.unibuy.smartdevice.structure.mqtt.CommandAckPayload(cmdId, "failed"));
                                        }
                                    });

                            dispenseManager.startDispense(slot);
                        } catch (Exception e) {
                            logs.info("Failed to parse or start dispense command: " + e.getMessage());
                            mqttManager.publishCommandAck(cmdId, "failed", "Error: " + e.getMessage());
                        }
                    } else if ("update_app".equals(cmd)) {
                        logs.info("Executing remote app OTA update command...");
                        try {
                            String apkUrl = null;
                            String targetVersion = null;
                            if (command.getPayload() != null && command.getPayload().isJsonObject()) {
                                com.google.gson.JsonObject payloadObj = command.getPayload().getAsJsonObject();
                                if (payloadObj.has("url")) {
                                    apkUrl = payloadObj.get("url").getAsString();
                                }
                                if (payloadObj.has("version") && !payloadObj.get("version").isJsonNull()) {
                                    targetVersion = payloadObj.get("version").getAsString();
                                }
                            }

                            if (apkUrl == null || apkUrl.isEmpty()) {
                                throw new Exception("Missing 'url' parameter in update_app payload.");
                            }

                            // 啟動背景 OTA 更新管理器（帶目標版本，供安裝後比對回報終態）
                            com.unibuy.smartdevice.tools.OtaManager.startUpdate(getApplicationContext(), apkUrl, cmdId, targetVersion);

                        } catch (Exception e) {
                            logs.info("Failed to start remote app update: " + e.getMessage());
                            mqttManager.publishCommandAck(cmdId, "failed", "Error: " + e.getMessage());
                        }
                    } else if ("update_settings".equals(cmd)) {
                        logs.info("Executing remote settings sync command...");

                        // 🚩 忙碌時拒收：交易/購物車/對話框進行中，避免支付開關等設定中途被改動。
                        //    後台 RemoteCommand 約 1 分鐘 timeout，閒置後可重發。
                        String busyReason = MyApp.getInstance().getBusyReason();
                        if (!busyReason.isEmpty()) {
                            logs.info("Remote settings sync rejected: " + busyReason);
                            mqttManager.publishCommandAck(cmdId, "failed", "Machine busy: " + busyReason);
                        } else {
                            // 📢 在主執行緒顯示 Toast 提示使用者
                            new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                    android.widget.Toast.makeText(getApplicationContext(),
                                            "📢 系統設定更新中...", android.widget.Toast.LENGTH_SHORT).show());

                            com.unibuy.smartdevice.tools.HandlerMain settingHandler = new com.unibuy.smartdevice.tools.HandlerMain(
                                    getApplicationContext(), logs) {
                                @Override
                                protected void execute(Context context, int code, String message) {
                                    logs.info("Settings sync event: [" + code + "] " + message);
                                    if (code == 200 && "綁定成功".equals(message)) {
                                        // 1. 先發 ACK，確保在 MQTT 重連之前送達後台
                                        mqttManager.publishCommandAck(cmdId, "success",
                                                "Settings sync completed successfully");

                                        new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                                android.widget.Toast.makeText(context,
                                                        "✅ 系統設定更新完成", android.widget.Toast.LENGTH_SHORT).show());

                                        // 2. 延後重連 MQTT（token 可能已輪換），留時間讓 ACK 先送出
                                        new android.os.Handler(android.os.Looper.getMainLooper())
                                                .postDelayed(() -> MyApp.getInstance().initMqtt(), 1500);
                                    } else if (code >= 400) {
                                        mqttManager.publishCommandAck(cmdId, "failed",
                                                "Settings sync failed: " + message);

                                        new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
                                                android.widget.Toast.makeText(context,
                                                        "❌ 系統設定更新失敗: " + message, android.widget.Toast.LENGTH_LONG).show());
                                    }
                                }
                            };
                            // 只重抓設定（B014），不串接商品/廣告；reconnectMqtt=false，改由上方 ACK 後再重連
                            new com.unibuy.smartdevice.external.StarCloudAPI(settingHandler, settingHandler).downloadSetting(false);
                        }
                    } else {
                        logs.info("Unknown MQTT command: " + cmd);
                        mqttManager.publishCommandAck(cmdId, "failed", "Unknown command");
                    }
                } catch (Exception e) {
                    logs.info("Error executing MQTT command: " + e.getMessage());
                    mqttManager.publishCommandAck(cmdId, "failed", "Error: " + e.getMessage());
                }
            });
            mqttManager.connect(machineId, apiToken);
        }

        // START_STICKY 確保 Service 被系統殺掉後會自動重啟
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        logs.info("MqttService Destroyed");
        if (wakeLock != null && wakeLock.isHeld()) {
            wakeLock.release();
            logs.info("WakeLock released");
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel serviceChannel = new NotificationChannel(
                    CHANNEL_ID,
                    "MQTT Service Channel",
                    NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(serviceChannel);
            }
        }
    }
}
