package com.unibuy.smartdevice.ui;

import android.content.Context;

import com.blankj.utilcode.util.ToastUtils;
import com.unibuy.smartdevice.MyApp;
import com.unibuy.smartdevice.R;
import com.unibuy.smartdevice.structure.BuyStructure;
import com.unibuy.smartdevice.structure.SlotStructure;
import com.unibuy.smartdevice.tools.HandlerMain;
import com.unibuy.smartdevice.ui.dialog.BuyDialog;
import com.unibuy.smartdevice.ui.dialog.FontendPickupCodeDialog;
import com.unibuy.v2.UnibuyHelper;
import com.unibuy.v2.ui.ShoppingCartFragment;

/**
 * basic 模式（原 General 通用版）銷售流程。
 * 結帳時彈出標準付款 Dialog (BuyDialog)；購物車浮鈕則開啟多品項購物車 Fragment。
 */
public class BasicSaleFlow extends SaleFlowHandler {

    /** 多品項購物車 Fragment（懶建立，重複開啟時沿用同一實例）。 */
    private ShoppingCartFragment cartFragment;

    // ─── 取貨口（主櫃 XinYuan）狀態常數（移植自 LockBoard-Honlifeng）───
    private static final int PICKUP_OK = 0;            // 正常，可放行
    private static final int PICKUP_HAVE_THINGS = 1;   // 取貨口有東西 (02 05)
    private static final int PICKUP_DOOR = 2;          // 取貨口門未關 (02 06 / 02 09)
    private static final int PICKUP_ELEVATOR = 3;      // 升降梯未回原點 (02 07)
    private static final int PICKUP_NO_RESPONSE = 4;   // 下位機完全無通訊(可能真的斷線)
    private static final int PICKUP_ALIVE_UNKNOWN = 5; // 下位機在線但未回報取貨口狀態(忙其他協定)→放行，交後續階段再判

    // 「點選商品」階段已判斷過取貨口的旗標與時效。直購路徑把取貨口判斷提前到點選當下，
    // 之後 BuyDialog 開啟時若旗標仍新鮮就跳過重驗取貨口（避免剛連線就偵測導致漏判、也免重複跳窗）。
    private static final long PICKUP_PRECHECK_TTL_MS = 30000;
    private static volatile long lastPickupOkAtMs = 0L;

    // 單飛守衛：同一時間只允許一個「點選商品」檢查在進行。連續快速點商品時，若不擋，
    // 每次點擊都會疊加一輪 checkSlot/syncPackNo 到送出佇列，塞爆後觸發過載→下位機失連。
    private static volatile boolean pickupCheckRunning = false;

    // 「點選商品」階段的合併檢查開關（連線＋取貨口，比照舊專案 DaiLemei）。
    // true=點下商品當下就用一次主動問答同時判「下位機是否連線」與「取貨口是否有東西」，早退、~3.5 秒封頂：
    //   正常→開詳情頁並記旗標(直接購買略過重複連線判斷)；有東西/門/升降→擋；完全無回應→視為未連線擋住。
    // false=點商品直接開詳情頁（連線判斷延到直接購買時做）。
    private static final boolean PICKUP_PRECHECK_AT_SELECT = true;

    /** 直購取貨口預檢旗標是否仍在時效內；是則消費掉（一次性）並回 true。BuyDialog 開啟時呼叫以決定是否略過重驗。 */
    public static boolean consumeFreshPickupPreCheck() {
        boolean fresh = lastPickupOkAtMs > 0
                && android.os.SystemClock.elapsedRealtime() - lastPickupOkAtMs <= PICKUP_PRECHECK_TTL_MS;
        lastPickupOkAtMs = 0L; // 用過即失效，避免下一筆誤用舊結果
        return fresh;
    }

    /** 只查詢(不消費)預檢旗標是否仍新鮮。onBuyRequested 用來決定是否略過重複的下位機連線判斷。 */
    private boolean isFreshPickupPreCheck() {
        return lastPickupOkAtMs > 0
                && android.os.SystemClock.elapsedRealtime() - lastPickupOkAtMs <= PICKUP_PRECHECK_TTL_MS;
    }

