package com.zhimai.websocket.util;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.google.gson.Gson;
import com.zhimai.websocket.Config;
import com.zhimai.websocket.RxWebSocket;
import com.zhimai.websocket.RxWebSocketUtil;
import com.zhimai.websocket.WebSocketSubscriber;
import com.zhimai.websocket.bean.CommonMsgData;
import com.zhimai.websocket.bean.MessageSqlBean;
import com.zhimai.websocket.db.MessageDBManager;
import com.zhimai.websocket.http.CallBackListener;
import com.zhimai.websocket.http.MyOkHttpUtils;
import com.zhimai.websocket.listener.MessageQueueServiceListner;
import com.zhimai.websocket.listener.QueueMessageBackListener;
import com.zhimai.websocket.listener.SocketConnectListener;
import com.zhimai.websocket.listener.SocketConnectListener2;
import com.zhimai.websocket.listener.SocketMessageBackListener;
import com.zhimai.websocket.listener.WebSocketSubscriberListener;
import com.zhimai.websocket.queue.MessageQueue;
import com.zhimai.websocket.queue.QueueMessageService;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.litepal.LitePal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.annotations.NonNull;
import okhttp3.RequestBody;
import okhttp3.WebSocket;
import okio.ByteString;

/**
 * 推送消息websocket通用工具
 * 工具调用入口
 */
public class MsgWebSocketUtil {
    private static final String TAG = "---MsgWebSocketUtil----";

    private WebSocketSubscriber webSocketSubscriber;

    public final static int BUILD_TYPE_DEV = 0;
    public final static int BUILD_TYPE_SHOP = 1;
    public final static int BUILD_TYPE_BETA = 2;
    public final static int BUILD_TYPE_RELEASE = 3;

    private String basicUrl = "https://inapi.qimai.cn/";//默认正式环境接口
    private String baseUrl = basicUrl + "gw/";
    private String getMsgUrl_end = "soc-center/soc-msg/list-soc-msg";
    private String confirmMsgUrl_end = "soc-center/soc-msg/confirm-soc-msg";
    private String confirmMoreMsgUrl_end = "soc-center/soc-msg/batch-confirm-soc-msg";
    private String getMsgUrl = baseUrl + getMsgUrl_end;
    private String confirmMsgUrl = baseUrl + confirmMsgUrl_end;
    private String confirmMoreMsgUrl = baseUrl + confirmMoreMsgUrl_end;

    private String token = "";
    private String client_id = "";

    /**
     * 心跳ping的内容
     */
    private String pingData = "{\"type\":\"ping\",\"data\":{\"newVerWithMsgConfirm\":true}}";
    /**
     * 心跳Ping的频率
     */
    private int pingRate = 5000;//ping 频率
    private static MsgWebSocketUtil msgWebSocketUtil;
    private WebSocket mWebSocket;
    private int mQueueInterval = 1000;
    /**
     * socket链接状态
     */
    private boolean isConnected = false;
    /**
     * socket地址
     */
    private String webSocketUrl = "";
    //通道
    private String socketWay = "";
    /**
     * 设备号
     * 绑定socket连接时使用，也是推送唯一标识
     */
    private String DeviceName = "";
    /**
     * 是否打印日志
     */
    private boolean isShowLog = true;
    /**
     * 是否开启队列
     */
    private boolean useQueue = false;
    /**
     * 禁用队列时 消息监听
     */
    private SocketMessageBackListener socketMessageListener;
    /**
     * 开启队列时 消息监听
     */
    private QueueMessageBackListener queueMessageListener;
    /**
     * socket连接监听
     */
    private SocketConnectListener socketConnectListener;
    private WebSocketSubscriberListener mWebSocketSubscriberListener;

    /**
     * 离线时间
     * 离线达到一定时间，自动重启socket
     */
    private int offLineTime = 0;

    private Context context;

    //socket2.0 涉及参数
    private boolean isSocket2 = false;
    private boolean openTimingGetMessage = false;
    private int getMessageInterval = 60000;

    private Gson gson;

    private MsgWebSocketUtil() {
    }

    private MsgWebSocketUtil(Context context) {
        this.context = context.getApplicationContext();
        gson = new Gson();
    }

