package com.unibuy.smartdevice.ui.dialog;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.graphics.Color;
import android.os.CountDownTimer;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.work.BackoffPolicy;
import androidx.work.ExistingWorkPolicy;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

import com.unibuy.smartdevice.DialogAbstract;
import com.unibuy.smartdevice.MyApp;
import com.unibuy.smartdevice.R;
import com.unibuy.smartdevice.controller.DevController;
import com.unibuy.smartdevice.controller.DevDigitalDisplayController;
import com.unibuy.smartdevice.controller.DevElectricController;
import com.unibuy.smartdevice.database.SlotsDao;
import com.unibuy.smartdevice.database.MemoDao;

import com.unibuy.smartdevice.databinding.DialogDispatchBinding;
import com.unibuy.smartdevice.devices.ComPort;
import com.unibuy.smartdevice.devices.PortTools;
import com.unibuy.smartdevice.devices.SlotField;
import com.unibuy.smartdevice.exception.ErrorCode;
import com.unibuy.smartdevice.exception.LogsEmptyException;
import com.unibuy.smartdevice.exception.LogsIOException;
import com.unibuy.smartdevice.exception.LogsParseException;
import com.unibuy.smartdevice.exception.LogsSecurityException;
import com.unibuy.smartdevice.exception.LogsSettingEmptyException;
import com.unibuy.smartdevice.exception.LogsUnsupportedOperationException;
import com.unibuy.smartdevice.external.HttpAPI;
import com.unibuy.smartdevice.external.mqtt.MqttManager;
import com.unibuy.smartdevice.structure.BuyStructure;
import com.unibuy.smartdevice.structure.DispatchStructure;
import com.unibuy.smartdevice.structure.ProductStructure;
import com.unibuy.smartdevice.structure.ReportDispatchInfoStructure;
import com.unibuy.smartdevice.structure.ReportFreeGiftCodeStructure;
import com.unibuy.smartdevice.structure.SlotStructure;
import com.unibuy.smartdevice.structure.MemoStructure;
import com.unibuy.smartdevice.tools.HandlerMainCountdown;
import com.unibuy.smartdevice.tools.HandlerMain;
import com.unibuy.smartdevice.tools.ToastHandlerMain;
import com.unibuy.smartdevice.tools.TransactionFinalizeBuilder;
import com.unibuy.smartdevice.ui.FontendActivity;
import com.unibuy.smartdevice.ui.recycler.RecyclerDialogDispatchListAdpter;
import com.unibuy.smartdevice.ui.tools.ImageGlide;
import com.unibuy.smartdevice.ui.tools.StringRefactoring;
import com.unibuy.smartdevice.worker.UploadLogOneTimeWorker;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class DispatchDialog extends DialogAbstract {
    public enum Option {
        TOAST(0),
        COUNTDOWN_CANCEL(1),
        SET_COUNTDOWN_TEXT(2),
        NEXT_PRODUCT(3),
        ENABLED_TRUE(4),
        ENABLED_FALSE(5),
        REPORT_DISPATCH(8),
        SET_MESSAGE_TEXT(9),
        CURRENTLY_PROCESSING_GOODS(71),
        SHIPPED_PROCESSING_GOODS(72),
        NEXT_PROCESS_GOODS(73),
        ERROR_PROCESS_GOODS(74),
        ;

        private int option;

        public int getOption() {
            return option;
        }

        Option(int option) {
            this.option = option;
        }
    }

    public static final Map<Integer, Option> optionMap = new HashMap<>();
    static {
        for (Option option : Option.values()) {
            optionMap.put(option.getOption(), option);
        }
    }

    @Override
    protected Context setCtx() {
        return getContext();
    }

    @Override
    protected Class<? extends DialogAbstract> setCls() {
        return getClass();
    }

    @Override
    protected HandlerMain setHandlerMain() {
        return new ToastHandlerMain(getCtx(), getLogs()) {

            @Override
            protected void execute(Context context, int commandCode, String message) {
                if (!optionMap.containsKey(commandCode)) {
                    getLogs().debug("key not found:" + commandCode);
                    return;
                }

                Option option = optionMap.get(commandCode);

                switch (option) {
                    case TOAST:
                        super.execute(context, commandCode, message);
                        break;
                    case COUNTDOWN_CANCEL:
                        cancelOnThreadCountdown();
                        break;
                    case SET_COUNTDOWN_TEXT:
                        if (isFinalizing)
                            break; // 結案倒數中，忽略舊的倒數事件
                        setButtonOkText(Integer.parseInt(message));
                        // 💡 徹底閹割背景防護計時器對 TextView 倒數文字的修改，防堵假倒數與 14 秒閃跳 Bug！
                        break;
                    case NEXT_PRODUCT:
                        if (isFinalizing)
                            break; // 結案倒數中，不再重新啟動出貨
                        currentlyProcessingGoods();
                        break;
                    case ENABLED_TRUE:
                        if (isFinalizing)
                            break; // 結案倒數中，不改變按鈕狀態
                        openButtonOk();
                        break;
                    case ENABLED_FALSE:
                        if (isFinalizing)
                            break; // 結案倒數中，不改變按鈕狀態
                        closeButtonOk();
                        break;
                    case REPORT_DISPATCH:
                        // [已停用] 舊的 B602 HTTP 分段上報，改由 MQTT Finalize 統一結案
                        // 此事件仍保留以維持向下相容，但不執行任何 HTTP 呼叫
                        getLogs().debug("REPORT_DISPATCH event received but skipped (MQTT Finalize mode)");
                        break;
                    case SET_MESSAGE_TEXT:
                        setTextViewSmall(message);
                        break;
                    case CURRENTLY_PROCESSING_GOODS:
                        if (isFinalizing)
                            break; // 結案倒數中，不再處理
                        currentlyProcessingGoods();
                        break;
                    case SHIPPED_PROCESSING_GOODS:
                        if (isFinalizing)
                            break; // 結案倒數中，不再處理
                        shippedProcessingGoods();
                        break;
                    case NEXT_PROCESS_GOODS:
                        if (isFinalizing)
                            break; // 結案倒數中，不再處理
                        nextProcessGoods();
                        break;
                    case ERROR_PROCESS_GOODS:
                        if (!buttonControlCountdown.isShutdown()) {
                            buttonControlCountdown.shutdown();
                        }
                        buttonControlCountdown.start(10);

                        closeDialogCountdown.shutdown();
                        setTextViewSmall(message);
                        setButtonOkText(0);

                        // 顯示錯誤圖片
                        binding.linearLayout4.setVisibility(View.GONE);
                        binding.imgDeliveryFailed.setVisibility(View.VISIBLE);
                        binding.tvCountdown.setVisibility(View.VISIBLE);
                        // 啟動倒數計時
                        startCountdown();

                        getLogs().info("✅ 出貨到一半失敗");
                        deliverylot(0);// 出貨失敗，寫入log
                        getLogs().info("📦 出貨狀況總結：");
                        for (String logItem : deliveryLogs) {
                            getLogs().info(logItem);
                        }
                        finalizeFailedDispatch(message);

                        new Handler().postDelayed(() -> {
                            Context ctx = getContext();
                            Context base = ctx;

                            while (base instanceof ContextWrapper && !(base instanceof Activity)) {
                                base = ((ContextWrapper) base).getBaseContext();
                            }

                            if (base instanceof Activity) {
                                ReSetAPPDispatch(base);
                                getLogs().info("DispatchDialog --- 無出貨商品，重啟 APP");
                            } else {
                                getLogs().info("DispatchDialog --- 找不到 Activity，無法重啟 APP");
                            }
                        }, 15000);
                        break;
                }
            }
        };
    }

    private DialogDispatchBinding binding;
    private RecyclerDialogDispatchListAdpter recyclerDialogDispatchListAdpter;
    private CloseDialogOnThreadHandler closeDialogCountdown;
    private List<DispatchStructure> dispatchList;
    private int dispatchIndex;
    private boolean isReportDispatch;
    private boolean isFinalizing = false; // 標記是否正在進行最後的取貨倒數結案
    private boolean isChangeStatus;
    private int countdownNumber = 40;
    private CountDownTimer countDownTimer;
    private TextView tvCountdown;
    private HandlerMainCountdown buttonControlCountdown;
    private HttpAPI httpAPI;
    private final List<String> deliveryLogs = new ArrayList<>();
    /** MQTT Finalize 模式：累積本次交易的所有出貨記錄，最後統一送出 */
    private TransactionFinalizeBuilder finalizeBuilder;

    public DispatchDialog(Context context, HandlerMain srcHandlerMain) {
        super(context, srcHandlerMain);
        dispatchList = new ArrayList<>();
        dispatchIndex = 0;
        isReportDispatch = true;
        isChangeStatus = true;
    }

    @Override
    protected void onCreate(Context context) {
        binding = DialogDispatchBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        isReportDispatch = true;
        tvCountdown = binding.tvCountdown;
        httpAPI = new HttpAPI(getHandlerMain());
        // 初始化 Finalize Builder（在支付完成、進入出貨流程時建立）
        finalizeBuilder = new TransactionFinalizeBuilder();
        if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(42);
        }

        MyApp.getInstance().setReportFreeGiftCodeStructure(new ReportFreeGiftCodeStructure("", 0, "", 0, 0, ""));

        getLogs().info("BuyList size:" + MyApp.getInstance().getBuyList().size());
        // ✅ 在這裡確保 dispatchList 在每次創建時都被清空，避免舊數據干擾
        this.dispatchList.clear();

        List<BuyStructure> buyList = MyApp.getInstance().getBuyList();
        if (buyList.size() > 1) {
            getLogs().info("✅ BuyList 有多筆，將依序連續出貨。");
        }

        for (BuyStructure buyStructure : buyList) {
            getLogs().info(buyStructure.toString());
            if (!buyStructure.getProduct().getMaterialCode().isEmpty()
                    && buyStructure.getProduct().getMaterialCode().contains(":")) {
                String[] materialCodes = buyStructure.getProduct().getMaterialCode().split(":");
                String productId = materialCodes[0];
                int count = Integer.parseInt(materialCodes[1]);
                try {
                    SlotStructure slotData = MyApp.getInstance().getSlotData(buyStructure.getField(),
                            buyStructure.getSlot());
                    if (slotData.getProduct().getProductID().equals(productId)) {
                        DispatchStructure dispatchData = new DispatchStructure(buyStructure.getField(),
                                buyStructure.getSlot(), slotData.getProduct(), count * buyStructure.getCount());
                        dispatchData.setFreeGift(buyStructure.isFreeGift());
                        // ✅ 這裡的 buyStructure.getCount() 現在會是正確的 QTY
                        this.dispatchList.add(dispatchData);
                    }
                } catch (LogsEmptyException e) {
                    throw new RuntimeException(e);
                    // 很嚴重的錯誤
                }
            } else {
                DispatchStructure dispatchData = new DispatchStructure(buyStructure.getField(), buyStructure.getSlot(),
                        buyStructure.getProduct(), buyStructure.getCount());
                dispatchData.setFreeGift(buyStructure.isFreeGift());
                // ✅ 這裡的 buyStructure.getCount() 現在會是正確的 QTY
                this.dispatchList.add(dispatchData);
            }
            /* 應出貨明細 */
            String ptId = buyStructure.getProduct().getProductID();
            String pName = buyStructure.getProduct().getProductName();
            int pcount = buyStructure.getCount();
            deliveryLogs.add("▲ 應出貨明細 - " + ptId + " / " + pName + " / 數量 " + pcount);

        }

        getLogs().info("DispatchList size:" + this.dispatchList.size());

        if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
            try {
                String patName = "未知患者";
                String sn = "";
                String drgNo = "";

                // 優先從全域解密資料讀取（最完整，包含姓名與領藥號）
                String decryptedStr = MyApp.getInstance().getDecryptedData();
                if (decryptedStr != null && !decryptedStr.isEmpty()) {
                    JSONObject fullJson = new JSONObject(decryptedStr);
                    patName = fullJson.optString("NAME", "");
                    if (patName.isEmpty())
                        patName = fullJson.optString("PATNAME", "");
                    if (patName.isEmpty())
                        patName = fullJson.optString("patName", "");
                    if (patName.isEmpty())
                        patName = "未知患者";

                    sn = fullJson.optString("SN", "");
                    drgNo = fullJson.optString("DRGNO", "");
                }

                // 如果全域資料讀不到，嘗試從商品行銷計劃 (marketingPlan) 讀取 (備用防呆)
                if (sn.isEmpty() && this.dispatchList != null && !this.dispatchList.isEmpty()) {
                    String marketingPlan = this.dispatchList.get(0).getProduct().getMarketingPlan();
                    if (marketingPlan != null && !marketingPlan.isEmpty()) {
                        JSONObject rxJson = new JSONObject(marketingPlan);
                        sn = rxJson.optString("SN", "");
                        if (drgNo.isEmpty()) {
                            drgNo = rxJson.optString("DRGNO", "");
                        }
                    }
                }

                // 按照最新要求：[患者姓名]([藥單號]) 用括號
                String barcodeInfo = patName + "(" + drgNo + ")";
                getLogs().info(
                        "✅ 中國醫 QR Code 取物單資訊打包上報：姓名 " + patName + ", 藥單號 " + drgNo + " -> barcodeInfo: " + barcodeInfo);

                // 設定到 flowBarCode (會連動到後台 member_barcode 欄位)
                MyApp.getInstance().getReportFlowInfoData().setFlowBarCode(barcodeInfo);

                // CMUH: flow_id 保留 App 機台流水號；order_no 使用取物單序號 SN 方便後台對藥單。
                if (sn != null && !sn.isEmpty()) {
                    finalizeBuilder.setOrderNo(sn);
                    MyApp.getInstance().getReportFlowInfoData().setOrderId(sn);
                    getLogs().info("✅ 中國醫銷貨紀錄 order_no 已設定為取物單序號 (SN)：" + sn);
                }
            } catch (Exception e) {
                getLogs().warning(e);
            }
        }

        this.recyclerDialogDispatchListAdpter = new RecyclerDialogDispatchListAdpter(getCtx(), this.dispatchList,
                getHandlerMain());
        this.binding.recyclerDialogDispatchList.setAdapter(recyclerDialogDispatchListAdpter);
        this.binding.recyclerDialogDispatchList.setLayoutManager(new LinearLayoutManager(context));

        this.binding.buttonOk.setOnClickListener(v -> {
            if (isFinalizing) {
                // 結案倒數中，按下 = 提前確認取貨，關閉 dialog
                if (countDownTimer != null) {
                    countDownTimer.cancel();
                }
                dialogCancel();
            } else {
                currentlyProcessingGoods();
            }
        });

        // 出貨過程中隱藏按鈕，等到 15 秒取貨倒數再顯示
        binding.buttonOk.setVisibility(View.GONE);
        setButtonOkText(0);

        closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain());
        buttonControlCountdown = new HandlerMainCountdown(getHandlerMain()) {
            @Override
            protected Class<?> setCls() {
                return getClass();
            }

            @Override
            protected HandlerMain setHandlerMain() {
                return getSrcHandlerMain();
            }

            @Override
            protected void execute(long countdown, HandlerMain handlerMain) {
                if (countdown < 2) {
                    handlerMain.start("devShippedProduct", Option.ENABLED_TRUE.getOption(), "enabled true");
                }
            }

            @Override
            protected void close(HandlerMain handlerMain) {
                handlerMain.start("devShippedProduct", Option.ENABLED_TRUE.getOption(), "enabled true");
            }
        };

        currentlyProcessingGoods();
    }

    private void startCountdown() {
        countDownTimer = new CountDownTimer(15_000, 1_000) { // 10 秒，每秒 tick 一次
            public void onTick(long millisUntilFinished) {
                int seconds = (int) (millisUntilFinished / 1000);
                tvCountdown.setText("（" + seconds + "s）後關閉");
            }

            public void onFinish() {
                tvCountdown.setText("（0s）後關閉");
            }
        }.start();
    }

    public void closeButtonOk() {
        getLogs().info("關閉按鈕");
        binding.buttonOk.setTextColor(Color.GRAY);
        binding.buttonOk.setEnabled(false);
        binding.buttonOk.setTextColor(Color.GRAY);
        binding.buttonOk.setEnabled(false);
    }

    public void openButtonOk() {
        getLogs().info("開啟按鈕");
        binding.buttonOk.setTextColor(Color.BLACK);
        binding.buttonOk.setEnabled(true);
        binding.buttonOk.setTextColor(Color.BLACK);
        binding.buttonOk.setEnabled(true);
    }

    public void dialogCancel() {
        closeDialogCountdown.shutdown();
        int buyListSize = MyApp.getInstance().getBuyList().size();
        MyApp.getInstance().getBuyList().clear();

        if (buyListSize > 0) {
            recyclerDialogDispatchListAdpter.notifyItemRangeRemoved(0, buyListSize);
        }
        cancel();
    }

    public void cancelOnThreadCountdown() {
        MyApp.getInstance().getBuyList().clear();
        cancel();
    }

    private boolean isDispatchingNext = false;
    private boolean isCheckSlotByVMC = false;
    private int checkSlotRunCount = 0;

    public class DevCheckSlotByVMC extends DevController.ReadBufferOnScheduler {
        private int slot;

        public DevCheckSlotByVMC(HandlerMain handlerMain, int slot) {
            super(handlerMain);

            this.slot = slot;
        }

        @Override
        protected HandlerMain setHandlerMain() {
            return getSrcHandlerMain();
        }

        @Override
        protected Class<?> setCls() {
            return getClass();
        }

        @Override
        public void readBufferOnScheduler(DevController devController, HandlerMain handlerMain) {
            if (isCheckSlotByVMC)
                return;
            getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA006");
            isCheckSlotByVMC = true; // 進來立即設定true，可以判斷掉重複出貨

            boolean isCMD = false;
            boolean isStatus = false;
            String errorDetail = "";

            for (byte[] buffer : devController.getReadBufferByMax()) {
                getLogs().info("🔍 checkSlot 收到原始 Buffer: " + PortTools.showHex(buffer));
                deliveryLogs.add("🔍 checkSlot 收到: " + PortTools.showHex(buffer));

                if (buffer[0] == 0x02) {
                    getLogs().info("XinYuan Read:" + PortTools.showHex(buffer));

                    isCMD = true;
                    if (buffer[1] == 0x01 || buffer[1] == 0x02 || buffer[1] == 0x04) {
                        isStatus = true;
                        break;
                    } else {
                        errorDetail = String.format("%02X%02X", buffer[0], buffer[1]);
                    }
                }
            }

            getLogs().info("vmc isCMD:" + isCMD + " isStatus:" + isStatus);

            if (isCMD) {
                if (isStatus) {
                    getLogs().info("-----------------商品出貨中-----------------");
                    setTextViewCountdown("商品出貨中", 65);
                    MyApp.getInstance().getDevXinYuanController()
                            .getReadBufferByNow(new DevGetProductByVMC(handlerMain, slot), 5000);
                    MyApp.getInstance().getDevXinYuanController().addSendBuffer(
                            MyApp.getInstance().getDevXinYuanController().getDevXinYuan().getProduct(slot));
                } else {
                    if (!errorDetail.isEmpty()) {
                        MqttManager.getInstance().publishError(slot, errorDetail);
                    }
                    buttonControlCountdown.shutdown();
                    getSrcHandlerMain().start(getClass().getSimpleName(), Option.ERROR_PROCESS_GOODS.getOption(),
                            getCtx().getString(
                                    R.string.check_whether_goods_have_been_taken_out_before_confirming_collection));// 請檢查商品有無取出再確認取貨
                }
            } else {
                boolean isCmuh = "Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project);
                int maxRetries = isCmuh ? 10 : 3;
                if (checkSlotRunCount >= maxRetries) {
                    buttonControlCountdown.shutdown();
                    if (isCmuh) {
                        getLogs().info("機台未回應，已達 Cmuh 最大重試次數 10 次，嘗試重新連線下位機");
                        MyApp.getInstance().getDevXinYuanController().start();
                        if (!MyApp.checkAndReconnectDev()) {
                            getLogs().info("無法連線到下位機，出貨作業中止");
                        }
                        getSrcHandlerMain().start(getClass().getSimpleName(), Option.ERROR_PROCESS_GOODS.getOption(),
                                "領藥有問題 請等3秒後再重掃");
                    } else {
                        getSrcHandlerMain().start(getClass().getSimpleName(), Option.ERROR_PROCESS_GOODS.getOption(),
                                getCtx().getString(R.string.slave_computer_did_not_respond_wait_3_seconds_try_again));
                        MyApp.getInstance().getDevXinYuanController().start();
                    }
                    checkSlotRunCount = 0;
                } else {
                    getLogs().info("機台未回應，第 " + (checkSlotRunCount + 1) + " 次重試 checkSlot..."
                            + (isCmuh ? " (溫和模式，不發送指令)" : ""));
                    isCheckSlotByVMC = false; // 重置旗標，允許下次回調進入
                    checkSlotRunCount++;
                    MyApp.getInstance().getDevXinYuanController()
                            .getReadBufferByNow(new DevCheckSlotByVMC(handlerMain, slot));
                    if (!isCmuh) {
                        MyApp.getInstance().getDevXinYuanController().addSendBuffer(
                                MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slot));
                    }
                }
            }
        }
    }

    private boolean isGetProductByVMC = false;
    private int getProductRunCount = 0;

    public class DevGetProductByVMC extends DevController.ReadBufferOnScheduler {
        private int slot;

        public DevGetProductByVMC(HandlerMain handlerMain, int slot) {
            super(handlerMain);

            this.slot = slot;
        }

        @Override
        protected HandlerMain setHandlerMain() {
            return getSrcHandlerMain();
        }

        @Override
        protected Class<?> setCls() {
            return getClass();
        }

        @Override
        public void readBufferOnScheduler(DevController devController, HandlerMain handlerMain) {
            if (isGetProductByVMC)
                return;

            boolean isCMD = false;
            boolean inTheMiddle = false;
            boolean isSuccess = false;
            String errorDetail = "";

            for (byte[] buffer : devController.getReadBufferByMax()) {
                getLogs().info("🔍 getProduct 收到原始 Buffer: " + PortTools.showHex(buffer));
                deliveryLogs.add("🔍 getProduct 收到: " + PortTools.showHex(buffer));

                if (buffer[0] == 0x04) {
                    getLogs().info("XinYuan Read:" + PortTools.showHex(buffer));

                    isCMD = true;

                    // if (buffer[1] == 0x01) {
                    // inTheMiddle = true;
                    // isSuccess = true;
                    // break;
                    // }

                    if (buffer[1] == 0x01 || buffer[1] == 0x10 || buffer[1] == 0x11) {
                        inTheMiddle = true;
                        continue;
                    }

                    if (buffer[1] == 0x02 || buffer[1] == 0x24) {
                        inTheMiddle = true;
                        isSuccess = true;
                        break;
                    }

                    // If it's a known error code
                    errorDetail = String.format("04%02X", buffer[1]);
                }
            }

            getLogs().info("vmc isCMD:" + isCMD + " inTheMiddle:" + inTheMiddle + " isSuccess:" + isSuccess);

            if (isCMD) {
                if (inTheMiddle) {
                    if (isSuccess) {
                        // ✅ 立即設置為 true，防止重複觸發
                        if (isGetProductByVMC) {
                            getLogs().info("⚠ DevGetProductByVMC 重複收到成功訊號，忽略");
                            return;
                        }
                        isGetProductByVMC = true;

                        if (dispatchIndex >= 0 && dispatchIndex < dispatchList.size()) {
                            DispatchStructure dispatch = dispatchList.get(dispatchIndex);
                            if (dispatch.isGetProductComplete()) {
                                getLogs().info("⚠ 已標記完成出貨，不再觸發");
                                return;
                            }
                            dispatch.setGetProductComplete(true);
                        }
                        if (dispatchIndex >= 0 && dispatchIndex < dispatchList.size()) {
                            DispatchStructure dispatch = dispatchList.get(dispatchIndex);

                            // ✅ 防呆：若已完成出貨，忽略這次訊號
                            if (dispatch.getCount() >= dispatch.getQuantity()) {
                                getLogs().info("⚠ DevGetProductByVMC 重複收到成功訊號，但已出貨完畢，忽略");
                                return;
                            }
                        }
                        getLogs().info("✅ 出貨成功，觸發 nextProcessGoods");
                        deliverylot(1);// 出貨成功，寫入出貨狀況的LOG
                        getSrcHandlerMain().send(getClass().getSimpleName(), Option.NEXT_PROCESS_GOODS.getOption(),
                                "next process goods");
                    } else {
                        getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA002");
                        MyApp.getInstance().getDevXinYuanController()
                                .getReadBufferByNow(new DevGetProductByVMC(handlerMain, slot), 5000L);
                    }
                } else {
                    if (!errorDetail.isEmpty()) {
                        MqttManager.getInstance().publishError(slot, errorDetail);
                    }
                    buttonControlCountdown.shutdown();
                    getSrcHandlerMain().send(getClass().getSimpleName(), Option.ERROR_PROCESS_GOODS.getOption(),
                            getCtx().getString(R.string.product_delivery_issues_contact_the_counter_window));
                }
            } else {
                getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA003");
                MyApp.getInstance().getDevXinYuanController()
                        .getReadBufferByNow(new DevGetProductByVMC(handlerMain, slot), 5000L);
                MyApp.getInstance().getDevXinYuanController()
                        .addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slot));
            }
        }
    }

    private void saveData() {
        SlotsDao slotsDao = new SlotsDao(getCtx());
        slotsDao.deleteAll();
        slotsDao.insertAll(MyApp.getInstance().getSlotMap());
        slotsDao.close();
        getLogs().info("save count:" + MyApp.getInstance().getSlotList(SlotField.VMC.getField()).size());
    }

    public void currentlyProcessingGoods() { // 出貨前動作，UI與資料變更
        getLogs().info("----- currentlyProcessingGoods -----");

        if (this.dispatchIndex == -1) { // 最後一次確定完後沒有貨要出就關閉視窗
            dialogCancel();
        }

        if (this.dispatchList != null && !this.dispatchList.isEmpty() && this.dispatchIndex > -1) {
            DispatchStructure dispatch = this.dispatchList.get(this.dispatchIndex);
            int field = dispatch.getField();
            int slot = dispatch.getSlot();
            int count = dispatch.getCount();
            int quantity = dispatch.getQuantity();
            boolean isShipped = dispatch.isShipped(); // 表記是否是正在出貨
            int increase = 1; // 機台要出幾個貨？

            try {
                // 出貨數量
                if (SlotField.getSlotField(field).isCanOneShipped()) {
                    increase = 1;
                } else {
                    increase = quantity;
                }
            } catch (LogsEmptyException e) {
                increase = 1;
            }

            ProductStructure product = dispatch.getProduct();

            if (!isShipped) {
                isShipped = true;
                this.dispatchList.get(this.dispatchIndex).setShipped(true);
                this.recyclerDialogDispatchListAdpter.notifyItemChanged(this.dispatchIndex);
            }

            getLogs().info("isShipped:" + isShipped);
            getLogs().info("dispatchIndex:" + dispatchIndex + " dispatchList:" + dispatchList.size());
            getLogs().info("count:" + count + " quantity:" + quantity);

            if (isShipped) { // 正在要出貨的話
                if (count < quantity) { // count 目前出了多少貨 Quantity 總貨物數量
                    // 設定商品名稱和圖片
                    binding.textProductName.setText(product.getProductName());

                    try {
                        new ImageGlide(getContext()).fileload(product.getProductImg(), binding.imageProductPicture);
                    } catch (LogsEmptyException e) {
                        getLogs().warning(e);
                    }

                    setTextViewCountdown(getCtx().getString(R.string.preparing_to_dispatch), 65);
                    isGetProductByVMC = false;
                    isCheckSlotByVMC = false;
                    checkSlotRunCount = 0;
                    getProductRunCount = 0;
                    isChangeStatus = true;

                    getHandlerMain().start(getClass().getSimpleName(), Option.SHIPPED_PROCESSING_GOODS.getOption(),
                            "shipped processing goods");
                }
            }
        }
    }

    // devShippedProduct(field, slot, increase);
    // 出貨中動作
    public void shippedProcessingGoods() {
        getLogs().info("----- shippedProcessingGoods -----");

        if (this.dispatchList != null && !this.dispatchList.isEmpty() && this.dispatchIndex > -1) {
            DispatchStructure dispatch = this.dispatchList.get(this.dispatchIndex);
            int field = dispatch.getField();
            int slot = dispatch.getSlot();
            int count = dispatch.getCount();
            int quantity = dispatch.getQuantity();
            boolean isShipped = dispatch.isShipped(); // 表記是否是正在出貨
            int increase = 1; // 機台要出幾個貨？

            try {
                // 出貨數量
                if (SlotField.getSlotField(field).isCanOneShipped()) {
                    increase = 1;
                } else {
                    increase = quantity;
                }
            } catch (LogsEmptyException e) {
                increase = 1;
            }

            ProductStructure product = dispatch.getProduct();

            getLogs().info("isShipped:" + isShipped);
            getLogs().info("dispatchIndex:" + dispatchIndex + " dispatchList:" + dispatchList.size());
            getLogs().info("count:" + count + " quantity:" + quantity);

            if (isShipped) { // 正在要出貨的話
                // 控制倒數計時，先倒數再出貨
                try {
                    SlotField slotField = SlotField.getSlotField(field);
                    if (slotField.isAutoNextProduct()) {
                        if (!closeDialogCountdown.isShutdown()) {
                            closeDialogCountdown.shutdown();
                        }
                        closeDialogCountdown.start(countdownNumber);
                    } else {
                        setButtonOkText(0);
                    }
                } catch (LogsEmptyException e) {
                    setButtonOkText(0);
                }

                if (!buttonControlCountdown.isShutdown()) {
                    buttonControlCountdown.shutdown();
                }
                closeButtonOk();
                devRunVMC(field, slot, increase);
                // try {
                // closeButtonOk();
                // SlotField slotField = SlotField.getSlotField(field);
                // switch (slotField) {
                // case VMC:
                // devRunVMC(field, slot, increase);
                // break;
                // case ELECTRIC:
                // try {
                // devRunElectric(field, slot, increase);
                // } catch (LogsSecurityException | LogsUnsupportedOperationException |
                // LogsIOException e) {
                // getLogs().warning(e);
                // }
                //
                // break;
                // }
                // } catch (LogsEmptyException e) {
                // getLogs().warning(e);
                // }
            }
        } else {
            dialogCancel();
        }

        getLogs().info("index:" + this.dispatchIndex);
    }

    private void nextProcessGoods() { // 出貨後動作，索引變更和上傳資料
        getLogs().info("----- nextProcessGoods -----");

        // ✅ 在方法開頭檢查 dispatchIndex 的有效性
        if (this.dispatchList == null || this.dispatchList.isEmpty() || this.dispatchIndex < 0
                || this.dispatchIndex >= this.dispatchList.size()) {
            getLogs().info(
                    "⚠ nextProcessGoods: dispatchList 為空或 dispatchIndex 越界，直接返回。dispatchIndex=" + this.dispatchIndex);
            isDispatchingNext = false; // 確保重置鎖
            isGetProductByVMC = false; // 確保重置鎖
            return;
        }

        if (isDispatchingNext) {
            getLogs().info("🚫 已處理過 nextProcessGoods，忽略重複觸發");
            return;
        }
        isDispatchingNext = true; // ✅ 加鎖

        DispatchStructure dispatch = this.dispatchList.get(this.dispatchIndex);
        int field = dispatch.getField();
        int slot = dispatch.getSlot();
        int count = dispatch.getCount();
        int quantity = dispatch.getQuantity();
        boolean isShipped = dispatch.isShipped(); // 表記是否是正在出貨
        int increase = 1; // 機台要出幾個貨？
        int remaining = 0;// 計算貨道剩餘數量

        // ✅ 防呆：如果這筆出貨已完成，不應再進入出貨流程
        if (count >= quantity) {
            getLogs().info("⚠ dispatch 已出貨完成，忽略重複 nextProcessGoods");
            isDispatchingNext = false;
            isGetProductByVMC = false;
            return;
        }

        // 🔽 保持原本 nextProcessGoods 原樣繼續執行...
        try {
            // 出貨數量
            if (SlotField.getSlotField(field).isCanOneShipped()) {
                increase = 1;
            } else {
                increase = quantity;
            }
        } catch (LogsEmptyException e) {
            increase = 1;
        }
        ProductStructure product = dispatch.getProduct();

        getLogs().info("isShipped:" + isShipped);
        getLogs().info("dispatchIndex:" + dispatchIndex + " dispatchList:" + dispatchList.size());
        getLogs().info("count:" + count + " quantity:" + quantity);

        if (isShipped) { // 正在要出貨的話，出貨完成後
            if (count < quantity) { // count 目前出了多少貨 Quantity 總貨物數量
                try {
                    if (SlotField.getSlotField(field).isCanOneShipped()) { // 一個出貨還是一次出貨
                        count += 1; // 目前出貨量+將要出1個
                    } else {
                        count = quantity; // 要出全部
                    }
                } catch (LogsEmptyException e) {
                    count += 1;
                }

                this.dispatchList.get(this.dispatchIndex).setCount(count);
                this.recyclerDialogDispatchListAdpter.notifyItemChanged(this.dispatchIndex);
            }

            try {
                int slotCount = MyApp.getInstance().getSlotData(field, slot).getCount();
                remaining = slotCount - increase; // 計算剩餘數量
                MyApp.getInstance().getSlotData(field, slot).setCount(remaining);
                MyApp.getInstance().getReportDispatchInfoData().setRemaining(remaining);
            } catch (LogsEmptyException e) {
                getLogs().warning(e);
            }

            /*
             * 設定回傳到後台系統資料
             * 1)將原來的ProductId欄位，由醫令代碼修改為原單的ProductId代碼
             * 2)flowtype 直接固定寫入42，代表QRCode取貨，中國醫專用
             * 3)dispatchStatusType欄位寫入0，代表成功
             */
            int price = product.getRealPrice() * count;
            // [MQTT Finalize 模式] 累積出貨成功記錄，最後統一送出
            int productIdInt = 0;
            try {
                productIdInt = Integer.parseInt(product.getProductID());
            } catch (Exception ignored) {
            }
            finalizeBuilder.addDispenseRecord(slot, productIdInt, price * increase, 0, true, remaining);
            getLogs().info(
                    "[Finalize] 已累積出貨記錄: slot=" + slot + ", product=" + productIdInt + ", remaining=" + remaining);

            getLogs().info("count:" + count + " quantity:" + quantity);

            if (count == quantity) { // 貨物是否已經出貨完畢
                // 偵測是否還有下個出貨商品
                if (this.dispatchIndex >= this.dispatchList.size() - 1) { // 沒有貨
                    // ✅ 所有商品出貨完畢，發送 MQTT Finalize 結案訊息
                    if (isFinalizing)
                        return; // 防呆：如果已經在結案倒數了，直接跳出
                    isFinalizing = true;

                    getLogs().info("所有商品出貨完畢，發送 MQTT Finalize 結案訊息。");
                    if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
                        insertDrgnoToMemo();
                    }
                    MqttManager.getInstance().publishTransactionFinalize(finalizeBuilder.build());
                    getSrcHandlerMain().send(getClass().getSimpleName(), FontendActivity.Option.RESET_DATA.getOption(),
                            "update data");
                    saveData();

                    getLogs().info("📦 出貨狀況總結：");
                    for (String logItem : deliveryLogs) {
                        getLogs().info(logItem);
                    }

                    // 停止所有計時器
                    closeDialogCountdown.shutdown();
                    if (!buttonControlCountdown.isShutdown()) {
                        buttonControlCountdown.shutdown();
                    }
                    if (countDownTimer != null) {
                        countDownTimer.cancel();
                    }

                    // 隱藏底部倒數文字
                    binding.tvCountdown.setVisibility(View.GONE);

                    // 顯示取貨確認按鈕（讓使用者可以提前確認取貨） -> 依需求隱藏該按鈕
                    binding.buttonOk.setVisibility(View.GONE);
                    binding.buttonOk.setEnabled(false);
                    binding.buttonOk.setTextColor(Color.BLACK);
                    setButtonOkText(0); // 顯示「取貨確認」不帶倒數

                    // 顯示取貨倒數 15 秒後自動關閉對話框
                    isChangeStatus = true;
                    // 💡 統一都有括號，消除初始與倒數的格式不一致閃爍
                    setTextViewCountdown(
                            getCtx().getString(R.string.pickup_port_open) + "(15)"
                                    + getCtx().getString(R.string.pick_up_within),
                            65);
                    countDownTimer = new CountDownTimer(15_000, 1_000) {
                        public void onTick(long millisUntilFinished) {
                            int seconds = (int) (millisUntilFinished / 1000);
                            setTextViewCountdown(seconds);
                        }

                        public void onFinish() {
                            dialogCancel();
                        }
                    }.start();
                    getLogs().info("📢 出貨完畢，取貨倒數 15 秒後關閉對話框");

                    isDispatchingNext = false;
                    isGetProductByVMC = false;
                    return; // 結束方法
                } else { // 有下一個商品要出了
                    getLogs().info("📦 還有商品要出，啟動精準 15 秒取貨時間後再出下一個商品");

                    // 1. 停止原本在出貨中啟動的 40 秒防護計時器，防堵背景干擾
                    closeDialogCountdown.shutdown();
                    if (!buttonControlCountdown.isShutdown()) {
                        buttonControlCountdown.shutdown();
                    }
                    if (countDownTimer != null) {
                        countDownTimer.cancel();
                    }

                    // 2. 表記下一個商品要出貨
                    this.dispatchIndex += 1;

                    // 3. 啟動精準的 15 秒取貨倒數計時，時間到了之後再自動出下一個商品！
                    isChangeStatus = true;
                    // 💡 統一都有括號，消除初始與倒數的格式不一致閃爍
                    setTextViewCountdown(
                            getCtx().getString(R.string.pickup_port_open) + "(15)"
                                    + getCtx().getString(R.string.pick_up_within),
                            65);
                    countDownTimer = new CountDownTimer(15_000, 1_000) {
                        public void onTick(long millisUntilFinished) {
                            int seconds = (int) (millisUntilFinished / 1000);
                            setTextViewCountdown(seconds);
                        }

                        public void onFinish() {
                            getLogs().info("📢 15 秒取貨時間結束，自動啟動下一個商品出貨。Index=" + dispatchIndex);
                            if (!isFinalizing) {
                                currentlyProcessingGoods();
                            }
                        }
                    }.start();
                }
            }
        }

        getLogs().info("確保重置出貨控制狀態 dispatchIndex=" + dispatchIndex);
        // ✅ 在這裡確保重置出貨控制狀態
        dispatch.setGetProductComplete(false); // 重置出貨旗標
        isDispatchingNext = false; // 解鎖流程控制
        isGetProductByVMC = false; // 解鎖出貨成功判定
    }

    private void finalizeFailedDispatch(String message) {
        if (isFinalizing) {
            return;
        }
        isFinalizing = true;

        if (finalizeBuilder == null) {
            getLogs().info("finalizeBuilder is null, cannot publish failed dispatch finalize: " + message);
            return;
        }

        addFailedDispenseRecordsFromCurrent();

        if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project) && hasAnySuccessfulDispatch()) {
            insertDrgnoToMemo();
        } else if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
            getLogs().info("CMUH 整單未出貨成功，不寫入 SN/DRGNO Memo，允許重新掃碼領藥");
        }

        getLogs().info("出貨失敗，發送 MQTT Finalize 結案訊息: " + message);
        MqttManager.getInstance().publishTransactionFinalize(finalizeBuilder.build());
        getSrcHandlerMain().send(getClass().getSimpleName(), FontendActivity.Option.RESET_DATA.getOption(),
                "update data");
        saveData();
    }

    private void addFailedDispenseRecordsFromCurrent() {
        if (dispatchList == null || dispatchList.isEmpty()) {
            return;
        }

        int startIndex = Math.max(0, dispatchIndex);
        for (int i = startIndex; i < dispatchList.size(); i++) {
            DispatchStructure dispatch = dispatchList.get(i);
            int failedQuantity = dispatch.getQuantity() - dispatch.getCount();
            if (failedQuantity <= 0) {
                continue;
            }

            ProductStructure product = dispatch.getProduct();
            int productId = 0;
            try {
                productId = Integer.parseInt(product.getProductID());
            } catch (Exception ignored) {
            }

            int remaining = 0;
            try {
                remaining = MyApp.getInstance().getSlotData(dispatch.getField(), dispatch.getSlot()).getCount();
            } catch (LogsEmptyException e) {
                getLogs().warning(e);
            }

            int amount = product.getRealPrice() * failedQuantity;
            finalizeBuilder.addDispenseRecord(dispatch.getSlot(), productId, amount, 0, false, remaining);
            getLogs().info("[Finalize] 已累積出貨失敗記錄: slot=" + dispatch.getSlot()
                    + ", product=" + productId
                    + ", failedQuantity=" + failedQuantity
                    + ", remaining=" + remaining);
        }
    }

    private boolean hasAnySuccessfulDispatch() {
        if (dispatchList == null || dispatchList.isEmpty()) {
            return false;
        }

        for (DispatchStructure dispatch : dispatchList) {
            if (dispatch.getCount() > 0) {
                return true;
            }
        }
        return false;
    }

    private void deliverylot(int msgflag) {
        String productId = dispatchList.get(dispatchIndex).getProduct().getProductID();
        String productName = dispatchList.get(dispatchIndex).getProduct().getProductName();
        if (msgflag == 1) {
            deliveryLogs.add("✔ 出貨成功 - " + productId + " / " + productName);
        } else if (msgflag == 0) {
            deliveryLogs.add("✖ 出貨失敗（貨道無實體商品或商品未取出等問題）- " + productId + " / " + productName);
        }
    }

    private void devRunVMC(int field, int slot, int increase) {

        if (buttonControlCountdown.isShutdown()) {
            buttonControlCountdown.start(25);
        } else {
            buttonControlCountdown.setCountdown(25);
        }

        getLogs().info("11111");
        MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(new DevCheckSlotByVMC(getHandlerMain(), slot));
        getLogs().info("22222");
        MyApp.getInstance().getDevXinYuanController()
                .addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slot));
        getLogs().info("33333");
    }

    private void devRunElectric(int field, int slot, int increase)
            throws LogsEmptyException, LogsSecurityException, LogsUnsupportedOperationException, LogsIOException {
        if (buttonControlCountdown.isShutdown()) {
            buttonControlCountdown.start(5);
        } else {
            buttonControlCountdown.setCountdown(5);
        }

        setTextViewCountdown("開桌完成後點取貨確認", 65);
        isChangeStatus = false;

        SlotStructure slotData = MyApp.getInstance().getSlotData(field, slot);
        // slotData.setLock(true);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.TAIWAN);
        Date timeEpiredDate = new Date();
        if (slotData.getOtherData().has("timeExpired")) {
            try {
                timeEpiredDate = sdf.parse(slotData.getOtherData().getString("timeExpired"));
            } catch (JSONException | ParseException e) {
                getLogs().warning(e);
            }
        }

        String timeEpiredString = sdf.format(timeEpiredDate);
        getLogs().debug("timeEpiredString1:" + timeEpiredString);

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(timeEpiredDate);
        calendar.add(Calendar.HOUR_OF_DAY, increase);
        calendar.add(Calendar.MINUTE, 1);
        timeEpiredDate = calendar.getTime();
        timeEpiredString = sdf.format(timeEpiredDate);
        getLogs().debug("timeEpiredString2:" + timeEpiredString);

        try {
            slotData.getOtherData().put("timeExpired", timeEpiredString);
        } catch (JSONException e) {
            getLogs().warning(e);
        }

        Date now = new Date();
        long diffInMillis = timeEpiredDate.getTime() - now.getTime();

        long diffInSeconds = diffInMillis / 1000; // 總秒數
        int hours = (int) (diffInSeconds / 3600); // 小時
        int minutes = (int) ((diffInSeconds % 3600) / 60); // 剩下的分鐘
        int seconds = (int) (diffInSeconds % 60); // 剩下的秒數
        int hoursByDay = (int) (diffInSeconds / 3600 % 24); // 小時
        int day = (int) (diffInSeconds / 3600 / 24); // 小時

        getLogs().info("hours:" + hours + " minutes:" + minutes + " seconds:" + seconds);

        DevElectricController devElectricController = new DevElectricController(getHandlerMain());
        DevDigitalDisplayController devDigitalDisplayController = new DevDigitalDisplayController(getHandlerMain());

        devElectricController.devSwitch(slot - 1, hours, minutes + 2, seconds);
        devDigitalDisplayController.devPut(slot, hours, minutes, seconds);

        getHandlerMain().getHandler().postDelayed(new Runnable() {
            @Override
            public void run() {
                devElectricController.start();

                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

                devDigitalDisplayController.start();
            }
        }, 200);

        getHandlerMain().start(getClass().getSimpleName(), Option.NEXT_PROCESS_GOODS.getOption(), "next process goods");
    }

    public void ReSetAPPDispatch(Context context) {
        MyApp.getInstance().getBuyList().clear();
        if (context == null) {
            getLogs().info("ReSetAPP -- Context 為 null，無法重啟");
            return;
        }
        Intent intent = new Intent(context, FontendActivity.class);
        // 清空所有 Task，重新啟動
        intent.addFlags(
                Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);

        // 建議搭配 finishAffinity()，確保所有舊的 Activity 都被清掉
        if (context instanceof Activity) {
            ((Activity) context).finishAffinity();
        }
        android.os.Process.killProcess(android.os.Process.myPid());
        // 完全不需要 killProcess()
    }

    public void setButtonOkText(long countdown) {
        if (countdown <= 0) {
            binding.buttonOk.setText(getContext().getString(R.string.pickup_confirmation));// 取貨確認
        } else {
            binding.buttonOk.setText(getContext().getString(R.string.pickup_confirmation) + "(" + countdown + ")");
        }
    }

    public void setTextViewCountdown(long countdown) {
        if (countdown <= 0) {
            setTextViewCountdown(getCtx().getString(R.string.pick_up_goods_within_15_seconds_after_pick_up_port_opened),
                    65);// 取貨口開啟15秒內取貨
        } else {
            setTextViewCountdown(getCtx().getString(R.string.pickup_port_open) + "(" + countdown + ")"
                    + getCtx().getString(R.string.pick_up_within), 65);// 取貨口開啟,秒內取貨
        }
    }

    public void setTextViewSmall(String message) {
        setTextViewCountdown(message, 60);
    }

    public void setTextViewCountdown(String message, int size) {
        if (isChangeStatus) {
            binding.dispatchtips.setText(message);
            binding.dispatchtips.setTextSize(size);
        }
    }

    public class CloseDialogOnThreadHandler extends HandlerMainCountdown {
        public CloseDialogOnThreadHandler(HandlerMain handlerMain) {
            super(handlerMain);
        }

        @Override
        protected HandlerMain setHandlerMain() {
            return getSrcHandlerMain();
        }

        @Override
        protected void close(HandlerMain handlerMain) {
            handlerMain.start(getClass().getSimpleName(), Option.NEXT_PRODUCT.getOption(), "next product");
        }

        @Override
        protected void execute(long countdown, HandlerMain handlerMain) {
            handlerMain.start(getClass().getSimpleName(), Option.SET_COUNTDOWN_TEXT.getOption(),
                    String.valueOf(countdown));
        }

        @Override
        protected Class<?> setCls() {
            return getClass();
        }
    }

    public void insertDrgnoToMemo() {
        try {
            /* 刷藥單解碼後進來的json資料 */
            JSONObject jsonObject = new JSONObject(dispatchList.get(0).getProduct().getMarketingPlan());
            // **要新增的 ITEM 資料**
            JSONObject newItem = new JSONObject();
            newItem.put("SN", jsonObject.optString("SN")); // **領藥人藥單序號**
            newItem.put("DRGNO", jsonObject.optString("DRGNO")); // **領藥人醫令**

            /* 從Memo的JSON資料中，讀取SN及DRGNO的全部資料 */
            String jsonStr = MyApp.getInstance().getMemoMap().get("CMUH").get(0).getData().toString();
            JSONObject jsonObject2 = new JSONObject(jsonStr);
            JSONArray itemArray = jsonObject2.getJSONArray("ITEM");

            // **判斷是否已存在**
            boolean exists = false;
            for (int i = 0; i < itemArray.length(); i++) {
                JSONObject item = itemArray.getJSONObject(i);
                if (item.getString("SN").equals(newItem.getString("SN"))
                        && item.getString("DRGNO").equals(newItem.getString("DRGNO"))) {
                    exists = true;
                    break; // **如果已存在，就結束迴圈**
                }
            }

            // **如果不存在，才加入**
            if (!exists) {
                itemArray.put(newItem);
                getLogs().info("✅ 領藥人醫令新增到Memo成功：" + newItem.toString());
            } else {
                getLogs().info("⚠️ 領藥人醫令已存在，不新增！");
            }

            // **輸出結果**並新增到Memo 的 SQLite資料庫 data欄位中
            MyApp.getInstance().getMemoMap().get("CMUH").get(0).setData(jsonObject2);
            // ✅ 寫入 SQLite
            MemoStructure updatedMemo = MyApp.getInstance().getMemoMap().get("CMUH").get(0);
            MemoDao memoDao = new MemoDao(getCtx());
            memoDao.insertOnlyOne(updatedMemo);
            memoDao.close();
        } catch (JSONException e) {
            throw new RuntimeException(e);
        }
    }

}
