package com.mogo.cloud.socket;


import static com.mogo.cloud.socket.SocketServicesConstants.getTag;

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

import androidx.annotation.NonNull;

import com.elegant.network.utils.GsonUtil;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.MessageLiteOrBuilder;
import com.mogo.cloud.passport.MoGoAiCloudClient;
import com.mogo.cloud.socket.entity.MsgBody;
import com.mogo.cloud.socket.internal.InternalSocketManager;
import com.mogo.cloud.socket.third.ThirdSocketManager;
import com.zhidao.ptech.connsvr.protocol.MogoConnsvr;
import com.zhidao.socket.ConnectionLifecycleListener;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * Socket 长链核心服务
 */
public class SocketManager implements IMogoCloudSocketManager {

    private static final String TAG = "SocketManager";
    private static volatile SocketManager mInstance;

    private SocketManager() {
    }

    public static SocketManager getInstance() {
        if (mInstance == null) {
            synchronized (SocketManager.class) {
                if (mInstance == null) {
                    mInstance = new SocketManager();
                }
            }
        }
        return mInstance;
    }

    /**
     * 管理消息分发
     * <p>
     * key - msgType
     */
    private final Map<Integer, List<IMogoCloudSocketOnMessageListener>> mListeners = new ConcurrentHashMap<>();

    /**
     * 管理消息回执
     * <p>
     * key - msgId
     */
    private final Map<Long, IMogoCloudSocketMsgAckListener> mAckListeners = new ConcurrentHashMap<>();

    private final Map<String, IMogoCloudSocketErrorListener> mErrorListeners = new ConcurrentHashMap<>();

    public static final int MAX_CAP = 64; //保证充足的容量应对非常延时的推送
    private final ArrayList<Long> mReceivedMsgId = new ArrayList<>(MAX_CAP);
    private int mCurrentIndex = 0;

    @Override
    public void init(Context context, String sn, double lat, double lon) {
        if (check()) {
            ThirdSocketManager.getInstance().init(context, sn, lat, lon);
        } else {
            InternalSocketManager.getInstance().init(context);
        }
    }

    @Override
    public void reConnect() {
        if (check()) {
            ThirdSocketManager.getInstance().reConnect();
        }
    }

    @Override
    public void registerOnMessageListener(int msgType, IMogoCloudSocketOnMessageListener listener) {
        if (listener == null) {
            return;
        }
        if (mListeners.containsKey(msgType)) {
            if (SocketBuildConfig.isPrintLog) {
                Log.w(getTag(), "msgType " + msgType + " is exist.");
            }
            return;
        }
        if (!mListeners.containsKey(msgType)) {
            mListeners.put(msgType, new ArrayList<>());
        }
        mListeners.get(msgType).add(listener);
    }

    @Override
    public void unregisterOnMessageListener(int msgType, IMogoCloudSocketOnMessageListener listener) {
        if (listener == null) {
            return;
        }
        if (!mListeners.containsKey(msgType)) {
            return;
        }
        List<IMogoCloudSocketOnMessageListener> listeners = mListeners.get(msgType);
        if (listeners != null) {
            listeners.remove(listener);
        }
    }

    @Override
    public void registerSocketConnCallback(ConnectionLifecycleListener listener) {
        ThirdSocketManager.getInstance().registerSocketConnCallback(listener);
    }

    @Override
    public void unregisterSocketConnCallback(ConnectionLifecycleListener listener) {
        ThirdSocketManager.getInstance().unregisterSocketConnCallback(listener);
    }

    @Override
    public void registerSocketErrorCallback(String tag, IMogoCloudSocketErrorListener listener) {
        if (listener == null || tag == null || TextUtils.isEmpty(tag)) {
            return;
        }
        if (mErrorListeners.containsKey(tag)) {
            if (SocketBuildConfig.isPrintLog) {
                Log.w(getTag(), "Type " + tag + " is exist.");
            }
            return;
        }
        mErrorListeners.put(tag, listener);
    }

    @Override
    public void unregisterSocketErrorCallback(String tag) {
        if (tag == null || TextUtils.isEmpty(tag)) {
            return;
        }
        if (!mErrorListeners.containsKey(tag)) {
            return;
        }
        mErrorListeners.remove(tag);
    }