    /**
     * 入口
     *
     * @return
     */
//    public static MsgWebSocketUtil getInstance() {
//        if (msgWebSocketUtil == null) {
//            msgWebSocketUtil = new MsgWebSocketUtil();
//        }
//        return msgWebSocketUtil;
//    }
    public static MsgWebSocketUtil getInstance(Context context) {
        if (msgWebSocketUtil == null) {
            msgWebSocketUtil = new MsgWebSocketUtil(context);
        }
        return msgWebSocketUtil;
    }

    /**
     * 初始化数据库，此方法必须在 application 中调用
     */
    public void initSql() {
        LitePal.initialize(context);
        MessageDBManager.deleteYesterdayAllHistory(context);
    }

    /**
     * 设置socket地址
     *
     * @param url
     */
    public MsgWebSocketUtil setUrl(String url) {
        webSocketUrl = url;
        if (StringUtil.isNotNull(webSocketUrl)) {
            socketWay = webSocketUrl.substring(webSocketUrl.lastIndexOf("/") + 1);
        }
        return msgWebSocketUtil;
    }

    /**
     * 设置 socket 2.0 接口基地址
     *
     * @param url
     * @return
     */
    public MsgWebSocketUtil setHttpBasicUrl(String url) {
//        basicUrl = "http://inapi.qimai.shop";
        basicUrl = url;
        baseUrl = basicUrl + "gw/";
        getMsgUrl = baseUrl + getMsgUrl_end;
        confirmMsgUrl = baseUrl + confirmMsgUrl_end;
        confirmMoreMsgUrl = baseUrl + confirmMoreMsgUrl_end;
        return msgWebSocketUtil;
    }

    //与上面接口一样功能，使用一个即可
    public MsgWebSocketUtil setHttpBasicUrl(int build_type) {
//        basicUrl = "http://inapi.qimai.shop";
        switch (build_type) {
            case BUILD_TYPE_DEV:
                basicUrl = "http://inapi.zmcms.cn/";
                break;
            case BUILD_TYPE_SHOP:
                basicUrl = "http://inapi.qimai.shop/";
                break;
            case BUILD_TYPE_BETA:
                basicUrl = "http://inapi.qimai.co/";
                break;
            case BUILD_TYPE_RELEASE:
            default:
                basicUrl = "http://inapi.qimai.cn/";
                break;
        }
        baseUrl = basicUrl + "gw/";
        getMsgUrl = baseUrl + getMsgUrl_end;
        confirmMsgUrl = baseUrl + confirmMsgUrl_end;
        confirmMoreMsgUrl = baseUrl + confirmMoreMsgUrl_end;
        return msgWebSocketUtil;
    }

    public MsgWebSocketUtil setToken(String token) {
        this.token = token;
        return msgWebSocketUtil;
    }

    /**
     * 设置是否打印日志
     *
     * @param is_show
     */
    public MsgWebSocketUtil showLog(boolean is_show) {
        Logger.LOG_ENABLE = isShowLog = is_show;
        return msgWebSocketUtil;
    }

    /**
     * 设置设备号
     *
     * @param device_name 绑定socket时使用
     */
    public MsgWebSocketUtil setDeviceName(String device_name) {
        DeviceName = device_name;
        return msgWebSocketUtil;
    }

    /**
     * ping频率
     * socket ping的频率
     * 默认 5000ms
     *
     * @param ping_rate
     */
    public MsgWebSocketUtil setPingRate(int ping_rate) {
        pingRate = ping_rate;
        return msgWebSocketUtil;
    }

    /**
     * 设置ping内容
     * 默认  "{\"type\":\"ping\",\"data\":\"\"}"
     * 如有改动可用此方法修改 ping的内容
     *
     * @param ping_data
     */
    public MsgWebSocketUtil setPingData(String ping_data) {
        pingData = ping_data;
        return msgWebSocketUtil;
    }

    /**
     * 是否开启队列
     * 若不开启 接收到消息直接下放
     *
     * @param use_queue
     */
    public MsgWebSocketUtil useQueue(boolean use_queue) {
        useQueue = use_queue;
        return msgWebSocketUtil;
    }