    public BasicSaleFlow(Context context, HandlerMain handlerMain, SaleFlowCallback callback) {
        super(context, handlerMain, callback);
    }

    @Override
    public void onBuyRequested() {
        // 下位機「連線判斷」先移除（不穩定、常誤報「未連線」）：一律直接進付款，連線問題交由出貨階段處理。
        // 取貨口偵測仍保留——直購已在「點選商品」當下檢查(checkPickupOnly)；其他入口(購物車/發票重開等)
        // 由 BuyDialog.checkPickupPortBeforePay 檢查取貨口(有東西/門才擋，無回應則放行)。
        new BuyDialog(context, handlerMain).show();
    }

    /** 購物清單是否含主櫃(VMC)商品；非主櫃商品不需檢查主櫃下位機連線。 */
    private boolean buyListHasMainCabinetItem() {
        for (BuyStructure b : MyApp.getInstance().getBuyList()) {
            if (b.getField() == com.unibuy.smartdevice.devices.SlotField.VMC.getField()) {
                return true;
            }
        }
        return false;
    }

    /** 連線判斷中：轉圈圈 + 提示文字（不可取消）。 */
    private android.app.AlertDialog buildConnectingDialog() {
        android.widget.LinearLayout layout = new android.widget.LinearLayout(context);
        layout.setOrientation(android.widget.LinearLayout.HORIZONTAL);
        layout.setGravity(android.view.Gravity.CENTER_VERTICAL);
        int pad = dp(32);
        layout.setPadding(pad, pad, pad, pad);

        android.widget.ProgressBar spinner = new android.widget.ProgressBar(context);
        android.widget.LinearLayout.LayoutParams sp =
                new android.widget.LinearLayout.LayoutParams(dp(56), dp(56));
        sp.rightMargin = dp(24);
        layout.addView(spinner, sp);

        android.widget.TextView tv = new android.widget.TextView(context);
        tv.setText("下位機連線判斷中......");
        tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 28);
        tv.setTextColor(android.graphics.Color.BLACK);
        layout.addView(tv);

