package com.unibuy.smartdevice.tools;

import android.content.Context;
import android.util.Log;

import com.unibuy.smartdevice.MyApp;
import com.unibuy.smartdevice.external.mqtt.MqttManager;
import com.unibuy.smartdevice.structure.BuyStructure;
import com.unibuy.smartdevice.structure.InvoiceStructure;
import com.unibuy.smartdevice.structure.ReportFlowInfoStructure;
import com.unibuy.smartdevice.structure.mqtt.TransactionFinalizePayload;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * 交易結案 Payload 的組裝工具。
 *
 * 使用方式：
 * 1. 在支付成功進入出貨流程前，建立此 Builder 實例。
 * 2. 每次一個貨道出貨完成，呼叫 {@link #addDispenseRecord} 加入結果。
 * 3. 全部出貨完成後，呼叫 {@link #build()} 取得完整的 Payload。
 *
 * 此 Builder 取代舊的 B600 / B601 / B602 分段 HTTP 上報機制。
 */
public class TransactionFinalizeBuilder {

    private static final String TAG = "TransactionFinalizeBuilder";
    private static final SimpleDateFormat SDF =
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
    private static final int ORDER_STATUS_FAILED = 0;
    private static final int ORDER_STATUS_SUCCESS = 1;
    private static final int ORDER_STATUS_PARTIAL_FAILED = 2;

    /** 訂單建立時間（支付完成時的時間點） */
    private final String orderMachineTime;

    /** 機台本地流水號（格式：YYYYMMDDXXXX，例如 202605110001） */
    private String flowId;

    /** 銷貨紀錄訂單號，預設同 flow_id，特殊流程可覆寫 */
    private String orderNo;

    /** 累積的出貨結果清單 */
    private final List<TransactionFinalizePayload.DispenseRecord> dispenseRecords = new ArrayList<>();

    /**
     * 商品明細快照。在「buyList 仍完整」時擷取（例如進入出貨流程當下）；
     * 之後 build() 會優先採用此快照，不再即時讀 live buyList。
     * 用以修正掃碼支付(玉山)等較慢流程：結案前 buyList 可能被取消/倒數路徑清空，
     * 導致 order.items 變成空陣列（出現「無商品紀錄」），但出貨其實成功。
     */
    private List<TransactionFinalizePayload.ItemRecord> itemsSnapshot = null;

    private String codeId = "0";

    /** 交易生命週期狀態（pending/completed/failed/abandoned）；預設 completed 維持成功路徑原行為。 */
    private String status = "completed";

    /** 支付狀態：1=成功、0=失敗/未付款；預設 1 維持成功路徑原行為。 */
    private int paymentStatus = 1;

    /** 失敗結果碼（如刷卡機 0015）；非空時折進 payment_response 方便後台查閱。 */
    private String resultCode = null;

    // 訂單層級欄位的快照（在「全域 flowInfo 仍正確」時由 captureOrderInfo() 擷取）。
    // null 代表未快照 → build() 退回讀全域即時值（保留 publishPending/publishFailed 等同步路徑的原行為）。
    // 出貨是個長流程，期間全域 flowInfo 可能被別的路徑（殘留加購倒數彈出 BuyDialog →
    // initialReportData()）洗回 0；用快照可避免 payment_type/total 在結案當下被誤讀成 0「未知」，
    // 也避免通行碼(5)的不上報判斷失效。與既有 captureItems()（商品明細快照）一致。
    private Integer snapFlowType = null;
    private Integer snapTotalAmount = null;
    private Integer snapChangeAmount = null;
    private Integer snapPointsUsed = null;

    /** 現金收款面額明細快照（僅現金流程有值）；與其他 order 欄位快照同時擷取，避免結案前被全域清空。 */
    private TransactionFinalizePayload.CashDetail snapCashDetail = null;

    public TransactionFinalizeBuilder() {
        this.orderMachineTime = SDF.format(new Date());
        // flow_id 共用「進入付款時生成的 mqttFlowId」，讓 pending → completed/failed 為同一筆，
        // 後台才能依 flow_id 做狀態轉移。若尚未生成（防禦）則即時生成並回存 flowInfo。
        ReportFlowInfoStructure flowInfo = MyApp.getInstance().getReportFlowInfoData();
        String existing = flowInfo != null ? flowInfo.getMqttFlowId() : null;
        if (existing != null && !existing.isEmpty()) {
            this.flowId = existing;
        } else {
            this.flowId = FlowIdGenerator.next(MyApp.getInstance());
            if (flowInfo != null) {
                flowInfo.setMqttFlowId(this.flowId);
            }
        }
        this.orderNo = this.flowId;
        Log.i(TAG, "TransactionFinalizeBuilder created, flow_id=" + this.flowId);
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public void setPaymentStatus(int paymentStatus) {
        this.paymentStatus = paymentStatus;
    }

    public void setResultCode(String resultCode) {
        this.resultCode = resultCode;
    }

    public void setFlowId(String flowId) {
        this.flowId = flowId;
    }

    public String getFlowId() {
        return flowId;
    }

    public void setOrderNo(String orderNo) {
        this.orderNo = orderNo;
    }

    public void setCodeId(String codeId) {
        this.codeId = codeId;
    }

    // =========================================================================
    // 交易生命週期上報（pending / failed）。只在 basic 購物模式（本專案 general/demo）才送：
    //  - basic：以「進付款時生成的同一 flow_id」上報，後台依 flow_id 做 pending → completed/failed 轉移。
    //  - employee_card(晟崴)/pickup_sheet(中國醫)：無實際付款，直接 return 不送，後台資料不受影響。
    // 用 shopping_mode 判斷（雲端 B014 已逐機台下發），不另設旗標。
    // 成功(completed)仍由 DispatchDialog 出貨完成後的既有流程上報，不在此處。
    // =========================================================================

    /** 僅 basic 購物模式才上報生命週期；其餘模式不送。 */
    private static boolean lifecycleLoggingEnabled() {
        return "basic".equals(MyApp.getInstance().getShoppingMode());
    }

    /** 進入付款時上報 pending（進付款一律留底）。非 basic 模式則不送。 */
    public static void publishPending() {
        if (!lifecycleLoggingEnabled()) {
            return;
        }
        try {
            TransactionFinalizeBuilder b = new TransactionFinalizeBuilder();
            b.setStatus("pending");
            b.setPaymentStatus(0);
            MqttManager.getInstance().publishTransactionFinalize(b.build());
            Log.i(TAG, "published pending finalize, flow_id=" + b.getFlowId());
        } catch (Exception e) {
            Log.w(TAG, "publishPending failed: " + e.getMessage());
        }
    }

    /**
     * 付款失敗/逾時時上報。非 basic 模式則不送。
     *  - 0003 交易逾時 / 0006 使用者終止：顧客根本沒完成刷卡（電文無卡號）、不可能扣款，
     *    視為「未完成 abandoned」而非付款失敗 → 後台立即把 pending 轉 abandoned，
     *    列表不顯示為「失敗」，且不帶 code 故不產生刷卡機硬體警告。
     *  - 其餘（空電文、真實拒絕碼 0001/0002/0005 通訊失敗/0015/0016/1001… 等，卡片有讀到或可能已扣款）：
     *    照常上報 failed（帶 code），留底供對帳。
     */
    public static void publishFailed(String resultCode) {
        if (!lifecycleLoggingEnabled()) {
            return;
        }
        // 未完成（沒真的刷到卡）：逾時 / 使用者終止 → 改判 abandoned，不算付款失敗
        boolean abandonedNotFailed = "0003".equals(resultCode) || "0006".equals(resultCode);
        try {
            TransactionFinalizeBuilder b = new TransactionFinalizeBuilder();
            b.setPaymentStatus(0);
            if (abandonedNotFailed) {
                b.setStatus("abandoned");
                // 不帶 code：後台 regex 取不到回應碼 → 不會記為刷卡機硬體警告
                MqttManager.getInstance().publishTransactionFinalize(b.build());
                Log.i(TAG, "published abandoned finalize (no real tap) code=" + resultCode + ", flow_id=" + b.getFlowId());
            } else {
                b.setStatus("failed");
                b.setResultCode(resultCode);
                MqttManager.getInstance().publishTransactionFinalize(b.build());
                Log.i(TAG, "published failed finalize code=" + resultCode + ", flow_id=" + b.getFlowId());
            }
        } catch (Exception e) {
            Log.w(TAG, "publishFailed failed: " + e.getMessage());
        }
    }


    /**
     * 擷取目前 buyList 的商品明細快照。
     * 應在「buyList 仍完整」時呼叫（例如 DispatchDialog 進入出貨流程當下）；
     * 之後 {@link #build()} 會優先採用此快照，不再即時讀 live buyList，
     * 避免結案前 buyList 被取消/倒數等其他執行緒清空，導致 order.items 變成空陣列。
     * buyList 為空時不覆寫既有快照（避免把有效快照洗成空）。
     */
    public void captureItems() {
        List<BuyStructure> buyList = MyApp.getInstance().getBuyList();
        if (buyList == null || buyList.isEmpty()) {
            return;
        }
        this.itemsSnapshot = buildItemRecords(buyList);
        Log.i(TAG, "captureItems: snapshot " + this.itemsSnapshot.size()
                + " item(s), flow_id=" + flowId);
    }

    /**
     * 擷取訂單層級欄位（flowType / 總金額 / 找零 / 點數）的快照。
     * 應在「全域 flowInfo 已是本筆最終值」時呼叫（例如 DispatchDialog 進入出貨、且 CMUH→42
     * 覆寫之後）。之後 {@link #build()} 與「通行碼不上報」判斷皆優先採用此快照，不再即時讀全域，
     * 避免結案前全域被其他路徑清空導致 payment_type/total 落地成 0「未知」或誤報通行碼。
     */
    public void captureOrderInfo() {
        ReportFlowInfoStructure flowInfo = MyApp.getInstance().getReportFlowInfoData();
        if (flowInfo == null) {
            return;
        }
        this.snapFlowType = flowInfo.getFlowType();
        this.snapTotalAmount = flowInfo.getTotalPrice();
        this.snapChangeAmount = flowInfo.getGiveChange();
        this.snapPointsUsed = flowInfo.getUsePoints();
        // 現金流程才擷取面額明細快照（其餘支付方式無投幣統計，維持 null → 不帶 cash_detail）
        if (flowInfo.getFlowType() == 9) {
            this.snapCashDetail = buildCashDetail(flowInfo);
        }
        Log.i(TAG, "captureOrderInfo: flowType=" + snapFlowType + ", total=" + snapTotalAmount
                + ", flow_id=" + flowId);
    }

    /** 由 flowInfo 的收款張數欄位組出 cash_detail（captureOrderInfo 與 build() fallback 共用）。 */
    private TransactionFinalizePayload.CashDetail buildCashDetail(ReportFlowInfoStructure flowInfo) {
        return new TransactionFinalizePayload.CashDetail(
                flowInfo.getCashBill1000(),
                flowInfo.getCashBill500(),
                flowInfo.getCashBill100(),
                flowInfo.getCashCoin50(),
                flowInfo.getCashCoin10(),
                flowInfo.getCashCoin5(),
                flowInfo.getCashCoin1());
    }

    /** 取得快照的 flowType；尚未快照時退回全域即時值。供「通行碼(5)不上報」判斷使用。 */
    public int getOrderFlowType() {
        if (snapFlowType != null) {
            return snapFlowType;
        }
        ReportFlowInfoStructure flowInfo = MyApp.getInstance().getReportFlowInfoData();
        return flowInfo != null ? flowInfo.getFlowType() : 0;
    }

    // 訂單欄位取值：有快照用快照，否則退回全域即時值（build() 共用）。
    private int effectiveFlowType(ReportFlowInfoStructure flowInfo) {
        return snapFlowType != null ? snapFlowType : flowInfo.getFlowType();
    }

    private int effectiveTotalAmount(ReportFlowInfoStructure flowInfo) {
        return snapTotalAmount != null ? snapTotalAmount : flowInfo.getTotalPrice();
    }

    private int effectiveChangeAmount(ReportFlowInfoStructure flowInfo) {
        return snapChangeAmount != null ? snapChangeAmount : flowInfo.getGiveChange();
    }

    private int effectivePointsUsed(ReportFlowInfoStructure flowInfo) {
        return snapPointsUsed != null ? snapPointsUsed : flowInfo.getUsePoints();
    }

    /** 由 buyList 組出 items 明細（captureItems 與 build() fallback 共用）。 */
    private List<TransactionFinalizePayload.ItemRecord> buildItemRecords(List<BuyStructure> buyList) {
        List<TransactionFinalizePayload.ItemRecord> items = new ArrayList<>();
        if (buyList != null) {
            for (BuyStructure buy : buyList) {
                int productId = 0;
                try {
                    productId = Integer.parseInt(buy.getProduct().getProductID());
                } catch (NumberFormatException e) {
                    Log.w(TAG, "Cannot parse productID to int: " + buy.getProduct().getProductID());
                }
                int price = buy.getProduct().getRealPrice();
                int quantity = buy.getCount() > 0 ? buy.getCount() : 1;
                items.add(new TransactionFinalizePayload.ItemRecord(productId, price, quantity));
            }
        }
        return items;
    }

    /**
     * 加入一筆出貨結果記錄（每個貨道出貨後呼叫一次）。
     *
     * @param slotNo          貨道號碼（整數）
     * @param productId       商品 ID（整數，後台 ID）
     * @param amount          此貨道的商品金額
     * @param pointsUsed      使用點數
     * @param isSuccess       是否出貨成功
     * @param remainingStock  出貨後剩餘庫存
     */
    public void addDispenseRecord(int slotNo, int productId, int amount,
                                  int pointsUsed, boolean isSuccess, int remainingStock) {
        TransactionFinalizePayload.DispenseRecord record = new TransactionFinalizePayload.DispenseRecord(
                String.valueOf(slotNo),
                productId,
                amount,
                pointsUsed,
                isSuccess ? 1 : 0,
                remainingStock,
                SDF.format(new Date())
        );
        dispenseRecords.add(record);
        Log.d(TAG, "Added dispense record: slot=" + slotNo + " status=" + (isSuccess ? "success" : "failed"));
    }

    /**
     * 組裝完整的 TransactionFinalizePayload。
     * 應在所有出貨流程結束後呼叫。
     *
     * @return 完整的 MQTT finalize payload
     */
    public TransactionFinalizePayload build() {
        ReportFlowInfoStructure flowInfo = MyApp.getInstance().getReportFlowInfoData();

        TransactionFinalizePayload payload = new TransactionFinalizePayload();

        // --- flow_id：機台本地流水號 (YYYYMMDDXXXX) ---
        // 由 FlowIdGenerator 在建立 Builder 時生成，不依賴後端 API 回傳。
        payload.setFlow_id(flowId);

        // --- code_id：特殊驗證碼 ID (B660/B670/B680 取得) ---
        // 一般現金/電子支付交易為 "0"。
        if (this.codeId != null && !this.codeId.equals("0")) {
            payload.setCode_id(this.codeId);
        } else {
            String accessCodeId = MyApp.getInstance().getReportAccessCodeStructure().getAccessCodeId();
            if (accessCodeId != null && !accessCodeId.isEmpty()) {
                payload.setCode_id(accessCodeId);
            } else {
                payload.setCode_id("0");
            }
        }


        // --- payment_type：支付類型 (Root 級別) ---
        payload.setPayment_type(effectiveFlowType(flowInfo));

        // --- 組裝 order ---
        TransactionFinalizePayload.OrderPayload order = buildOrderPayload(flowInfo);
        order.setDelivery_status(calculateOrderStatus());
        payload.setOrder(order);

        // --- 出貨記錄 ---
        payload.setDispense(new ArrayList<>(dispenseRecords));

        // --- 電子發票（改由後台開立）---
        // 機台不開票，只把「開立輸入」（載具/愛心碼/統編/金額）帶上去，狀態固定 pending；
        // 後台建 pending 發票後派 Job 去綠界開立。無發票需求（invoiceInfo 為空）時不帶，後台不建立發票。
        // 只有付款成功(completed)才建立發票；pending/failed 不開票（避免對未成交的交易開立）。
        if ("completed".equals(status)
                && flowInfo.getInvoiceInfo() != null && !flowInfo.getInvoiceInfo().isEmpty()) {
            TransactionFinalizePayload.InvoicePayload invoicePayload =
                    new TransactionFinalizePayload.InvoicePayload();
            invoicePayload.setStatus("pending");
            invoicePayload.setAmount(effectiveTotalAmount(flowInfo));

            InvoiceStructure invoiceData = MyApp.getInstance().getInvoiceData();
            if (invoiceData != null) {
                invoicePayload.setCarrier_id(invoiceData.getCarrier());
                invoicePayload.setBusiness_tax_id(invoiceData.getCustomerIdentifier());
                invoicePayload.setLove_code(invoiceData.getLoveCode());
            }
            payload.setInvoice(invoicePayload);
        }

        Log.i(TAG, "TransactionFinalizePayload built: flow_id=" + flowId
                + ", order_no=" + order.getOrder_no()
                + ", items=" + order.getItems().size()
                + ", dispense=" + dispenseRecords.size());

        return payload;
    }

    private int calculateOrderStatus() {
        boolean hasSuccess = false;
        boolean hasFailed = false;

        for (TransactionFinalizePayload.DispenseRecord record : dispenseRecords) {
            if (record.getDispense_status() == 1) {
                hasSuccess = true;
            } else {
                hasFailed = true;
            }
        }

        if (hasSuccess && hasFailed) {
            return ORDER_STATUS_PARTIAL_FAILED;
        }
        if (hasSuccess) {
            return ORDER_STATUS_SUCCESS;
        }
        return ORDER_STATUS_FAILED;
    }

    /**
     * 從 MyApp 的全域狀態組裝 OrderPayload。
     */
    private TransactionFinalizePayload.OrderPayload buildOrderPayload(ReportFlowInfoStructure flowInfo) {
        TransactionFinalizePayload.OrderPayload order = new TransactionFinalizePayload.OrderPayload();

        order.setOrder_no(orderNo != null && !orderNo.isEmpty() ? orderNo : flowId);

        // 金額資訊（優先採快照，未快照時退回全域即時值）
        int totalAmount = effectiveTotalAmount(flowInfo);
        order.setOriginal_amount(totalAmount);
        order.setDiscount_amount(0); // TODO: 如有折扣活動需進一步填入
        order.setTotal_amount(totalAmount);
        order.setPay_amount(totalAmount);
        order.setChange_amount(effectiveChangeAmount(flowInfo));
        order.setPoints_used(effectivePointsUsed(flowInfo));

        // 現金交易(payment_type=9)才帶面額明細 cash_detail：優先用快照，未快照時退回全域即時值。
        if (effectiveFlowType(flowInfo) == 9) {
            order.setCash_detail(snapCashDetail != null ? snapCashDetail : buildCashDetail(flowInfo));
        }

        // 支付類型：flowType 直接對應 payment_type
        order.setPayment_type(effectiveFlowType(flowInfo));
        // 生命週期狀態與支付狀態：成功路徑沿用預設(completed/1)；pending/failed 由 setter 覆寫。
        order.setStatus(status);
        order.setPayment_status(paymentStatus);

        // 支付流程原始資料
        order.setPayment_request(
                flowInfo.getFlowSendInfo() != null ? flowInfo.getFlowSendInfo() : "");
        String paymentResponse = flowInfo.getFlowRequestInfo() != null ? flowInfo.getFlowRequestInfo() : "";
        if (resultCode != null && !resultCode.isEmpty()) {
            paymentResponse = paymentResponse + " | code=" + resultCode;
        }
        order.setPayment_response(paymentResponse);

        // 會員資訊
        order.setMember_barcode(
                flowInfo.getFlowBarCode() != null ? flowInfo.getFlowBarCode() : "");

        // 發票資訊
        order.setInvoice_info(
                flowInfo.getInvoiceInfo() != null ? flowInfo.getInvoiceInfo() : "");

        // 機台時間
        order.setMachine_time(orderMachineTime);

        // --- 組裝購買商品清單 (items) ---
        // 優先採用 captureItems() 在出貨流程開始時擷取的快照；無快照時才即時讀 live buyList。
        // 快照可避免結案前 buyList 被其他執行緒清空（掃碼支付等慢流程的競態），造成「無商品紀錄」。
        List<TransactionFinalizePayload.ItemRecord> items;
        if (itemsSnapshot != null && !itemsSnapshot.isEmpty()) {
            items = new ArrayList<>(itemsSnapshot);
        } else {
            items = buildItemRecords(MyApp.getInstance().getBuyList());
        }
        order.setItems(items);

        return order;
    }
}