    /**
     * socket消息回调
     * 不开启队列时，使用此消息监听
     *
     * @param socketMessageListener_
     */
    public MsgWebSocketUtil setSocketMessageListener(SocketMessageBackListener socketMessageListener_) {
        socketMessageListener = socketMessageListener_;
        return msgWebSocketUtil;
    }

    /**
     * queue消息回调
     * 开启队列时，使用此消息监听
     *
     * @param queueMessageListener_
     */
    public MsgWebSocketUtil setQueueMessageListener(QueueMessageBackListener queueMessageListener_) {
        queueMessageListener = queueMessageListener_;
        return msgWebSocketUtil;
    }

    public MsgWebSocketUtil setSocketConnectListener(SocketConnectListener socketConnectListener_) {
        socketConnectListener = socketConnectListener_;
        return msgWebSocketUtil;
    }

    /**
     * 是否是socket2.0
     *
     * @param is_socket_2
     * @return
     */
    public MsgWebSocketUtil isSocket2(boolean is_socket_2) {
        this.isSocket2 = is_socket_2;
        return msgWebSocketUtil;
    }

    /**
     * 是否开启 定时拉取消息
     *
     * @param is_open_timing_getmessage
     * @return
     */
    public MsgWebSocketUtil isopenTimingGetMessage(boolean is_open_timing_getmessage) {
        this.openTimingGetMessage = is_open_timing_getmessage;
        return msgWebSocketUtil;
    }

    /**
     * 设置拉取消息 间隔 时间
     *
     * @param time_interval
     * @return
     */
    public MsgWebSocketUtil setTimingGetMessageInterval(int time_interval) {
        this.getMessageInterval = time_interval;
        return msgWebSocketUtil;
    }

    /**
     * 连接socket
     * 最后调用此方法
     */
    public void initSocket() {
        if (StringUtil.isNull(webSocketUrl)) {
            Logger.e(TAG, "webSocketUrl is null");
            return;
        }
        if (StringUtil.isNull(DeviceName)) {
            Logger.e(TAG, "DeviceName is null");
            return;
        }
        Logger.e(TAG, "初始化socket  initSocket()");
        //是否开启日志
        RxWebSocketUtil.getInstance().setShowLog(isShowLog);

        //初始化ping定时器
        initPing();
        // ping/pong 设置：在设置config的时候，从okhttpclient中配置，设置5秒的心跳
//        OkHttpClient client = new OkHttpClient.Builder()
//                .connectTimeout(5, TimeUnit.SECONDS)
//                .readTimeout(3,TimeUnit.SECONDS)
//                .writeTimeout(3,TimeUnit.SECONDS)
//                .pingInterval(3, TimeUnit.SECONDS)
//                .build();
        Config config = new Config.Builder()
//                .setClient(client)   //if you want to set your okhttpClient
                .setShowLog(isShowLog, TAG)
                .setReconnectInterval(2, TimeUnit.SECONDS)  //set reconnect interval
//                .setSSLSocketFactory(yourSSlSocketFactory, yourX509TrustManager) // wss support
                .build();
        RxWebSocket.setConfig(config);

        Logger.e(TAG, "开启长连接；；" + webSocketUrl);
//        RxWebSocket.get(MyApplication.WEBSOCKET_HOST_AND_PORT)

        webSocketSubscriber = new WebSocketSubscriber() {
            @Override
            public void onOpen(@NonNull WebSocket webSocket) {
                Logger.e(TAG, "onOpen1:");
                if (mWebSocketSubscriberListener != null) {
                    mWebSocketSubscriberListener.onOpen(webSocket);
                }
                mWebSocket = webSocket;
                isConnected = true;
                if (socketConnectListener != null) {
                    socketConnectListener.connected();
                } else {
                    Logger.e(TAG, "socketConnectListener==null");
                }
            }

            @Override
            public void onMessage(@NonNull String text) {
                Logger.e(TAG, "onMessage;;" + text);
                if (mWebSocketSubscriberListener != null) {
                    mWebSocketSubscriberListener.onMessage(text);
                }
                checkMessage(text);
            }

            @Override
            public void onMessage(@NonNull ByteString bytes) {
                if (mWebSocketSubscriberListener != null) {
                    mWebSocketSubscriberListener.onMessage(bytes);
                }
                Logger.e(TAG, bytes.toString());
            }

            @Override
            protected void onReconnect() {
                Logger.e(TAG, "重连");
                if (mWebSocketSubscriberListener != null) {
                    mWebSocketSubscriberListener.onReconnect();
                }
                isConnected = false;
                if (socketConnectListener != null) {
                    socketConnectListener.disconnect();
                } else {
                    Logger.e(TAG, "socketConnectListener==null");
                }
            }

            @Override
            protected void onClose() {
                if (mWebSocketSubscriberListener != null) {
                    mWebSocketSubscriberListener.onClose();
                }
                Logger.e(TAG, "关闭");
                if (socketConnectListener != null) {
                    socketConnectListener.closed();
                } else {
                    Logger.e(TAG, "socketConnectListener==null");
                }
            }

            @Override
            public void onError(Throwable e) {
                if (mWebSocketSubscriberListener != null) {
                    mWebSocketSubscriberListener.onError(e);
                }
                Logger.e(TAG, "错误：" + e.getMessage());
            }
        };

        RxWebSocket.get(webSocketUrl)
//        RxWebSocket.get("ws://qmapp.dev.zmcms.cn:8282")
                //RxLifecycle : https://github.com/dhhAndroid/RxLifecycle
//                .compose(RxLifecycle.with(MyApplication.mContext).<WebSocketInfo>bindToLifecycle())//如果需要跟activity生命绑定，防止内存泄漏，可以用这个方法
                .subscribe(webSocketSubscriber);

        //如果开启队列，则启动队列消息服务
        if (useQueue) {
            QueueMessageService.getInstance().setInterval(mQueueInterval);
            QueueMessageService.getInstance().startPrintService(new MessageQueueServiceListner() {
                @Override
                public void getMessage(String message) {
                    if (queueMessageListener != null) {
                        queueMessageListener.reciever(message);
                    } else {
                        Logger.e(TAG, "queueMessageListener==null");
                    }
                }
            });

        }
    }

