package com.unibuy.smartdevice.tools;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

import com.unibuy.smartdevice.exception.ErrorCode;
import com.unibuy.smartdevice.tools.SimpleCashAcceptorHandler;

import java.util.ArrayList;
import java.util.List;

/**
 * 遠端找零管理器
 * 負責處理 MDB 設備的連線、初始化、庫存查詢及退幣/退鈔指令發送。
 */
public class CashPayoutManager {
    private static final String TAG = "CashPayoutManager";

    public interface PayoutListener {
        void onProgress(String message);
        void onSuccess();
        void onFailure(String error);
    }

    private final Context context;
    private final PayoutListener listener;
    private SimpleCashAcceptorHandler handler;
    private final Handler mainHandler = new Handler(Looper.getMainLooper());

    // 庫存變數
    private int coin50_count = 0;
    private int coin10_count = 0;
    private int coin5_count = 0;
    private int coin1_count = 0;
    private int note100_count = 0;

    public CashPayoutManager(Context context, PayoutListener listener) {
        this.context = context;
        this.listener = listener;
    }

    public void startPayout(int amount) {
        Log.i(TAG, "Starting remote payout for amount: " + amount);
        listener.onProgress("正在初始化退幣系統...");

        handler = new SimpleCashAcceptorHandler(context, event -> {
            Log.d(TAG, "MDB Event: " + event);
            handleMdbEvent(event);
        });

        new Thread(() -> {
            String connectResult = handler.connect();
            if ("成功連接到 USB 轉接器！".equals(connectResult)) {
                mainHandler.post(() -> {
                    handler.startReading();
                    performInitializationSequence(amount);
                });
            } else {
                mainHandler.post(() -> listener.onFailure("MDB 連線失敗: " + connectResult));
            }
        }).start();
    }

    private void performInitializationSequence(int amount) {
        // 依序發送初始化與查詢指令
        // 指令序列參考 CashMachineSettings 與 CashPayDialog
        sendMdbCommand("0C00000000", 0, () -> { // 先禁止硬幣接收
        sendMdbCommand("3400000000", 500, () -> { // 禁止紙鈔接收
        sendMdbCommand("370100000002", 500, () -> { // 設定紙鈔類型
        sendMdbCommand("3703", 500, () -> { // 查詢設置
        sendMdbCommand("3705", 500, () -> { // 查詢紙鈔庫存
        sendMdbCommand("0A", 1000, () -> { // 查詢硬幣庫存
            calculateAndExecutePayout(amount);
        });});});});});});
    }

    private void calculateAndExecutePayout(int amount) {
        listener.onProgress("正在計算退幣組合...");
        List<ChangeCommand> commands = new ArrayList<>();
        int remaining = amount;

        // 1. 100元紙鈔
        int hundredsToGive = Math.min(remaining / 100, note100_count);
        if (hundredsToGive > 0) {
            String cmd = "370600000" + String.format("%01X", hundredsToGive);
            commands.add(new ChangeCommand(cmd, 100, hundredsToGive));
            remaining -= hundredsToGive * 100;
        }

        // 2. 50元硬幣
        int fiftiesToGive = Math.min(remaining / 50, coin50_count);
        if (fiftiesToGive > 0) {
            String cmd = "0D" + String.format("%01X", fiftiesToGive) + "4";
            commands.add(new ChangeCommand(cmd, 50, fiftiesToGive));
            remaining -= fiftiesToGive * 50;
        }

        // 3. 10元硬幣
        int tensToGive = Math.min(remaining / 10, coin10_count);
        if (tensToGive > 0) {
            String cmd = "0D" + String.format("%01X", tensToGive) + "2";
            commands.add(new ChangeCommand(cmd, 10, tensToGive));
            remaining -= tensToGive * 10;
        }

        // 4. 5元硬幣
        int fivesToGive = Math.min(remaining / 5, coin5_count);
        if (fivesToGive > 0) {
            String cmd = "0D" + String.format("%01X", fivesToGive) + "1";
            commands.add(new ChangeCommand(cmd, 5, fivesToGive));
            remaining -= fivesToGive * 5;
        }

        // 5. 1元硬幣
        int onesToGive = Math.min(remaining, coin1_count);
        if (onesToGive > 0) {
            String cmd = "0D" + String.format("%01X", onesToGive) + "0";
            commands.add(new ChangeCommand(cmd, 1, onesToGive));
            remaining -= onesToGive;
        }

        if (remaining > 0) {
            Log.w(TAG, "Insufficient inventory to payout full amount. Missing: " + remaining);
            // 依然執行現有的，但回報錯誤訊息
        }

        if (commands.isEmpty()) {
            listener.onFailure("無需找零或庫存不足以找零");
            close();
            return;
        }

        executePayoutCommands(commands, 0);
    }

    private void executePayoutCommands(List<ChangeCommand> commands, int index) {
        if (index >= commands.size()) {
            listener.onProgress("退幣完成");
            // 延遲關閉，確保指令處理完畢
            mainHandler.postDelayed(() -> {
                close();
                listener.onSuccess();
            }, 2000);
            return;
        }

        ChangeCommand current = commands.get(index);
        listener.onProgress("正在執行: " + current.denomination + "元 x " + current.count);
        
        sendMdbCommand(current.command, 0, () -> {
            // 退幣指令後需要較長延遲
            executePayoutCommands(commands, index + 1);
        });
    }

    private void sendMdbCommand(String hexCommand, int delay, Runnable onComplete) {
        mainHandler.postDelayed(() -> {
            new Thread(() -> {
                try {
                    byte[] command = hexStringToByteArray(hexCommand);
                    handler.writeData(command);
                    if (onComplete != null) {
                        mainHandler.post(onComplete);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Error sending MDB command: " + hexCommand, e);
                }
            }).start();
        }, Math.max(delay, 1000)); // 每個指令至少間隔 1 秒，退幣可能需要更長
    }

    private void handleMdbEvent(String event) {
        if (event == null || event.trim().isEmpty()) return;
        String[] parts = event.trim().split("\\s+");

        // 解析硬幣庫存 (0A 回傳)
        if (parts.length == 19) {
            try {
                coin1_count = Integer.parseInt(parts[2], 16);
                coin5_count = Integer.parseInt(parts[3], 16);
                coin10_count = Integer.parseInt(parts[4], 16);
                coin50_count = Integer.parseInt(parts[6], 16);
                Log.d(TAG, String.format("Inventory Update: 1=%d, 5=%d, 10=%d, 50=%d", 
                    coin1_count, coin5_count, coin10_count, coin50_count));
            } catch (Exception e) {
                Log.e(TAG, "Parse coin inventory error", e);
            }
        }

        // 解析百鈔庫存 (3705 回傳)
        if (parts.length >= 30) {
            try {
                int noteCount = Integer.parseInt(parts[3], 16);
                if (noteCount >= 0 && noteCount <= 100) {
                    note100_count = noteCount;
                    Log.d(TAG, "Inventory Update: 100=" + note100_count);
                }
            } catch (Exception e) {
                Log.e(TAG, "Parse bill inventory error", e);
            }
        }
    }

    private void close() {
        if (handler != null) {
            handler.stopReading();
            handler.close();
            handler = null;
        }
    }

    private byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }

    private static class ChangeCommand {
        final String command;
        final int denomination;
        final int count;

        ChangeCommand(String command, int denomination, int count) {
            this.command = command;
            this.denomination = denomination;
            this.count = count;
        }
    }
}
