package com.unibuy.smartdevice.ui.dialog;

import android.content.Context;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.unibuy.smartdevice.DialogAbstract;
import com.unibuy.smartdevice.MyApp;
import com.unibuy.smartdevice.R;
import com.unibuy.smartdevice.databinding.DialogBuyBinding;
import com.unibuy.smartdevice.devices.SlotField;
import com.unibuy.smartdevice.exception.LogsEmptyException;
import com.unibuy.smartdevice.structure.BuyStructure;
import com.unibuy.smartdevice.structure.DevSetStructure;
import com.unibuy.smartdevice.structure.ReportFreeGiftCodeStructure;
import com.unibuy.smartdevice.structure.SlotStructure;
import com.unibuy.smartdevice.tools.HandlerMainCountdown;
import com.unibuy.smartdevice.tools.HandlerMain;
import com.unibuy.smartdevice.tools.ToastHandlerMain;
import com.unibuy.smartdevice.tools.Tools;
import com.unibuy.smartdevice.ui.FontendActivity;
import com.unibuy.smartdevice.utils.LanguageHelper;

import java.util.HashMap;
import java.util.Map;

public class BuyDialog extends DialogAbstract {
    public enum Option {
        TOAST(0),
        COUNTDOWN_CANCEL(1),
        SET_COUNTDOWN_TEXT(2),
        SET_PRICE(3),
        RESET_COUNTDOWM(4),
        RESET_COUNTDOWM_TOAST(5),
        FORCE_CLOSE(6),
        ;

        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(getContext(), getLogs()) {
            @Override
            protected void execute(Context context, int commandCode, String message) {
                if (MyApp.getInstance().getBuyList().size() <= 0) {
                    dialogCancel();
                } else {
                    Option option = optionMap.get(commandCode);

                    switch (option) {
                        case TOAST:
                                super.execute(context, commandCode, message);
                            break;
                        case COUNTDOWN_CANCEL:
                                cancelOnThreadCountdown();
                            break;
                        case SET_COUNTDOWN_TEXT:
                                setButtonCancelText(Integer.valueOf(message));
                            break;
                        case SET_PRICE:
                                refreshAmountBar();
                                closeDialogCountdown.setCountdown(40);
                            break;
                        case RESET_COUNTDOWM:
                                closeDialogCountdown.setCountdown(40);
                            break;
                        case RESET_COUNTDOWM_TOAST:
                                super.execute(context, commandCode, message);
                                closeDialogCountdown.setCountdown(40);
                            break;
                        case FORCE_CLOSE:
                            dialogCancel();
                            break;
                    }
                }
            }
        };
    }

    private DialogBuyBinding binding;
    private CloseDialogOnThreadHandler closeDialogCountdown;

    public BuyDialog(Context context, HandlerMain srcHandlerMain) {
        super(context, srcHandlerMain);
        getLogs().info("open:" + getClass().getSimpleName());
    }

