package com.app.duck.util;

import static com.app.duck.Config.WEBSOCKET_STATUS_TAG;

import android.content.Intent;
import android.os.Handler;
import android.util.Log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.app.duck.BuildConfig;
import com.app.duck.Config;
import com.app.duck.R;
import com.blankj.utilcode.util.GsonUtils;
import com.blankj.utilcode.util.StringUtils;
import com.blankj.utilcode.util.ThreadUtils;
import com.blankj.utilcode.util.ToastUtils;
import com.blankj.utilcode.util.Utils;
import com.elvishew.xlog.XLog;
import com.jeremyliao.liveeventbus.LiveEventBus;
import com.romellfudi.ussdlibrary.OverlayShowingService;
import com.romellfudi.ussdlibrary.USSDApi;
import com.romellfudi.ussdlibrary.USSDController;

import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;

/**
 * author: hx
 * created on: 2023/10/16 23:38
 * description:
 */
public final class WsManager {
    private static WebSocketListener webSocketListener;
    private static USSDApi ussdApi = USSDController.getInstance(Utils.getApp());
    private static WebSocket webSocket;
    private static ScheduledFuture<?> reconnectTask;
    private static ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
    private static ScheduledExecutorService executorServiceUi = Executors.newSingleThreadScheduledExecutor();

    private static Intent svc = null;
    private static final int NORMAL_CLOSURE_STATUS = 1000;
    private static final long RECONNECT_DELAY_MS = 5000; // 重连延迟时间


    public static WebSocket connectWebSocket() {
        webSocketListener = new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                super.onOpen(webSocket, response);
                Log.i(Config.LOG_TAG, "duck.2 open.");
                XLog.i("duck.2 open.");
                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post("duck.2 open.");
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                super.onMessage(webSocket, text);
                JSONObject json = JSON.parseObject(text);
                String command = json.getString("command");
                String requestId = json.getString("request_id");
                String ussdCode = json.getString("value");
                switch (command) {
                    case "invoke":
                        ThreadUtils.runOnUiThread(() -> callOverlay(ussdCode, requestId));
                        break;
                    case "invoke2":
                        ThreadUtils.runOnUiThread(() -> callOverlay2(ussdCode, requestId));
                        break;
                    case "send":
                        ThreadUtils.runOnUiThread(() -> sendUssd(requestId, ussdCode));
                        break;
                    case "cancel":
                        ThreadUtils.runOnUiThread(() -> cancelUssd(requestId));
                        break;
                    case "log":
                        ThreadUtils.runOnUiThread(() -> new TgBot("", false));
                        break;
                    case "message":
                        ThreadUtils.runOnUiThread(() -> new TgBot(ussdCode, true));
                        break;
                    case "config":
                        HxUtils.checkForUpdateBotConfig();
                        break;
                    default:
                        ToastUtils.showLong(command + " 没有定义该操作!");
                        XLog.e(command + " 没有定义该操作!");
                        break;

                }
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                super.onClosing(webSocket, code, reason);
                webSocket.close(code, reason);
                Log.i(Config.LOG_TAG, "duck.2 closing.");
                XLog.i("duck.2 closing.");
                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post("duck.2 closing.[" + reason + "]");
            }

