package com.ccwangluo.ccim;

import android.Manifest;
import android.content.Context;
import android.media.MediaPlayer;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.ccwangluo.ccim.listener.OnClientConnectListener;
import com.ccwangluo.ccim.listener.OnConnectListener;
import com.ccwangluo.ccim.listener.OnPlayAudioCallback;
import com.ccwangluo.ccim.listener.OnPushMsgListener;
import com.ccwangluo.ccim.listener.RecordAutoSendCallback;
import com.ccwangluo.ccim.listener.ResultEventCallback;
import com.ccwangluo.ccim.manage.CallBackManager;
import com.ccwangluo.ccim.manage.ChannelManager;
import com.ccwangluo.ccim.manage.ContactManager;
import com.ccwangluo.ccim.manage.MessageManager;
import com.ccwangluo.ccim.manage.SocketManager;
import com.ccwangluo.ccim.modle.Contact;
import com.ccwangluo.ccim.modle.MessageContent;
import com.ccwangluo.ccim.modle.NetStatus;
import com.ccwangluo.ccim.modle.PullContactsRes;
import com.ccwangluo.ccim.modle.PushPrivateMsgRes;
import com.ccwangluo.ccim.modle.SearchRes;
import com.ccwangluo.ccim.modle.User;
import com.ccwangluo.ccim.util.ErrorCode;
import com.ccwangluo.ccim.util.MediaHelper;
import com.ccwangluo.ccim.util.PermissionUtils;
import com.google.gson.Gson;
import com.google.protobuf.util.JsonFormat;
import com.karumi.dexter.Dexter;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionDeniedResponse;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.listener.PermissionRequest;
import com.karumi.dexter.listener.single.PermissionListener;

import java.io.File;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

import ccim.protocol.Chat;
import ccim.protocol.MessageSpec;
import ccim.protocol.Push;

public class CCIMClient {

    private final Logger logger = Logger.getLogger(CCIMClient.class.getName());
    private static SocketManager socketManager;
    private static CallBackManager<Chat.ChatResponseMessage> callBackManager;
    private static ContactManager contactManager;
    private static ChannelManager channelManager;
    private static MessageManager messageManager;
    private static User user;
    private static boolean isConnected = false; //链接状态
    private Long lastSendTimemillis = 0L; //上次发送消息时间,用于时间间隔
    private int maxSendMsgInterval = 0;
    private int maxAudioDuration = 0;
    private final int minAudioDuration = 500;
    public String downloadPrefix;
    public String cachePath;
    public Handler handler = new Handler(Looper.getMainLooper());

    private ExecutorService executor = Executors.newSingleThreadExecutor();

    private CCIMClient() {
        callBackManager = new CallBackManager<>();
        socketManager = new SocketManager(callBackManager, executor);
        contactManager = new ContactManager(socketManager, callBackManager);
        channelManager = new ChannelManager(socketManager, callBackManager);
        messageManager = new MessageManager(socketManager, callBackManager, executor);
    }

    private static CCIMClient mInstance;

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

    public void initServer(String ip, int port) {
        socketManager.initServer(ip, port);
    }

    public void initSDK(Context context, String appKey, String userName, String extData, Long userId, Long ts, String sign, String server, OnConnectListener resultCallback) {
        this.user = new User(userId,userName,extData);
        messageManager.init(context, userId);
        socketManager.initChat(context, appKey, userName, extData, userId, ts, sign, server, new OnClientConnectListener() {
            @Override
            public void onSocketConnection(Chat.ConnectResponse.ClientConfig clientConfig) {
                maxSendMsgInterval = clientConfig.getSendMsgInterval();
                maxAudioDuration = clientConfig.getMaxAudioDuration();
                downloadPrefix = clientConfig.getDownloadPrefix();
                getContactList(true, new ResultEventCallback<PullContactsRes>() {
                    @Override
                    public void onSuccess(PullContactsRes pullContactsRes) {
                        if (!isConnected) {
                            resultCallback.onSocketConnection();
                            isConnected = true;
                        }
                        channelManager.reConnection();
                    }

                    @Override
                    public void onFailed(int errorCode) {
                        resultCallback.onSocketDisconnection(errorCode);
                    }
                });
            }

            @Override
            public void onSocketReConnection() {
//                isConnected = false;
            }

            @Override
            public void onSocketDisconnection(int code) {
                isConnected = false;
                logger.info("disconnection  " + code);
                resultCallback.onSocketDisconnection(code);
            }

        });
        initCachePath(context);
    }