    /**
     * 处理分发接收到的消息
     * 根据接收的消息，进行相应的处理，这里随便写的，要根据自己定的规则进行处理
     */
    private void checkMessage(String message) {
        try {
            Log.d(TAG, "checkMessage: message= " + message);
            JSONObject jsonObject = new JSONObject(message);
            String type = jsonObject.optString("type");
            switch (type) {
                case "init"://初始化/验证
                    sendBind(jsonObject);
                    break;
                case "success":
                    //不是socket1.0
                    if (!isSocket2) {
                        //判断是否存在tag，tag不为空就 通知socket服务端，收到了此消息-----------
                        if (useQueue) {//如果开启队列，消息加入队列---------------------------------------------
                            MessageQueue.getInstance().enQueue(message);
                        } else {//直接 回调 传消息
                            if (socketMessageListener != null) {
                                socketMessageListener.recieveMessage(message);
                            } else {
                                Logger.e(TAG, "socketMessageListener==null");
                            }
                        }
                    } else {
                        //验证成功//2.0有消息的时候，type返回的竟然也是这个 0.o
                        //这里优化，如果是通知 有新消息，请求接口后 筛重 再塞队列
                        if (StringUtil.isNotNull(jsonObject.optString("new_message"))) {
                            if (jsonObject.optString("new_message").equals("true")) {
                                getSocketServiceMessage();
                            }
                        } else {
                            Logger.e(TAG, "socket绑定成功");
                        }
                    }
                    //判断是否存在tag，tag不为空就 通知socket服务端，收到了此消息-----------
//                    if (useQueue) {//如果开启队列，消息加入队列---------------------------------------------
//                        MessageQueue.getInstance().enQueue(message);
//                    } else {//直接 回调 传消息
//                        if (socketMessageListener != null) {
//                            socketMessageListener.recieveMessage(message);
//                        } else {
//                            Logger.e(TAG, "socketMessageListener==null");
//                        }
//                    }


                    break;
                case "error"://验证失败
                    Logger.e(TAG, "socket绑定失败");
                    break;
                default://其它的消息 下放给调用者 或 直接扔到队列中
                    //判断是否存在tag，tag不为空就 通知socket服务端，收到了此消息-----------
                    if (StringUtil.isNotNull(jsonObject.optString("tag"))) {
                        String tag = jsonObject.optString("tag");
                        boolean needConfirm = jsonObject.optBoolean("needConfirm");
                        Logger.e(TAG, "发送确认消息数据：needConfirm = " + needConfirm + "  内容：" + getConfirmMessage(tag));
                        if (mWebSocket != null && needConfirm) {
                            mWebSocket.send(getConfirmMessage(tag));
                        }

                        //数据库 存储消息id，判断数据库是否已有此消息，如果存在 忽略消息，不存在则存储消息，继续下一步--------------------------------
                        if (MessageDBManager.isRecieverMessage(tag)) {
                            Logger.e(TAG, "------该消息数据库已存在  tag = " + tag);
                            return;
                        }
                        //数据库记录消息id
                        MessageDBManager.addMessage(tag);
                    }
                    if (useQueue) {//如果开启队列，消息加入队列---------------------------------------------
                        Logger.e(TAG, "------消息加入队列 ");
                        MessageQueue.getInstance().enQueue(message);
                    } else {//直接 回调 传消息
                        if (socketMessageListener != null) {
                            socketMessageListener.recieveMessage(message);
                        } else {
                            Logger.e(TAG, "socketMessageListener==null");
                        }
                    }
                    break;
            }
        } catch (JSONException e) {

        }
    }