    @Override
    public void sendMsg(String appId, int headerType, MsgBody body, IMogoCloudSocketMsgAckListener listener) {
        if (SocketBuildConfig.isPrintLog) {
            Log.d(getTag(), "sendMsg  msgId : " + body.getMsgId());
        }

        final byte[] pb = convertToPBBytes(body.getMsgType(), body.getContent());
        if (check()) {
            ThirdSocketManager.getInstance().sendMsg(appId, pb, headerType, true, body.getMsgId());
        } else {
            InternalSocketManager.getInstance().sendMsg(pb, headerType, true, body.getMsgId());
        }
        mAckListeners.put(body.getMsgId(), listener);
    }

    public void update(@NonNull byte[] message, long msgId) {
        try {
            MogoConnsvr.Payload payload = MogoConnsvr.Payload.parseFrom(message);
            int msgType = payload.getMsgType();
            if (SocketBuildConfig.isPrintLog) {
                Log.d(getTag(), "received msg type = " + msgType);
            }

            List<IMogoCloudSocketOnMessageListener> listeners = mListeners.get(msgType);
            if (listeners != null && !listeners.isEmpty()) {
                Iterator<IMogoCloudSocketOnMessageListener> iterator = listeners.iterator();
                if (msgId != 0) { //兼容老版本
                    if (mReceivedMsgId.contains(msgId)) { // 避免消息重发
                        return;
                    }
                    cacheLastReceivedMsgId(msgId);
                }
                Object obj = null;
                while (iterator.hasNext()) {
                    IMogoCloudSocketOnMessageListener listener = iterator.next();
                    ByteString p = payload.getPayload();
                    if (p != null) {
                        Class clz = listener.target(msgType);
                        if (MessageLiteOrBuilder.class.isAssignableFrom(clz)) {
                            try {
                                Constructor c = clz.getDeclaredConstructor();
                                if (c != null) {
                                    c.setAccessible(true);
                                    MessageLiteOrBuilder o = (MessageLiteOrBuilder) c.newInstance();
                                    obj = o.getDefaultInstanceForType().getParserForType().parseFrom(p);
                                }
                            } catch (Throwable t) {
                                Log.e(TAG, Log.getStackTraceString(t));
                            }
                        } else {
                            obj = GsonUtil.objectFromJson(p.toStringUtf8(), listener.target(msgType));
                        }
                    }
                    if (listener != null && obj != null) {
                        if (SocketBuildConfig.isPrintLog) {
                            Log.d(getTag(), "received msgId = " + msgId + ", msgType = " + msgType + ", content = " + obj);
                        }
                        listener.onMsgReceived(msgType, obj);
                    }
                }
            }
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
    }

    private void cacheLastReceivedMsgId(long msgId) {
        if (msgId == 0) {
            return;
        }
        synchronized (this) {
            mReceivedMsgId.add(mCurrentIndex % MAX_CAP, msgId);
            mCurrentIndex++;
        }
    }

    public void onAck(@NonNull byte[] headerBytes) {
        try {
            MogoConnsvr.Header header = MogoConnsvr.Header.parseFrom(headerBytes);
            int msgType = header.getMsgType();
            String appId = header.getAppId();
            int productLine = header.getProductLine();
            long msgId = header.getMsgId();
            IMogoCloudSocketMsgAckListener listener = mAckListeners.remove(msgId);
            if (listener != null) {
                listener.onAck(msgId);
            }
            if (SocketBuildConfig.isPrintLog) {
                Log.d(getTag(), "send message success: msgId = " + msgId + " msgType = " + msgType + ", appId = " + appId + ", productLine = " + productLine);
            }
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
    }

    public void onError(int code, String msg) {
        for (Map.Entry<String, IMogoCloudSocketErrorListener> entry : mErrorListeners.entrySet()) {
            IMogoCloudSocketErrorListener listener = entry.getValue();
            if (listener != null) {
                listener.onError(code, msg);
            }
        }
    }

    /**
     * 将byte数组转换成PB数据
     *
     * @param msgType      消息类型
     * @param payloadBytes 原始数据
     * @return PB数据
     */
    private byte[] convertToPBBytes(int msgType, byte[] payloadBytes) {
        MogoConnsvr.Payload payloadData = MogoConnsvr.Payload.newBuilder()
                .setMsgType(msgType)
                .setPayload(ByteString.copyFrom(payloadBytes)).build();
        return payloadData.toByteArray();
    }

    public synchronized void release() {
        if (check()) {
            ThirdSocketManager.getInstance().release();
        } else {
            InternalSocketManager.getInstance().release();
        }
    }

    private boolean check(){
        return (MoGoAiCloudClient.getInstance().getAiCloudClientConfig()!= null
                && MoGoAiCloudClient.getInstance().getAiCloudClientConfig().isThirdLogin());
    }

}