    /**
     * 初始化缓存目录
     *
     * @param context
     */
    private void initCachePath(Context context) {
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            cachePath = context.getExternalFilesDir("cache").getAbsolutePath();
        } else {
            cachePath = context.getCacheDir().getAbsolutePath();
        }
        File fileDir = new File(cachePath);
        boolean b = !fileDir.exists();
        if (b) {
            fileDir.mkdirs();
        }
    }


    public void registerMessageListener(OnPushMsgListener pushMsgListener) {
        callBackManager.addCallBack(0, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                Gson gson = new Gson();
                if (chatResponseMessage.hasPushPrivateMsgRes()) {
                    MessageSpec.ChatMessage pushPrivateMsgRes = chatResponseMessage.getPushPrivateMsgRes().getMessage();
                    pushPrivateMessageAck(pushPrivateMsgRes.getSenderUserId(), pushPrivateMsgRes.getMsgId());
                    try {
                        String print = JsonFormat.printer().print(chatResponseMessage.getPushPrivateMsgRes());
                        PushPrivateMsgRes pushPrivateMsgRes1 = gson.fromJson(print, PushPrivateMsgRes.class);

                        if (pushPrivateMsgRes1.getStrangerDialog() != null) {
                            contactManager.putStrangerDialog(pushPrivateMsgRes1.getStrangerDialog());
                        }
                        if (pushPrivateMsgRes1.getMessage() != null) {
                            MessageContent message = pushPrivateMsgRes1.getMessage();
                            Long senderUserId = message.getSenderUserId();
                            contactManager.putContactDialog(senderUserId, null);
                            pushPrivateMsgRes1.getMessage().setReciveUserId(user.getUserId());
                            pushMsgListener.pushPrivateMsg(pushPrivateMsgRes1.getMessage());
                            messageManager.pushNewMessage(pushPrivateMsgRes1.getMessage());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                if (chatResponseMessage.hasPushChannelMsgRes()) {
                    Push.PushChannelMessageResponse channelMsgRes = chatResponseMessage.getPushChannelMsgRes();
                    pushChannelMessage(channelMsgRes.getChannelId(), channelMsgRes.getMessage().getMsgId());
                    try {
                        String print = JsonFormat.printer().print(channelMsgRes.getMessage());
                        MessageContent messageContent = gson.fromJson(print, MessageContent.class);
                        messageContent.setChannelId(channelMsgRes.getChannelId());
                        pushMsgListener.pushChannelMsg(messageContent);
                        messageManager.pushNewMessage(messageContent);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                if (chatResponseMessage.hasPushNewContactRes()) {
                    Push.PushNewContactResponse pushNewContactRes = chatResponseMessage.getPushNewContactRes();
                    try {
                        String print = JsonFormat.printer().print(pushNewContactRes.getContact());
                        Contact contact = gson.fromJson(print, Contact.class);
                        contactManager.pushNewContact(contact);
                        pushMsgListener.pushNewContact(contact);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

                if (chatResponseMessage.hasPushContactDeleteMeRes()) {
                    Push.PushContactDeleteMeResponse pushNewContactRes = chatResponseMessage.getPushContactDeleteMeRes();
                    long contactUserId = pushNewContactRes.getContactUserId();
                    contactManager.pushDeleteContact(contactUserId);
                    pushMsgListener.pushDeleteContact(contactUserId);
                }

                if (chatResponseMessage.hasPushContactNetStatusRes()) {
                    Push.PushContactNetStatusResponse pushNewContactRes = chatResponseMessage.getPushContactNetStatusRes();
                    long contactUserId = pushNewContactRes.getContactUserId();
                    NetStatus netStatus = NetStatus.valueOf(pushNewContactRes.getNetStatus().name());
                    contactManager.pushContactNetStatus(contactUserId, netStatus);
                    pushMsgListener.pushContactNetStatus(contactUserId, netStatus);
                }

                if (chatResponseMessage.hasPushContactNewReqRes()) {
                    Push.PushContactNewReqResponse pushContactNewReqRes = chatResponseMessage.getPushContactNewReqRes();
                    String username = pushContactNewReqRes.getContactNewReq().getUsername();
                    Long id = pushContactNewReqRes.getContactNewReq().getUserId();
                    String extData = pushContactNewReqRes.getContactNewReq().getExtData();
                    pushMsgListener.pushContactNewReq(new User(id, username,extData));
                }

                if (chatResponseMessage.hasPushUserDeletedRes()) {
                    Push.PushUserDeletedResponse pushUserDeletedRes = chatResponseMessage.getPushUserDeletedRes();
                    long userId = pushUserDeletedRes.getUserId();
                    contactManager.pushDeleteContact(userId);
                    pushMsgListener.pushDeleteContact(userId);
                }

            }

            @Override
            public void onFailed(int errorCode) {

            }
        });
    }


    /**
     * 获取联系人列表
     */
    public void getContactList(boolean isClearCache, ResultEventCallback<PullContactsRes> resultEventCallback) {
        contactManager.getContactList(resultEventCallback, isClearCache);
    }


    /**
     * 搜索好友
     */
    public void searchUser(String userName, long userId, int page, int size, ResultEventCallback<SearchRes> resultEventCallback) {
        contactManager.searchUser(userName, userId, page, size, resultEventCallback);
    }

    /**
     * 添加好友
     */
    public void addContact(Long userId, ResultEventCallback<Void> callback) {
        contactManager.addContact(userId, callback);
    }

    /**
     * 联系人申请列表
     */
    public void getContactReqList(ResultEventCallback<List<User>> callback) {
        contactManager.getContactReqList(callback);
    }

    /**
     * 验证联系人申请
     */
    public void checkContactReq(Long reqUserId, boolean accept, ResultEventCallback<Void> callback) {
        contactManager.checkContactReq(reqUserId, accept, callback);
    }

    /**
     * 创建联系人群组
     */
    public void createContactGroup(String name, ResultEventCallback<Long> callback) {
        contactManager.createContactGroup(name, callback);
    }


    /**
     * 删除联系人群组
     */
    public void deleteContactGroup(Long groupId, ResultEventCallback<Void> callback) {
        contactManager.deleteContactGroup(groupId, callback);
    }


    /**
     * 移动联系人所在的群组
     */
    public void moveContactGroup(Long groupId, Long userId, ResultEventCallback<Void> callback) {
        contactManager.moveContactGroup(groupId, userId, callback);
    }

    /**
     * 删除联系人
     */
    public void deleteContact(Long userId, ResultEventCallback<Void> callback) {
        contactManager.deleteContact(userId, callback);
    }

    /**
     * 拉黑联系人
     */
    public void blockContact(Long userId, ResultEventCallback<Void> callback) {
        contactManager.blockContact(userId, callback);
    }

    /**
     * 举报违规用户
     */
    public void reportIllegalUser(String title, String content, Long userId, ResultEventCallback<Void> callback) {
        contactManager.reportIllegalUser(title, content, userId, callback);
    }


    /**
     * 创建频道
     */
    public void createChannel(String channelName, String tag, boolean isOpen, List<Long> userIds, ResultEventCallback<Long> callback) {
        channelManager.createChannel(channelName, tag, isOpen, userIds, callback);
    }

    /**
     * 加入频道
     */
    public void joinChannel(Long channelId, ResultEventCallback<Void> callback) {
        channelManager.joinChannel(channelId, callback);
    }

    /**
     * 移除频道成员
     */
    public void removeChannelMember(Long channelId, Long userId, ResultEventCallback<Void> callback) {
        channelManager.removeChannelMember(channelId, userId, callback);
    }

    /**
     * 频道拉黑
     */
    public void blockChannelUser(Long channelId, Long userId, ResultEventCallback<Void> callback) {
        channelManager.blockChannelUser(channelId, userId, callback);
    }

    /**
     * 退出频道
     */
    public void exitChannel(Long channelId, ResultEventCallback<Void> callback) {
        channelManager.exitChannel(channelId, callback);
    }

    /**
     * 同步离线消息
     */
    private void getOfflineMessage(Long contactId, ResultEventCallback<List<MessageContent>> resultEventCallback) {
        messageManager.getOfflineMessage(contactId, resultEventCallback);
    }

    /**
     * 校验时间间隔
     */
    private boolean checkTimeInterval() {
        if (lastSendTimemillis != 0) {
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis - lastSendTimemillis < maxSendMsgInterval) {
                return false;
            }
        }
        lastSendTimemillis = System.currentTimeMillis();
        return true;
    }

    /**
     * 发送消息
     */
    public void sendTextMessage(String content, Long receiveUserId, String receiveUserName, ResultEventCallback<MessageContent> callback) {
        if (TextUtils.isEmpty(content)) {
            callback.onFailed(ErrorCode.MSG_CONTENT_IS_EMPTY);
            return;
        }

        if (!checkTimeInterval()) {
            callback.onFailed(ErrorCode.SEND_MSG_INTERVAL);
            return;
        }
        messageManager.sendPrivateMessage(content, receiveUserId, MessageSpec.MessageType.MESSAGE_TYPE_TEXT, new ResultEventCallback<MessageContent>() {

            @Override
            public void onSuccess(MessageContent messageContent) {
                contactManager.putContactDialog(receiveUserId, receiveUserName);
                callback.onSuccess(messageContent);
            }

            @Override
            public void onFailed(int errorCode) {
                callback.onFailed(errorCode);
            }
        });
    }

    /**
     * 发送频道消息
     */
    public void sendChannelMessage(String content, Long channelId, ResultEventCallback<MessageContent> callback) {
        if (TextUtils.isEmpty(content)) {
            callback.onFailed(ErrorCode.MSG_CONTENT_IS_EMPTY);
            return;
        }

        if (!checkTimeInterval()) {
            callback.onFailed(ErrorCode.SEND_MSG_INTERVAL);
            return;
        }
        messageManager.sendChannelMessage(content, channelId, MessageSpec.MessageType.MESSAGE_TYPE_TEXT, callback);
    }

    /**
     * 获取私聊消息
     */
    public void getPrivateMessage(Long userId, ResultEventCallback<List<MessageContent>> callback) {
        messageManager.getPrivateMessage(userId, callback);
    }

    /**
     * 获取频道消息
     */
    public void getChannelMessage(Long channelId, ResultEventCallback<List<MessageContent>> callback) {
        messageManager.getChannelMessage(channelId, callback);
    }

    /**
     * 推送私人消息成功ack
     */
    private void pushPrivateMessageAck(Long contactUserId, Long msgId) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        Push.PushPrivateMessageAckRequest pushPrivateMessageAckRequest = Push.PushPrivateMessageAckRequest.newBuilder()
                .setContactUserId(contactUserId)
                .setMsgId(msgId)
                .build();
        builder.setPushPrivateMsgAckReq(pushPrivateMessageAckRequest);
        socketManager.sendRequest(builder);
    }


    /**
     * 推送渠道消息成功ack
     */
    private void pushChannelMessage(Long channelId, Long msgId) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        Push.PushChannelMessageAckRequest channelMessageAckRequest = Push.PushChannelMessageAckRequest.newBuilder()
                .setChannelId(channelId)
                .setMsgId(msgId)
                .build();
        builder.setPushChannelMsgAckReq(channelMessageAckRequest);
        socketManager.sendRequest(builder);
    }

    /**
     * 开始录音
     */
    public void startRecordAudioMessage(Context context, Long receiveUserId, String receiveUserName, ResultEventCallback<Void> resultCallback, RecordAutoSendCallback callback) {
        PermissionUtils.check(context, Manifest.permission.RECORD_AUDIO, new ResultEventCallback<Void>() {
            @Override
            public void onSuccess(Void o) {
                MediaHelper.getInstance().prepare(cachePath, maxAudioDuration, resultCallback);
                runnable = new Runnable() {
                    @Override
                    public void run() {
                        logger.info("time out ,auto send");
                        Long release = MediaHelper.getInstance().release();
                        if (release / 1000 >= maxAudioDuration) {
                            uploadAndSendMessage(receiveUserId, receiveUserName, new ResultEventCallback<MessageContent>() {
                                @Override
                                public void onSuccess(MessageContent messageContent) {
                                    callback.onSuccess(messageContent);
                                }

                                @Override
                                public void onFailed(int errorCode) {
                                    callback.onFailed(errorCode);
                                }
                            });
                        }
                        runnable = null;
                    }
                };
                handler.postDelayed(runnable,maxAudioDuration * 1000L);
            }

            @Override
            public void onFailed(int errorCode) {
                resultCallback.onFailed(errorCode);
            }
        });

    }

    /**
     * 停止录音并上传 私聊
     */
    public void stopAndSendAudioMessage(Long receiveUserId, String receiveUserName, ResultEventCallback<MessageContent> callback) {
        if (checkAudio(callback)) {
            return;
        }
        uploadAndSendMessage(receiveUserId, receiveUserName, callback);
    }

    private void uploadAndSendMessage(Long receiveUserId, String receiveUserName, ResultEventCallback<MessageContent> callback) {
        messageManager.uploadFileMessage(cachePath, new ResultEventCallback<String>() {
            @Override
            public void onSuccess(String fileName) {
                messageManager.sendPrivateMessage(fileName, receiveUserId, MessageSpec.MessageType.MESSAGE_TYPE_VOICE, new ResultEventCallback<MessageContent>() {
                    @Override
                    public void onSuccess(MessageContent messageContent) {
                        contactManager.putContactDialog(receiveUserId, receiveUserName);
                        callback.onSuccess(messageContent);
                    }

                    @Override
                    public void onFailed(int errorCode) {
                        callback.onFailed(errorCode);
                    }
                });
            }

            @Override
            public void onFailed(int errorCode) {
                callback.onFailed(errorCode);
            }
        });
    }

    private Runnable runnable;

    /**
     * 开始录音
     */
    public void startRecordChannelAudioMessage(Context context,Long channelID, ResultEventCallback<Void> resultCallback,RecordAutoSendCallback callback) {
        PermissionUtils.check(context, Manifest.permission.RECORD_AUDIO, new ResultEventCallback<Void>() {
            @Override
            public void onSuccess(Void o) {
                MediaHelper.getInstance().prepare(cachePath, maxAudioDuration, resultCallback);
                runnable = new Runnable() {
                    @Override
                    public void run() {
                        logger.info("time out ,auto send");
                        Long release = MediaHelper.getInstance().release();
                        if (release / 1000 >= maxAudioDuration) {
                            uploadAndSendChannelMessage(channelID, new ResultEventCallback<MessageContent>() {
                                @Override
                                public void onSuccess(MessageContent messageContent) {
                                    callback.onSuccess(messageContent);
                                }

                                @Override
                                public void onFailed(int errorCode) {
                                    callback.onFailed(errorCode);
                                }
                            });
                        }
                        runnable = null;
                    }
                };
                handler.postDelayed(runnable,maxAudioDuration * 1000L);
            }

            @Override
            public void onFailed(int errorCode) {
                resultCallback.onFailed(errorCode);
            }
        });

    }

    /**
     * 停止录音并上传 频道
     */
    public void stopAndSendChannelAudioMessage(Long channelID, ResultEventCallback<MessageContent> callback) {
        if (checkAudio(callback)) {
            return;
        }
        uploadAndSendChannelMessage(channelID, callback);
    }

    private void uploadAndSendChannelMessage(Long channelID, ResultEventCallback<MessageContent> callback) {
        messageManager.uploadFileMessage(cachePath, new ResultEventCallback<String>() {
            @Override
            public void onSuccess(String fileName) {
                messageManager.sendChannelMessage(fileName, channelID, MessageSpec.MessageType.MESSAGE_TYPE_VOICE, callback);
            }

            @Override
            public void onFailed(int errorCode) {
                callback.onFailed(errorCode);
            }
        });
    }

    private boolean checkAudio(ResultEventCallback<MessageContent> callback) {
        if (runnable != null){
            handler.removeCallbacks(runnable);
            runnable = null;
        }
        Long release = MediaHelper.getInstance().release();
        if (release < minAudioDuration) {
            callback.onFailed(ErrorCode.VOICE_TOO_SHORT);
            MediaHelper.getInstance().cancel();
            return true;
        }
        return false;
    }

    /**
     * 取消录音
     */
    public void cancleAudioMessage() {
        MediaHelper.getInstance().cancel();
    }

    /**
     * 播放音乐
     */
    public void startPlayAudio(Long msgId, OnPlayAudioCallback callback) {
        messageManager.playAudio(msgId, downloadPrefix, cachePath, callback);
    }

    /**
     * 停止播放音乐
     */
    public void stopPlayAudio() {
        MediaHelper.getInstance().stop();
    }

    /**
     * 获取时长
     */
    public void getAudioDuration(Long msgId, ResultEventCallback<Long> resultCallback) {
        messageManager.getAudioDuration(msgId, downloadPrefix, cachePath, resultCallback);
    }


    public void logout() {
        isConnected = false;
        socketManager.disConnect();
    }
}