    /**
     * 通知服务端 消息收到成功
     *
     * @param tag
     * @return
     */
    private String getConfirmMessage(String tag) {
        return "{\"type\": \"msg-confirm\",\"data\":{\"tag\":\"" + tag + "\"}}";
    }

    /**
     * 发送绑定/验证 请求
     */
    private void sendBind(JSONObject jsonObject) {
        try {
            Logger.e(TAG, "sendBind()被调用");
            JSONObject bindJson = new JSONObject();
            bindJson.put("type", "bind-uid");
            client_id = jsonObject.optString("client_id");
            bindJson.put("key", jsonObject.optString("client_id"));

            //data数据组装
            JSONObject dataJson = new JSONObject();
            dataJson.put("id", DeviceName);
            bindJson.put("data", dataJson);

            if (mWebSocket != null) {
                Logger.e(TAG, "发送绑定数据：" + bindJson.toString());
                mWebSocket.send(bindJson.toString());
            }
        } catch (Exception e) {
            Logger.e(TAG, "sendBind 错误:" + e.getMessage());
        }
    }

    /**
     * 发送消息方法
     *
     * @param message
     * @return
     */
    public boolean sendMessage(String message) {
        if (!isConnected) {
            Logger.e(TAG, "socket已断开" + message);
            return false;
        }

        Logger.e(TAG, "sendMessage()被调用");
        if (mWebSocket != null) {
            Logger.e(TAG, "发送数据：" + message);
            mWebSocket.send(message);
            return true;
        } else {
            Logger.e(TAG, "mWebSocket==null");
            return false;
        }
    }

    /**
     * 获取socket连接状态
     * true:已连接
     * false:连接断开 或 未初始化
     *
     * @return
     */
    public boolean getConnectStatus() {
        return isConnected;
    }

    /**
     * 开始定时发送心跳
     * 这个是监测 连接是否正常的规则，可自定义
     */
    private void initPing() {
        RxTimerUtil.cancel(SysCode.RX_TIMER_TYPE.SOCKET);
        RxTimerUtil.interval(SysCode.RX_TIMER_TYPE.SOCKET, pingRate, new RxTimerUtil.IRxNext() {
            @Override
            public void doNext(long number) {
                if (isConnected) {
                    if (mWebSocket != null) {
                        boolean isPing = mWebSocket.send(pingData);
                        if (socketConnectListener != null && socketConnectListener instanceof SocketConnectListener2) {
                            ((SocketConnectListener2) socketConnectListener).ping(isPing);
                        }
                        if (isPing) {
                            offLineTime = 0;
                        } else {
                            offLineTime++;
                        }
                        Logger.e(TAG, isPing ? ("ping... 内容：" + pingData) : "ping失败...");
                    }
                } else {
                    offLineTime++;
                    Logger.e(TAG, "Socket中断，停止ping...offLineTime" + offLineTime);
                }

                if (offLineTime > 10) {//超过10次ping（50S）失败/离线50S  重启socket
                    destroySocket();
                    initSocket();
                }
                if (context != null) {
                    if (NetWorkUtils.isOffline(context)) {
                        Toast.makeText(context, "请检查当前网络", Toast.LENGTH_SHORT).show();
                    }
                }

                //在ping里面触发 定时消息请求
                if (isSocket2 && openTimingGetMessage) {
                    if (number * pingRate % getMessageInterval == 0) {
                        //定时拉取服务端打印消息
                        getSocketServiceMessage();
                    } else {
                        Logger.e(TAG, "number = " + number + "  ;; pingRate = " + pingRate + "  ;; getMessageInterval = " + getMessageInterval);
                    }
                } else {
                    Logger.e(TAG, "isSocket2 = " + isSocket2 + "  ;; openTimingGetMessage = " + openTimingGetMessage);
                }
            }
        });
    }