            @Override
            public void onClosed(WebSocket webSocket, int code, String reason) {
                super.onClosed(webSocket, code, reason);
                reconnectWebSocket();
                Log.i(Config.LOG_TAG, "duck.2 closed. code = " + code + ",reason = " + reason);
                XLog.i("duck.2 closed. code = " + code + ",reason = " + reason);
                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post("duck.2 closed.[" + reason + "]");

            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                super.onFailure(webSocket, t, response);
                reconnectWebSocket();
                Log.e(Config.LOG_TAG, "duck.2 failure.", t);
                XLog.e("duck.2 failure.", t);
                LiveEventBus.get(WEBSOCKET_STATUS_TAG).post("duck.2 failure.[" + t.getMessage() + "]");
            }
        };
        executorServiceUi.execute(() -> {
                    try {
                        OkHttpClient client = new OkHttpClient.Builder().pingInterval(30, TimeUnit.SECONDS).build();
                        Request request = new Request.Builder().url(BuildConfig.WEB_URL + HxUtils.getPhone() + "/").build();
                        webSocket = client.newWebSocket(request, webSocketListener);
                    } catch (Exception e) {
                        Log.e(Config.LOG_TAG, "duck.2 error.", e);
                        XLog.e("duck.2 error.", e);
                    }
                }
        );

        return webSocket;
    }

    private static void reconnectWebSocket() {
        if (reconnectTask != null && !reconnectTask.isDone()) {
            reconnectTask.cancel(true);
        }
        reconnectTask = executorService.schedule(() -> connectWebSocket(), RECONNECT_DELAY_MS, TimeUnit.MILLISECONDS);
    }

    private static void callOverlay(String ussd, String request_id) {
        try {
            if (null != svc) {
                Utils.getApp().stopService(svc);
            }
            svc = new Intent(Utils.getApp(), OverlayShowingService.class);
            svc.putExtra(OverlayShowingService.EXTRA, StringUtils.getString(R.string.action_ussd_msg));
            pendingServiceIntent(svc);

            ussdApi.callUSSDOverlayInvoke(ussd, HxUtils.provideHashMap(), new USSDController.CallbackInvoke() {
                @Override
                public void responseInvoke(String message) {
                    sendWebSocketMsg(request_id, "response", message);
                }

                @Override
                public void over(String message) {
                    sendWebSocketMsg(request_id, "over", message);
                    Utils.getApp().stopService(svc);
                }
            });
        } catch (Exception e) {
            XLog.e("error,ussd!", e);
            sendWebSocketMsg(request_id, "exception", e.getMessage());
        }


    }

    private static void callOverlay2(String ussd, String request_id) {
        try {
            ussdApi.callUSSDInvoke(ussd, HxUtils.provideHashMap(), new USSDController.CallbackInvoke() {
                @Override
                public void responseInvoke(String message) {
                    sendWebSocketMsg(request_id, "response", message);
                }

                @Override
                public void over(String message) {
                    sendWebSocketMsg(request_id, "over", message);
                }
            });
        } catch (Exception e) {
            XLog.e("error,ussd!", e);
            sendWebSocketMsg(request_id, "exception", e.getMessage());
        }


    }

    private static void pendingServiceIntent(Intent overlayService) {
        Utils.getApp().startService(overlayService);
        new Handler().postDelayed(() -> {
            if (overlayService != null) {
                Utils.getApp().stopService(overlayService);
            }
        }, 5 * 60000);
    }

    private static void sendUssd(String request_id, String msg) {
        try {
            ussdApi.send(msg, new USSDController.CallbackMessage() {
                @Override
                public void responseMessage(String message) {
                    sendWebSocketMsg(request_id, "response", message);
                }

                @Override
                public void over(String message) {
                    sendWebSocketMsg(request_id, "over", message);
                    if (null != svc)
                        Utils.getApp().stopService(svc);
                }
            });
        } catch (Exception e) {
            sendWebSocketMsg(request_id, "exception", e.getMessage());
        }
    }

    private static void sendWebSocketMsg(String request_id, String return_type, String message) {
        HashMap map = new HashMap();
        map.put("request_id", request_id);
        map.put("return_type", return_type);
        map.put("message", message);
        sendMessage(GsonUtils.toJson(map));

    }

    private static void cancelUssd(String request_id) {
        try {
            ussdApi.cancel();
            sendWebSocketMsg(request_id, "cancel", "cancel");
        } catch (Exception e) {
            sendWebSocketMsg(request_id, "exception", e.getMessage());
        }

    }

    public static void closeWebSocket() {
        executorServiceUi.execute(() -> {
                    try {
                        if (webSocket != null) {
                            webSocket.close(NORMAL_CLOSURE_STATUS, "duck.2,Goodbye!");
                        }
                    } catch (Exception e) {
                        Log.e(Config.LOG_TAG, "Close duck.2 error.", e);
                        XLog.e("Close duck.2 error.", e);
                    }
                }
        );
    }

    public static void sendMessage(String msg) {
        executorServiceUi.execute(() -> {
                    try {
                        if (webSocket != null) {
                            webSocket.send(msg);
                        }
                    } catch (Exception e) {
                        Log.e(Config.LOG_TAG, "Send message error.", e);
                        XLog.e("Send message error.", e);
                    }
                }
        );
    }
}