    @Override
    protected void onCreate(Context context) {
        binding = DialogBuyBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // 後台「UI元素設定」支付方式按鈕圖（P1~P6），未設定則保留各自預設圖
        com.unibuy.smartdevice.ui.tools.UiSkin.apply("P1", binding.imgPayCredit);    // 信用卡
        com.unibuy.smartdevice.ui.tools.UiSkin.apply("P2", binding.imgPaySmartCard); // 電子票證/卡片
        com.unibuy.smartdevice.ui.tools.UiSkin.apply("P3", binding.imgPayEsun);      // 玉山掃碼
        com.unibuy.smartdevice.ui.tools.UiSkin.apply("P4", binding.imgPayMobile);    // 手機支付
        com.unibuy.smartdevice.ui.tools.UiSkin.apply("P5", binding.imgPayCash);      // 現金
        com.unibuy.smartdevice.ui.tools.UiSkin.apply("P6", binding.imgPayTapPay);    // TapPay 掃碼

        // 讀取最後設定語言的設定
//        LanguageHelper.applySavedLanguage(context);


        MyApp.getInstance().initialReportData();
        MyApp.getInstance().getReportFlowInfoData().setOrderId(Tools.generateTimeBasedUUID());
        MyApp.getInstance().getPostInvoiceByGreenData().setOrderId(MyApp.getInstance().getReportFlowInfoData().getOrderId());



        this.binding.buttonNfcCard.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(1);
            gotoDialog();
        });

        this.binding.buttonNfcRecharge.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(2);
            gotoDialog();
        });

        this.binding.buttonEsunPay.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(3);
            gotoDialog();
        });

        this.binding.buttonTapPay.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(30);
            gotoDialog();
        });

        this.binding.buttonLinePay.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(70);
            gotoDialog();
        });

        this.binding.buttonNfcPhone.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(10); // 手機感應＝payment_type 10，全程一致（避免失敗上報撞後台 4=紙鈔機）
            gotoDialog();
        });

        this.binding.buttonCash.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(9);
            gotoDialog();
        });

        this.binding.buttonAccessCode.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(5);
            gotoDialog();
        });

        this.binding.buttonFreeGift.setOnClickListener(v -> {
            MyApp.getInstance().getReportFlowInfoData().setFlowType(7);
            gotoDialog();
        });

        // 來店禮入口：獨立判斷，由雲端 B014 FunctionSet.WelcomeGift 下發控制（DevSet.isWelcomeGift）。
        // 與取貨模組無關；關閉時隱藏「來店禮」按鈕，避免進入 flowType 7。
        // 防呆：本筆已套用過來店禮（freeGiftId 非空）即隱藏入口，禁止重複掃描覆蓋／疊加折扣。
        boolean welcomeGiftApplied =
                !MyApp.getInstance().getReportFreeGiftCodeStructure().getFreeGiftId().isEmpty();
        this.binding.buttonFreeGift.setVisibility(
                (MyApp.getInstance().getDevSet().isWelcomeGift() && !welcomeGiftApplied)
                        ? View.VISIBLE : View.GONE);

        // 通行碼入口（付款格內 buttonAccessCode，flowType 5）：權威判斷，涵蓋購物車/單品所有模式。
        // 從屬取貨模組，PickupModule 或 PassCode 任一關閉即隱藏；購物車模式下方不會再覆寫此值。
        this.binding.buttonAccessCode.setVisibility(
                (MyApp.getInstance().getDevSet().isPickupModule()
                        && MyApp.getInstance().getDevSet().isPassCode()) ? View.VISIBLE : View.GONE);

        this.binding.buttonCancel.setOnClickListener(v -> dialogCancel());

        closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain());
        closeDialogCountdown.start(40);

        refreshAmountBar();

        if (MyApp.getInstance().getDevSet().isShoppingCar()) {
            // 刷卡機（母 isDevNFCPay）三子項：各自 母 && 子 判斷，母關則三者全隱藏。
            //   信用卡=NfcCard(canCreditCard)、電子票證/卡片=NfcRecharge(canCardPay)、手機=NfcPhone(canMobilePay)
            binding.buttonNfcCard.setVisibility(MyApp.getInstance().getDevSet().canCreditCard() ? View.VISIBLE : View.GONE);
            binding.buttonNfcRecharge.setVisibility(MyApp.getInstance().getDevSet().canCardPay() ? View.VISIBLE : View.GONE);
            binding.buttonNfcPhone.setVisibility(MyApp.getInstance().getDevSet().canMobilePay() ? View.VISIBLE : View.GONE);

            // 掃碼（母 isDevScanPay）子項：玉山 / TapPay，母關則隱藏。
            binding.buttonEsunPay.setVisibility(MyApp.getInstance().getDevSet().canEsunPay() ? View.VISIBLE : View.GONE);
            binding.buttonTapPay.setVisibility(MyApp.getInstance().getDevSet().canTapPay() ? View.VISIBLE : View.GONE);

            // Line 官方支付：獨立大項，不從屬掃碼。
            binding.buttonLinePay.setVisibility(MyApp.getInstance().getDevSet().canLinePay() ? View.VISIBLE : View.GONE);

            // 現金：獨立大項。
            binding.buttonCash.setVisibility(MyApp.getInstance().getDevSet().canCash() ? View.VISIBLE : View.GONE);
        } else {
            BuyStructure buyData = MyApp.getInstance().getBuyList().get(0);

            try {
                SlotField slotField = SlotField.getSlotField(buyData.getField());
                // 貨道層級是否支援 NFC（VMC/ELECTRIC 不同）；三個刷卡機子項共用這個貨道旗標。
                boolean slotNfc = slotField.isDevNFCPay();

                if (MyApp.getInstance().getDevSet().isDevNFCPay()) {
                    // 刷卡機開啟：三子項各自 (母&&子) 且貨道支援 NFC 才顯示。
                    binding.buttonNfcCard.setVisibility(
                            (MyApp.getInstance().getDevSet().canCreditCard() && slotNfc) ? View.VISIBLE : View.GONE);
                    binding.buttonNfcRecharge.setVisibility(
                            (MyApp.getInstance().getDevSet().canCardPay() && slotNfc) ? View.VISIBLE : View.GONE);
                    binding.buttonNfcPhone.setVisibility(
                            (MyApp.getInstance().getDevSet().canMobilePay() && slotNfc) ? View.VISIBLE : View.GONE);
                } else {
                    // 刷卡機關閉：三子項全隱藏，並啟用通行碼備援入口（仍須從屬取貨模組且通行碼開啟，
                    // 否則 NFC 不支援時會把已關閉的通行碼漏出來）。
                    binding.buttonNfcCard.setVisibility(View.GONE);
                    binding.buttonNfcPhone.setVisibility(View.GONE);
                    binding.buttonNfcRecharge.setVisibility(View.GONE);
                    boolean passCodeOn = MyApp.getInstance().getDevSet().isPickupModule()
                            && MyApp.getInstance().getDevSet().isPassCode();
                    binding.buttonAccessCode.setVisibility(passCodeOn ? View.VISIBLE : View.GONE);
                }

                // 掃碼子項：母(isDevScanPay)&&子 且貨道支援。
                binding.buttonEsunPay.setVisibility(
                        (MyApp.getInstance().getDevSet().canEsunPay() && slotField.isDevEsunpay()) ? View.VISIBLE : View.GONE);
                binding.buttonTapPay.setVisibility(
                        (MyApp.getInstance().getDevSet().canTapPay() && slotField.isDevTapPay()) ? View.VISIBLE : View.GONE);

                // Line 官方支付：獨立大項，僅貨道支援與自身開關。
                binding.buttonLinePay.setVisibility(
                        (MyApp.getInstance().getDevSet().canLinePay() && slotField.isDevLinePay()) ? View.VISIBLE : View.GONE);

                // 現金：獨立大項。
                binding.buttonCash.setVisibility(
                        (MyApp.getInstance().getDevSet().canCash() && slotField.isDevCash()) ? View.VISIBLE : View.GONE);

            } catch (LogsEmptyException e) {
                // 無貨道資訊：略過貨道層級限制，只依「母&&子」開關判斷。
                binding.buttonNfcCard.setVisibility(MyApp.getInstance().getDevSet().canCreditCard() ? View.VISIBLE : View.GONE);
                binding.buttonNfcRecharge.setVisibility(MyApp.getInstance().getDevSet().canCardPay() ? View.VISIBLE : View.GONE);
                binding.buttonNfcPhone.setVisibility(MyApp.getInstance().getDevSet().canMobilePay() ? View.VISIBLE : View.GONE);
                binding.buttonEsunPay.setVisibility(MyApp.getInstance().getDevSet().canEsunPay() ? View.VISIBLE : View.GONE);
                binding.buttonTapPay.setVisibility(MyApp.getInstance().getDevSet().canTapPay() ? View.VISIBLE : View.GONE);
                binding.buttonLinePay.setVisibility(MyApp.getInstance().getDevSet().canLinePay() ? View.VISIBLE : View.GONE);
                binding.buttonCash.setVisibility(MyApp.getInstance().getDevSet().canCash() ? View.VISIBLE : View.GONE);
            }
        }

        // 準備付款時檢查取貨口（basic 模式）：取貨口有東西(0x05)/門沒關(0x06、0x09) 就提示並關閉付款頁回銷售頁。
        // 員工卡(點擊已自檢)、取貨碼(自有檢查)、pickup_sheet 不在此檢查。
        // 直購路徑已在「點選商品」當下檢查過取貨口（BasicSaleFlow.checkPickupOnly）→ 旗標仍新鮮時略過重驗，
        // 只有其他入口（購物車結帳、發票對話框重開等未預檢者）才在此做取貨口檢查。（比照 LockBoard-Honlifeng）
        if ("basic".equals(MyApp.getInstance().getShoppingMode())
                && !com.unibuy.smartdevice.ui.BasicSaleFlow.consumeFreshPickupPreCheck()) {
            checkPickupPortBeforePay();
        }
    }

    // 準備付款時檢查取貨口（basic 模式）。送 checkSlot 後 800ms 讀回；此時距實際出貨還有數秒
    // （使用者尚未選付款），不會與出貨流程搶同一條 DevXinYuanController。機器無回應(3000ms 超時)視為正常放行。
    private void checkPickupPortBeforePay() {
        java.util.concurrent.atomic.AtomicBoolean isHandled = new java.util.concurrent.atomic.AtomicBoolean(false);
        new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(
                () -> isHandled.getAndSet(true), 3000);
        MyApp.getInstance().getDevXinYuanController().addSendBuffer(
                MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlotPre(1, 1));
        MyApp.getInstance().getDevXinYuanController().addSendBuffer(
                MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(1));
        MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(
                new DevCheckPickupByVMC(getHandlerMain(), isHandled), 800);
    }

    private class DevCheckPickupByVMC extends com.unibuy.smartdevice.controller.DevController.ReadBufferOnScheduler {
        private final java.util.concurrent.atomic.AtomicBoolean isHandled;
        private final HandlerMain dialogHandler;

        DevCheckPickupByVMC(HandlerMain handlerMain, java.util.concurrent.atomic.AtomicBoolean isHandled) {
            super(handlerMain);
            this.dialogHandler = handlerMain;
            this.isHandled = isHandled;
        }

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

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

        @Override
        public void readBufferOnScheduler(com.unibuy.smartdevice.controller.DevController devController, HandlerMain handlerMain) {
            boolean haveThings = false;
            boolean doorError = false;
            for (byte[] buffer : devController.getReadBufferByMax()) {
                if (buffer[0] == 0x02) {
                    if (buffer[1] == 0x05) {
                        haveThings = true;
                        break;
                    } else if (buffer[1] == 0x06 || buffer[1] == 0x09) {
                        doorError = true;
                        break;
                    }
                }
            }

            // 後備判斷（移植自 LockBoard-Honlifeng）：主動查詢的 800ms 讀窗常錯過下位機自發廣播，
            // 或緩衝被清/狂送非標準幀導致讀不到乾淨回應。退回讀 controller 背景快取的 VMC_02 廣播
            // （有東西時約每 4 秒一次，取 4000ms 內），避免「取貨口有東西」漏判。
            if (!haveThings && !doorError) {
                int cached = MyApp.getInstance().getDevXinYuanController().getRecentSlotStatus(4000);
                if (cached == 0x05) {
                    haveThings = true;
                } else if (cached == 0x06 || cached == 0x09) {
                    doorError = true;
                }
            }

            // 狀態安全：放行付款
            if (!haveThings && !doorError) return;
            // 已被超時或前一筆處理過，避免重複跳窗
            if (isHandled.getAndSet(true)) return;

            final String message = doorError
                    ? BuyDialog.this.getContext().getString(R.string.pickup_door_not_closed)
                    : BuyDialog.this.getContext().getString(R.string.there_is_something_at_the_pickup_port);

            new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
                android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(BuyDialog.this.getContext())
                        .setTitle(R.string.abnormal_reminder)
                        .setMessage(message)
                        .setCancelable(false)
                        .setPositiveButton(R.string.i_knew, (d, which) -> {
                            d.dismiss();
                            // 關閉付款頁回銷售頁
                            dialogHandler.start("BuyDialogPickupCheck",
                                    Option.FORCE_CLOSE.getOption(), "pickup port blocked");
                        })
                        .create();
                dialog.show();
                android.widget.TextView mv = dialog.findViewById(android.R.id.message);
                if (mv != null) {
                    mv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 24);
                }
            });
        }
    }

    /** 商品原始總額（未套用來店禮折扣），不含免費商品。 */
    public int getOriginalPrice() {
        int totalPrice = 0;
        for(BuyStructure buy: MyApp.getInstance().getBuyList()) {
            if (!buy.isFreeGift()) totalPrice += buy.getFullPrice();
        }
        return totalPrice;
    }

    /** 商品總件數（不含免費商品），用於支付頁顯示「共 X 件」。 */
    public int getTotalCount() {
        int count = 0;
        for(BuyStructure buy: MyApp.getInstance().getBuyList()) {
            if (!buy.isFreeGift()) count += buy.getCount();
        }
        return count;
    }

    public int getTotalPrice() {
        if (MyApp.getInstance().getBuyList().isEmpty()) {
            return 0;
        }
        int totalPrice = getOriginalPrice();
        if (!MyApp.getInstance().getReportFreeGiftCodeStructure().getFreeGiftId().isEmpty()) {
            switch (MyApp.getInstance().getReportFreeGiftCodeStructure().getMode()) {
                case 0:
                    break;
                case 1:
                    totalPrice = totalPrice - MyApp.getInstance().getReportFreeGiftCodeStructure().getDiscountAmount();
                    break;
                case 2:
                    totalPrice = (int) (totalPrice * MyApp.getInstance().getReportFreeGiftCodeStructure().getDiscountPercentage());
                    break;
            }
        }
        if (totalPrice < 1) totalPrice = 1; // 折抵後至少 1 元（避免 0 元交易）
        return totalPrice;
    }

    /**
     * 刷新支付頁上方「件數 / 應付金額」列。
     * 金額已含來店禮折抵；有套用來店禮時於後方標示實際折抵金額（折抵/打折統一以省下的錢顯示）。
     */
    private void refreshAmountBar() {
        binding.textCount.setText(getContext().getString(R.string.pay_item_count, getTotalCount()));

        int payable = getTotalPrice();
        String priceText = "NT$" + payable;
        android.text.SpannableStringBuilder spannable = new android.text.SpannableStringBuilder(priceText);
        spannable.setSpan(
                new android.text.style.ForegroundColorSpan(androidx.core.content.ContextCompat.getColor(getContext(), R.color.primary)),
                0,
                priceText.length(),
                android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        );

        if (!MyApp.getInstance().getReportFreeGiftCodeStructure().getFreeGiftId().isEmpty()) {
            int saved = getOriginalPrice() - payable;
            if (saved > 0) {
                String discountPart = " (" + getContext().getString(R.string.pay_discounted_format, saved) + ")";
                int start = spannable.length();
                spannable.append(discountPart);
                spannable.setSpan(
                        new android.text.style.ForegroundColorSpan(androidx.core.content.ContextCompat.getColor(getContext(), R.color.success)),
                        start,
                        spannable.length(),
                        android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                );
            }
        }
        binding.textPrice.setText(spannable);
    }

    public void dialogCancel() {
        MyApp.getInstance().setReportFreeGiftCodeStructure(new ReportFreeGiftCodeStructure("", 0, "", 0, 0, ""));
        getSrcHandlerMain().start(getClass().getSimpleName(), FontendActivity.Option.STOP_COUNTDOWN.getOption(), "");
        closeDialogCountdown.shutdown();
        MyApp.getInstance().getBuyList().clear();
        cancel();
    }

    public void cancelOnThreadCountdown() {
        MyApp.getInstance().setReportFreeGiftCodeStructure(new ReportFreeGiftCodeStructure("", 0, "", 0, 0, ""));
        getSrcHandlerMain().start(getClass().getSimpleName(), FontendActivity.Option.STOP_COUNTDOWN.getOption(), "");
        MyApp.getInstance().getBuyList().clear();
        getTools().dialogClose();
        cancel();
    }

    public void setButtonCancelText(long countdown) {
        binding.buttonCancel.setText(getContext().getString(R.string.cancel) + "（" + countdown + "秒）");//取消
    }

    public void gotoDialog() {
        closeDialogCountdown.shutdown();
        int flowType = MyApp.getInstance().getReportFlowInfoData().getFlowType();
        getLogs().debug("buy flowType:" + flowType);
        // 非現金付款(flowType != 9)：此刻機台剛進收款模式、還在 POLL，立即送 inComeByIPC 給足額 credit
        // (沿用開機已驗證的 0x08/9999)，讓「收款式」下位機結束收款、後續正常出貨；
        // 否則機台會卡在等錢(狂送 VMC_29 金錢開關)、不送 POLL → checkSlot 送不出去 → 出貨失敗。
        // 現金(9)不送：由現金流程依實收金額自行送 inComeByIPC，避免預付 credit 造成免費出貨。
        if (flowType != 9) {
            try {
                MyApp.getInstance().getDevXinYuanController().addSendBuffer(
                        MyApp.getInstance().getDevXinYuanController().getDevXinYuan().inComeByIPC((byte) 0x08, 9999));
                getLogs().info("非現金付款(flowType=" + flowType + ") → 送 inComeByIPC 給下位機收款 credit");
            } catch (Exception ignore) {}
        }
//        if (flowType == 5 || flowType == 7 || flowType == 30) {
        if (flowType == 5 || flowType == 7) {
            changeCheckout();
        } else {
            if (MyApp.getInstance().getDevSet().isShoppingCar()) {
                if (MyApp.getInstance().getDevSet().isInvoice()) {
                    changeDialog(InvoiceTypeDialog.class);
                } else {
                    changeCheckout();
                }
            } else {
                if (MyApp.getInstance().getDevSet().isInvoice()) {
                    BuyStructure buyData = MyApp.getInstance().getBuyList().get(0);
                    try {
                        SlotField slotField = SlotField.getSlotField(buyData.getField());
                        if (slotField.isInvoice()) {
                            changeDialog(InvoiceTypeDialog.class);
                        } else {
                            changeCheckout();
                        }
                    } catch (LogsEmptyException e) {
                        changeDialog(InvoiceTypeDialog.class);
                    }
                } else {
                    changeCheckout();
                }
            }
        }
    }

    public void changeCheckout() {
        closeDialogCountdown.shutdown();
        int flowType = MyApp.getInstance().getReportFlowInfoData().getFlowType();
        // 入口防呆：即使按鈕被繞過（殘留事件/外部觸發），進入支付 dialog 前再以同一份「母&&子」
        // 開關權威判斷一次；母開關或子項任一關閉即擋下，避免已停用的支付方式漏出。
        if (!isPaymentFlowAllowed(flowType)) {
            getLogs().info("⛔ 支付方式已停用，封堵入口 flowType=" + flowType);
            dialogCancel();
            return;
        }

        // 進入付款：先清掉上一筆殘留的 MQTT 交易流水號，避免被本筆誤用（flow_id 撞單）。
        com.unibuy.smartdevice.structure.ReportFlowInfoStructure flowInfo =
                MyApp.getInstance().getReportFlowInfoData();
        flowInfo.setMqttFlowId("");
        // basic 購物模式(本專案 general/demo) 且為實際付款方式(1/2/3/4/9/30/70) 時：
        // 為本筆生成新流水號，並上報 pending（「進付款一律留底」）。pending → completed/failed 全程共用此 flow_id。
        // 通行碼(5)/來店禮(7) 非付款，不在此處理；employee_card(晟崴)/pickup_sheet(中國醫) 無付款不送，後台資料不受影響。
        boolean realPayment = (flowType == 1 || flowType == 2 || flowType == 3 || flowType == 10
                || flowType == 9 || flowType == 30 || flowType == 70);
        if ("basic".equals(MyApp.getInstance().getShoppingMode()) && realPayment) {
            flowInfo.setMqttFlowId(com.unibuy.smartdevice.tools.FlowIdGenerator.next(MyApp.getInstance()));
            com.unibuy.smartdevice.tools.TransactionFinalizeBuilder.publishPending();
        }

        switch (flowType) {
            case 1:
                changeDialog(CheckoutNfcCardDialog.class);
                break;
            case 2:
                changeDialog(CheckoutNfcRechargeDialog.class);
                break;
            case 3:
                changeDialog(CheckoutEsunPayDialog.class);
                break;
            case 10:
                changeDialog(CheckoutNfcPhoneDialog.class);
                break;
            case 5:
                changeDialog(CheckoutAccessCodeDialog.class);
                break;
            case 7:
                changeDialog(CheckoutFreeGiftCodeDialog.class);
                break;
            case 9:
                changeDialog(cashDialogClass());
                break;
            case 30:
                changeDialog(CheckoutTapPayDialog.class);
                break;
            case 70:
                changeDialog(CheckoutLinePayDialog.class);
                break;
        }
    }

    /**
     * 入口防呆：依 flowType 對應的支付方式，判斷其「母&&子」開關是否允許。
     * flowType 5（通行碼）、7（來店禮）非支付方式，由取貨模組/來店禮各自把關，此處放行。
     */
    private boolean isPaymentFlowAllowed(int flowType) {
        DevSetStructure d = MyApp.getInstance().getDevSet();
        switch (flowType) {
            case 1:  return d.canCreditCard();   // 信用卡（刷卡機）
            case 2:  return d.canCardPay();      // 電子票證/卡片支付（刷卡機）
            case 10: return d.canMobilePay();    // 手機支付（刷卡機）
            case 3:  return d.canEsunPay();      // 玉山（掃碼）
            case 30: return d.canTapPay();       // TapPay（掃碼）
            case 70: return d.canLinePay();      // Line 官方支付（獨立）
            case 9:  return d.canCash();         // 現金（獨立）
            default: return true;                // 5/7 等非支付流程放行
        }
    }

    public static 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.COUNTDOWN_CANCEL.getOption(), "close dialog");
        }

        @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();
        }
    }
}