    public void destroySocket() {
        if (webSocketSubscriber != null) {
            webSocketSubscriber.dispose();
            Logger.e(TAG, "socket服务已关闭...");
        }
        RxTimerUtil.cancel(SysCode.RX_TIMER_TYPE.SOCKET);
        Logger.e(TAG, "socket心跳已关闭...");
    }

    public MsgWebSocketUtil setWebSocketSubscriberListener(WebSocketSubscriberListener mWebSocketSubscriberListener) {
        this.mWebSocketSubscriberListener = mWebSocketSubscriberListener;
        return this;

    }

    public MsgWebSocketUtil setQueueInterval(int mQueueInterval) {
        this.mQueueInterval = mQueueInterval;
        return this;
    }

    /**
     * socket 2.0
     * 从接口拉取消息
     */
    private void getSocketServiceMessage() {
        Map<String, String> params = new HashMap<>();
        params.put("pageNum", "1");
        params.put("pageSize", "10");
        params.put("clientUid", socketWay + "_" + DeviceName);
//        params.put("version_num", BuildConfig.VERSION_CODE + "");
        MyOkHttpUtils.getInstance().post(getMsgUrl, params, token, basicUrl, new CallBackListener() {
            @Override
            public void error(String error) {
                Logger.e(TAG, "get_msg_error : " + error);
            }

            @Override
            public void fail(String message) {
                Logger.e(TAG, "get_msg_fail : " + message);
            }

            @Override
            public void success(String content) {
                Logger.e(TAG, "get_msg_success : " + content);
                try {
                    JSONObject jsonObject = new JSONObject(content);
                    JSONArray jsonArray = jsonObject.optJSONArray("list");
                    ArrayList<CommonMsgData> lsTag = new ArrayList<>();
                    for (int i = 0; i < jsonArray.length(); i++) {
                        JSONObject object = jsonArray.getJSONObject(i);
                        checkMessage(object.toString());//check 消息
                        lsTag.add(gson.fromJson(object.toString(), CommonMsgData.class));
                    }
                    batchConfirmMsg(lsTag);

                } catch (Exception e) {
                    Logger.e(TAG, "get_msg_success : 遍历解析数据失败" + e.getMessage());
                }
            }
        });

    }

    private void batchConfirmMsg(ArrayList<CommonMsgData> ls_tag) {

        //////////////
        RequestBody body = null;
        okhttp3.FormBody.Builder formEncodingBuilder = new okhttp3.FormBody.Builder();
        for (int i = 0; i < ls_tag.size(); i++) {
            formEncodingBuilder.add("tag[]", ls_tag.get(i).getTag());
        }
        formEncodingBuilder.add("storeId", ls_tag.get(0).getStoreId() + "");
        formEncodingBuilder.add("clientUid", socketWay + "_" + DeviceName);

        body = formEncodingBuilder.build();

        //////////////
        MyOkHttpUtils.getInstance().post(confirmMoreMsgUrl, body, token, basicUrl, new CallBackListener() {
            @Override
            public void error(String error) {
                Logger.e(TAG, "confirm_msg_error : " + error);
            }

            @Override
            public void fail(String message) {
                Logger.e(TAG, "confirm_msg_fail : " + message);
            }

            @Override
            public void success(String content) {
                Logger.e(TAG, "confirm_msg_success : " + content);

            }
        });
    }
}