        return new android.app.AlertDialog.Builder(context)
                .setView(layout)
                .setCancelable(false)
                .create();
    }

    /** 主櫃未連線：跳出大字提示框。 */
    private void showNotConnectedDialog() {
        android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(context)
                .setMessage("主櫃尚未連線，請稍後再試")
                .setCancelable(false)
                .setPositiveButton("確定", (d, w) -> d.dismiss())
                .create();
        try { dialog.show(); } catch (Exception ignore) { return; }
        android.widget.TextView msg = dialog.findViewById(android.R.id.message);
        if (msg != null) msg.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 28);
        android.widget.Button btn = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE);
        if (btn != null) btn.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 24);
    }

    private int dp(int v) {
        return Math.round(v * context.getResources().getDisplayMetrics().density);
    }

    /**
     * 點選 VMC 商品，依雲端 B014 DevSet.ShoppingCar 決定流程：
     *  - 購物車開啟：加入多品項購物車並停留商品列表（不做取貨口檢查，出貨時才檢查）。
     *  - 購物車關閉：單品直購——清空後僅放入這一筆，直接彈出 BuyDialog（商品資料＋選付款）。
     * @return true 表示已消化此次點擊。
     */
    @Override
    public boolean handleProductSelected(SlotStructure slot) {
        // 【第一階段：點選商品】直購（購物車關閉）且點的是主櫃(VMC)商品：點選當下就用一次主動問答
        // 同時判「下位機是否連線」與「取貨口是否有東西」（比照舊專案 DaiLemei，快、~3.5 秒封頂、早退）。
        // 有東西/門/升降異常 → 直接跳提示擋住，連詳情頁都不開；完全無回應 → 視為未連線擋住；
        // 正常才開詳情頁並記旗標，之後按「直接購買」若旗標仍新鮮就不再重做連線判斷（避免重複佔用串口）。
        boolean cartOn = MyApp.getInstance().getDevSet().isShoppingCar();
        if (PICKUP_PRECHECK_AT_SELECT && !cartOn && isMainCabinetSlot(slot)) {
            checkPickupOnly(slot.getSlot(), () ->
                    new com.unibuy.smartdevice.ui.dialog.DialogProductDetail(context, handlerMain, slot).show());
            return true;
        }
        // 購物車開啟（加入購物車、出貨時才檢查）：一律直接彈出商品詳情頁。
        new com.unibuy.smartdevice.ui.dialog.DialogProductDetail(context, handlerMain, slot).show();
        return true;
    }

    /** 是否為主櫃(VMC,field 0)商品；只有主櫃才需取貨口檢查。 */
    private boolean isMainCabinetSlot(SlotStructure slot) {
        return slot != null
                && slot.getField() == com.unibuy.smartdevice.devices.SlotField.VMC.getField();
    }

    /**
     * 【第一階段：點選商品】一次主動問答同時判「下位機是否連線」與「取貨口是否有東西」（比照舊專案 DaiLemei）。
     * 有東西/門/升降 → 跳提示擋住（不開詳情頁）；完全無回應 → 視為下位機未連線擋住；
     * 正常 → onPass 開詳情頁並記旗標（直接購買據此略過重複連線判斷，BuyDialog 也略過取貨口重驗）。
     */
    private void checkPickupOnly(int slotnum, Runnable onPass) {
        // 單飛：已有一個檢查在進行(轉圈提示中)就忽略這次點擊，避免疊加查詢把送出佇列塞爆導致失連。
        if (pickupCheckRunning) return;
        pickupCheckRunning = true;
        final android.app.AlertDialog progress = buildProgressDialog(context.getString(R.string.pickup_checking));
        try { progress.show(); } catch (Exception ignore) {}
        new Thread(() -> {
            com.unibuy.smartdevice.exception.Logs logs = new com.unibuy.smartdevice.exception.Logs(BasicSaleFlow.class);
            // 下位機連線判斷已移除：不再自動重連重試。只做一次取貨口偵測。
            final int pickup = probePickupHasThing(slotnum);
            logs.info("點選商品-取貨口檢查 pickup=" + pickup
                    + " (0=OK,1=有東西,2=門,3=升降梯,4=無通訊,5=在線但狀態未知)");
            postMain(() -> {
                pickupCheckRunning = false;   // 檢查結束復位，允許下一次點擊
                dismissQuietly(progress);
                switch (pickup) {
                    case PICKUP_HAVE_THINGS:
                        showPickupBlockedDialog(context.getString(R.string.there_is_something_at_the_pickup_port));
                        return;
                    case PICKUP_DOOR:
                        showPickupBlockedDialog(context.getString(R.string.pickup_door_not_closed));
                        return;
                    case PICKUP_ELEVATOR:
                        showPickupBlockedDialog(context.getString(R.string.pickup_elevator_not_home));
                        return;
                    case PICKUP_OK:
                        // 收到「正常(01)」= 下位機在線且取貨口空 → 記旗標；
                        // 後續「直接購買」據此略過重複連線判斷，BuyDialog 也略過取貨口重驗。
                        lastPickupOkAtMs = android.os.SystemClock.elapsedRealtime();
                        onPass.run();
                        break;
                    case PICKUP_ALIVE_UNKNOWN:
                        // 下位機在線但此刻沒回報取貨口狀態(忙其他協定，3.5 秒內沒等到 VMC_02)。
                        // 選擇「保守擋住」而非放行：寧可多擋也不漏放「取貨口有東西」。請客人稍候再點一次，
                        // 屆時多半已收到取貨口狀態廣播即可正常判定。(非未連線，故不走重連提示)
                        showPickupBlockedDialog(context.getString(R.string.pickup_status_confirming));
                        break;
                    default:
                        // PICKUP_NO_RESPONSE：機台完全無 VMC 通訊。連線判斷已移除 → 不再擋「未連線」，
                        // 直接放行開詳情頁；連線問題交由出貨階段處理（BuyDialog 取貨口無回應也放行）。
                        onPass.run();
                }
            });
        }).start();
    }

    /**
     * 主動問答式的連線＋取貨口判斷（比照舊專案 DaiLemei，快）。
     * 下位機為輪詢式主從：送 checkSlot 後機台會於下一個 POLL 主動回一筆 VMC_02，通常數百毫秒內即到，
     * controller 收到即更新 lastSlotStatus。故此處送出後只採信「送出時間之後」才到的新回覆（getSlotStatusSince），
     * 一收到明確狀態就立即回傳（早退），不空等；~1、2 秒仍無回應才補送 syncPackNo 自癒；~3.5 秒封頂。
     * 不清讀緩衝：保留廣播快取當「有東西(05)」的保險，避免清緩衝丟掉剛到的廣播（過去漏判主因）。
     */
    private int probePickupHasThing(int slotnum) {
        com.unibuy.smartdevice.controller.DevXinYuanController dev =
                MyApp.getInstance().getDevXinYuanController();
        if (dev == null || !dev.isRun()) return PICKUP_NO_RESPONSE;
        final long startAt = System.currentTimeMillis();
        // 送出佇列若已堆積（browse 階段只會是前次查詢殘留的 checkSlot/syncPackNo），先清掉，
        // 確保這次的 checkSlot 立即排到最前面送出、不排在一堆舊指令後面而在時效內送不出去。
        // 這是連續點商品時「越查越慢、最後全部無回應」的關鍵：不清會一路堆到過載。
        try {
            if (dev.getCountBySendBuffer() > 6) dev.clearSendBuffers();
        } catch (Exception ignore) {}
        // 主動送 checkSlot 促請下位機立即回報取貨口/貨道狀態（不清讀緩衝）。
        try {
            dev.addSendBuffer(dev.getDevXinYuan().checkSlotPre(slotnum, 1));
            dev.addSendBuffer(dev.getDevXinYuan().checkSlot(slotnum));
        } catch (Exception ignore) {}
        final int MAX_MS = 3500;     // 封頂 ~3.5 秒；機台在線通常 <1 秒就回，早退提前結束
        int retries = 0;
        while (System.currentTimeMillis() - startAt < MAX_MS) {
            // 只採信「送出後」才到的新 VMC_02 回覆，避免採到查詢前的舊快取
            int st = com.unibuy.smartdevice.controller.DevXinYuanController.getSlotStatusSince(startAt);
            if (st == 0x05) return PICKUP_HAVE_THINGS;
            if (st == 0x06 || st == 0x09) return PICKUP_DOOR;
            if (st == 0x07) return PICKUP_ELEVATOR;
            if (st == 0x01) return PICKUP_OK;          // ★ 一收到「正常」立即放行，不空等
            // 逾 ~1 秒仍無回應：補送同步包修序號＋再查一次（比照舊專案 syncPackNo 自癒），最多 2 次
            long elapsed = System.currentTimeMillis() - startAt;
            if (retries < 2 && elapsed >= (retries + 1) * 1000L) {
                retries++;
                try {
                    dev.addSendBuffer(dev.getDevXinYuan().syncPackNo((byte) 0x01));
                    dev.addSendBuffer(dev.getDevXinYuan().checkSlot(slotnum));
                } catch (Exception ignore) {}
            }
            try { Thread.sleep(150); } catch (InterruptedException e) { return PICKUP_NO_RESPONSE; }
        }
        // 主動查詢在 3.5 秒內沒等到「送出後」的新回覆：不要立刻判斷線。先退回最近的 VMC_02 廣播——
        // 只要近 7 秒內收過任何 VMC_02，就代表下位機仍在線(這次只是回覆稍慢)，用該狀態判定，
        // 避免把「回覆慢」誤判成「主櫃尚未連線」(實測多數假性未連線的來源)。
        int cached = com.unibuy.smartdevice.controller.DevXinYuanController.getRecentSlotStatus(7000);
        if (cached == 0x05) return PICKUP_HAVE_THINGS;
        if (cached == 0x06 || cached == 0x09) return PICKUP_DOOR;
        if (cached == 0x07) return PICKUP_ELEVATOR;
        if (cached == 0x01) return PICKUP_OK;
        // 沒有 VMC_02，但機台近 4 秒仍有其他 VMC 通訊(POLL/ACK/金錢/狀態等) → 下位機其實在線，
        // 只是此狀態下沒回報取貨口狀態。視為「連線正常但取貨口狀態未知」，別誤判未連線、也別做無謂重連。
        if (com.unibuy.smartdevice.controller.DevXinYuanController.isRecentlyAlive(4000)) {
            return PICKUP_ALIVE_UNKNOWN;
        }
        // 連近 7 秒的廣播、近 4 秒的任何通訊都沒有 → 真的沒回應(交由呼叫端自動重連重試)。
        return PICKUP_NO_RESPONSE;
    }

    private void postMain(Runnable r) {
        new android.os.Handler(android.os.Looper.getMainLooper()).post(r);
    }

    private void dismissQuietly(android.app.AlertDialog d) {
        try { if (d != null && d.isShowing()) d.dismiss(); } catch (Exception ignore) {}
    }

    /** 處理中：轉圈圈 + 提示文字（不可取消）。text 例如「取貨口檢查中......」。 */
    private android.app.AlertDialog buildProgressDialog(String text) {
        android.widget.LinearLayout layout = new android.widget.LinearLayout(context);
        layout.setOrientation(android.widget.LinearLayout.HORIZONTAL);
        layout.setGravity(android.view.Gravity.CENTER_VERTICAL);
        int pad = dp(32);
        layout.setPadding(pad, pad, pad, pad);

        android.widget.ProgressBar spinner = new android.widget.ProgressBar(context);
        android.widget.LinearLayout.LayoutParams sp =
                new android.widget.LinearLayout.LayoutParams(dp(56), dp(56));
        sp.rightMargin = dp(24);
        layout.addView(spinner, sp);

        android.widget.TextView tv = new android.widget.TextView(context);
        tv.setText(text);
        tv.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 28);
        tv.setTextColor(android.graphics.Color.BLACK);
        layout.addView(tv);

        return new android.app.AlertDialog.Builder(context)
                .setView(layout)
                .setCancelable(false)
                .create();
    }

    /** 取貨口異常：跳大字提示框，擋住不開詳情頁。 */
    private void showPickupBlockedDialog(String message) {
        android.app.AlertDialog dialog = new android.app.AlertDialog.Builder(context)
                .setTitle(R.string.abnormal_reminder)
                .setMessage(message)
                .setCancelable(false)
                .setPositiveButton(R.string.i_knew, (d, w) -> d.dismiss())
                .create();
        try { dialog.show(); } catch (Exception ignore) { return; }
        android.widget.TextView msg = dialog.findViewById(android.R.id.message);
        if (msg != null) msg.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 28);
        android.widget.Button btn = dialog.getButton(android.app.AlertDialog.BUTTON_POSITIVE);
        if (btn != null) btn.setTextSize(android.util.TypedValue.COMPLEX_UNIT_SP, 24);
    }

    /** basic 使用 Luzui 移植的現金強化版（卡鈔/找零保障/自癒），上報接 TaiwanStar 新 MQTT。 */
    @Override
    public Class<? extends com.unibuy.smartdevice.DialogAbstract> cashDialogClass() {
        return com.unibuy.smartdevice.ui.dialog.CashPayDialogGeneral.class;
    }

    /**
     * 語言切換時：清掉快取的購物車 fragment 並從 FragmentManager 移除舊的（它已掛在被取代的舊容器上）。
     * 否則購買後再開購物車會拿到孤兒 fragment（openFragment 因 isAdded()==true 跳過重加）→ 畫面空白。
     * 下次 onShoppingCartRequested 會建立新的並掛到新容器。
     */
    @Override
    public void onLocaleSwitched() {
        if (cartFragment != null) {
            try {
                if (context instanceof androidx.fragment.app.FragmentActivity && cartFragment.isAdded()) {
                    ((androidx.fragment.app.FragmentActivity) context).getSupportFragmentManager()
                            .beginTransaction().remove(cartFragment).commitNowAllowingStateLoss();
                }
            } catch (Exception ignore) {}
            cartFragment = null;
        }
    }

    /** 購物車浮鈕：開啟多品項購物車 Fragment。 */
    @Override
    public void onShoppingCartRequested() {
        if (!(context instanceof FontendActivity)) {
            return;
        }
        if (cartFragment == null) {
            cartFragment = new ShoppingCartFragment(handlerMain);
        }
        ((FontendActivity) context).openFragment(cartFragment);
    }

    @Override
    public void onPickupRequested() {
        new FontendPickupCodeDialog(context, handlerMain).show();
    }
}
