package com.ccwangluo.ccim.manage;

import android.content.Context;
import android.media.MediaMetadataRetriever;

import androidx.room.Room;

import com.ccwangluo.ccim.CCIMClient;
import com.ccwangluo.ccim.database.AppDatabase;
import com.ccwangluo.ccim.listener.OnPlayAudioCallback;
import com.ccwangluo.ccim.listener.ResultEventCallback;
import com.ccwangluo.ccim.modle.MessageContent;
import com.ccwangluo.ccim.modle.PullMessageRes;
import com.ccwangluo.ccim.modle.PullOffLineMessageRes;
import com.ccwangluo.ccim.util.ErrorCode;
import com.ccwangluo.ccim.util.FileUtils;
import com.ccwangluo.ccim.util.MediaHelper;
import com.google.gson.Gson;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;

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

import ccim.protocol.Chat;
import ccim.protocol.Message;
import ccim.protocol.MessageSpec;

public class MessageManager {

    private final Logger logger = Logger.getLogger(CCIMClient.class.getName());
    private SocketManager socketManager;
    private CallBackManager<Chat.ChatResponseMessage> callBackManager;

    private ExecutorService executor;
    private long userId;
    private AppDatabase db;

    public MessageManager(SocketManager socketManager, CallBackManager<Chat.ChatResponseMessage> callBackManager, ExecutorService executor) {
        this.socketManager = socketManager;
        this.callBackManager = callBackManager;
        this.executor = executor;
    }

    public void init(Context context, Long userId) {
        this.userId = userId;
        if (db == null) {
            db = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "ccim_db").build();
        }
    }


    private List<MessageContent> messageContentList = new ArrayList<>();

    /**
     * 同步离线消息
     */
    public void getOfflineMessage(ResultEventCallback<Void> resultEventCallback) {
        executor.execute(() -> {
            long lastPullMsgId = 0;
            MessageContent lastMsgById = db.messageDao().getLastMsgById(userId);
            if (lastMsgById != null) {
                lastPullMsgId = lastMsgById.getMsgId();
            }
            messageContentList.clear();
            getOfflineMessageOnce(lastPullMsgId, new ResultEventCallback<Void>() {
                @Override
                public void onSuccess(Void unused) {
                    resultEventCallback.onSuccess(null);
                }

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

    public void getOfflineMessageOnce(Long lastPullMsgId, ResultEventCallback<Void> callback) {
        Message.PullOfflineMessageRequest offlineMessageRequest = Message.PullOfflineMessageRequest.newBuilder()
                .setLastPulledMsgId(lastPullMsgId)
                .build();
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        builder.setPullOfflineMsgReq(offlineMessageRequest);
        int id = socketManager.sendRequest(builder);
        callBackManager.addCallBack(id, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                if (chatResponseMessage.hasPullOfflineMsgRes()) {
                    logger.info(String.valueOf(chatResponseMessage));
                    Gson gson = new Gson();
                    try {
                        String print = JsonFormat.printer().print(chatResponseMessage.getPullOfflineMsgRes());
                        PullOffLineMessageRes pullOffLineMessageRes = gson.fromJson(print, PullOffLineMessageRes.class);
                        List<MessageContent> message = pullOffLineMessageRes.getOfflineMessages();
                        if (message == null) {
                            callback.onSuccess(null);
                            return;
                        }
                        for (MessageContent messageContent : message) {
                            if (!messageContent.getSenderUserId().equals(userId)) {
                                messageContent.setReciveUserId(userId);
                            }
                        }
                        messageContentList.addAll(message);
                        pushNewMessageList(message);
                        Long msgId = message.get(message.size() - 1).getMsgId();
                        if (message.size() > 0 && msgId >= lastPullMsgId) {
                            getOfflineMessageOnce(msgId, callback);
                        } else {
                            callback.onSuccess(null);
                        }
                    } catch (InvalidProtocolBufferException e) {
                        e.printStackTrace();
                    }
                }
            }

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


    /**
     * 发送消息
     */
    public void sendPrivateMessage(String content, Long reciveUserId, MessageSpec.MessageType messageType, ResultEventCallback<MessageContent> callback) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        Message.SendMessageRequest messageRequest = Message.SendMessageRequest.newBuilder()
                .setContent(content)
                .setMsgType(messageType)
                .setReceiverUserId(reciveUserId).build();
        builder.setSendMsgReq(messageRequest);
        int id = socketManager.sendRequest(builder);
        callBackManager.addCallBack(id, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                if (chatResponseMessage.hasSendMsgRes()) {
                    Message.SendMessageResponse sendMsgRes = chatResponseMessage.getSendMsgRes();
                    long msgId = sendMsgRes.getMsgId();
                    MessageContent message = new MessageContent();
                    String text = content;
                    if (messageType == MessageSpec.MessageType.MESSAGE_TYPE_TEXT) {
                        text = sendMsgRes.getContent();
                    } else if (messageType == MessageSpec.MessageType.MESSAGE_TYPE_VOICE) {
                        message.setVoiceContent(sendMsgRes.getContent());
                    }
                    message.setSenderUserId(userId);
                    message.setReciveUserId(reciveUserId);
                    message.setMsgId(msgId);
                    message.setContent(text);
                    message.setMsgType(MessageContent.MessageType.valueOf(messageType.name()));
                    message.setCreatedDate(System.currentTimeMillis());
                    message.setMsgSrcType(MessageContent.MessageSourceType.MESSAGE_SOURCE_TYPE_PRIVATE);
                    pushNewMessage(message);
                    callback.onSuccess(message);
                }
            }

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

    /**
     * 发送频道消息
     */
    public void sendChannelMessage(String content, Long channelId, MessageSpec.MessageType messageType, ResultEventCallback<MessageContent> callback) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        Message.SendChannelMessageRequest sendChannelMessageRequest = Message.SendChannelMessageRequest.newBuilder()
                .setContent(content)
                .setMsgType(messageType)
                .setChannelId(channelId)
                .build();
        builder.setSendChannelMsgReq(sendChannelMessageRequest);
        int id = socketManager.sendRequest(builder);
        callBackManager.addCallBack(id, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                if (chatResponseMessage.hasSendChannelMsgRes()) {
                    Message.SendChannelMessageResponse sendMsgRes = chatResponseMessage.getSendChannelMsgRes();
                    long msgId = sendMsgRes.getMsgId();
                    MessageContent message = new MessageContent();
                    String text = content;
                    if (messageType == MessageSpec.MessageType.MESSAGE_TYPE_TEXT) {
                        text = sendMsgRes.getContent();
                    } else if (messageType == MessageSpec.MessageType.MESSAGE_TYPE_VOICE) {
                        message.setVoiceContent(sendMsgRes.getContent());
                    }
                    message.setSenderUserId(userId);
                    message.setChannelId(channelId);
                    message.setMsgId(msgId);
                    message.setContent(text);
                    message.setMsgType(MessageContent.MessageType.valueOf(messageType.name()));
                    message.setCreatedDate(System.currentTimeMillis());
                    message.setMsgSrcType(MessageContent.MessageSourceType.MESSAGE_SOURCE_TYPE_PRIVATE);
                    pushNewMessage(message);
                    callback.onSuccess(message);
                }
            }

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


    public void uploadFileMessage(String path, ResultEventCallback<String> callback) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        ccim.protocol.User.PreUploadSignRequest preUploadSignRequest = ccim.protocol.User.PreUploadSignRequest.newBuilder().build();
        builder.setPreUploadSignReq(preUploadSignRequest);
        int id = socketManager.sendRequest(builder);
        callBackManager.addCallBack(id, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                if (chatResponseMessage.hasPreUploadSignRes()) {
                    ccim.protocol.User.PreUploadSignResponse preUploadSignRes = chatResponseMessage.getPreUploadSignRes();
                    String filename = preUploadSignRes.getFilename();
                    String filePath = MediaHelper.getInstance().getFilePath(filename, path);
                    String preSignedUrl = preUploadSignRes.getPreSignedUrl();
                    FileUtils.uploadFile(filePath, preSignedUrl, new ResultEventCallback<Void>() {
                        @Override
                        public void onSuccess(Void unused) {
                            callback.onSuccess(filename);
                        }

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

                }
            }

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


    public void playAudio(Long msgId, String downloadPrefix, String cachePath, OnPlayAudioCallback resultCallback, Float volume) {
        executor.execute(() -> {
            MessageContent messageContent = db.messageDao().getMsgById(msgId);
            if (messageContent == null) {
                resultCallback.onFailed(ErrorCode.MSG_NOT_EXISTS);
                return;
            }
            if (messageContent.getMsgType() == MessageContent.MessageType.MESSAGE_TYPE_TEXT) {
                resultCallback.onFailed(ErrorCode.MSG_TYPE_ERROR);
                return;
            }
            File file = new File(cachePath, messageContent.getContent());
            if (!file.exists()) {
                String url = downloadPrefix + messageContent.getContent();
                FileUtils.downLoadFile(url, file, new ResultEventCallback<File>() {
                    @Override
                    public void onSuccess(File file) {
                        MediaHelper.getInstance().play(file.getAbsolutePath(), resultCallback, volume);
                    }

                    @Override
                    public void onFailed(int errorCode) {
                        resultCallback.onFailed(errorCode);
                    }
                });
            } else {
                MediaHelper.getInstance().play(file.getAbsolutePath(), resultCallback, volume);
            }

        });


    }

    /**
     * 获取私聊消息记录
     *
     * @param contactId
     * @param callback
     */
    public void getPrivateMessage(Long contactId, ResultEventCallback<List<MessageContent>> callback) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        Message.PullPrivateMessageRequest build = Message.PullPrivateMessageRequest.newBuilder().setUserId(contactId).build();
        builder.setPullPrivateMsgReq(build);
        int id = socketManager.sendRequest(builder);
        callBackManager.addCallBack(id, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                if (chatResponseMessage.hasPullPrivateMsgRes()) {
                    Gson gson = new Gson();
                    Message.PullPrivateMessageResponse pullPrivateMsgRes = chatResponseMessage.getPullPrivateMsgRes();
                    try {
                        String print = JsonFormat.printer().print(pullPrivateMsgRes);
                        PullMessageRes pullPrivateMsgRes1 = gson.fromJson(print, PullMessageRes.class);
                        List<MessageContent> message = pullPrivateMsgRes1.getMessage();
                        for (MessageContent messageContent : message) {
                            if (messageContent.getSenderUserId().equals(userId)) {
                                messageContent.setReciveUserId(contactId);
                            } else {
                                messageContent.setReciveUserId(userId);
                            }
                        }
                        callback.onSuccess(message);
                        pushNewMessageList(message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

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


    /**
     * @param channelId
     * @param callback
     */
    public void getChannelMessage(Long channelId, ResultEventCallback<List<MessageContent>> callback) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        Message.PullChannelMessageRequest build = Message.PullChannelMessageRequest.newBuilder().setChannelId(channelId).build();
        builder.setPullChannelMsgReq(build);
        int id = socketManager.sendRequest(builder);
        callBackManager.addCallBack(id, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                if (chatResponseMessage.hasPullChannelMsgRes()) {
                    Gson gson = new Gson();
                    Message.PullChannelMessageResponse pullChannelMsgRes = chatResponseMessage.getPullChannelMsgRes();
                    try {
                        String print = JsonFormat.printer().print(pullChannelMsgRes);
                        PullMessageRes pullPrivateMsgRes1 = gson.fromJson(print, PullMessageRes.class);
                        List<MessageContent> message = pullPrivateMsgRes1.getMessage();
                        for (MessageContent messageContent : message) {
                            messageContent.setChannelId(channelId);
                        }
                        callback.onSuccess(message);
                        pushNewMessageList(message);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }

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

    public void pushNewMessage(MessageContent messageContent) {
        executor.execute(() -> db.messageDao().insert(messageContent));
    }

    private void pushNewMessageList(List<MessageContent> messageContents) {
        executor.execute(() -> db.messageDao().insertList(messageContents));
    }

    public void getAudioDuration(Long msgId, String downloadPrefix, String cachePath, ResultEventCallback<Long> resultCallback) {
        executor.execute(() -> {
            MessageContent messageContent = db.messageDao().getMsgById(msgId);
            if (messageContent == null) {
                resultCallback.onFailed(ErrorCode.MSG_NOT_EXISTS);
                return;
            }
            if (messageContent.getMsgType() == MessageContent.MessageType.MESSAGE_TYPE_TEXT) {
                resultCallback.onFailed(ErrorCode.MSG_TYPE_ERROR);
                return;
            }
            File file = new File(cachePath, messageContent.getContent());
            if (!file.exists()) {
                String url = downloadPrefix + messageContent.getContent();
                FileUtils.downLoadFile(url, file, new ResultEventCallback<File>() {
                    @Override
                    public void onSuccess(File file) {
                        resultCallback.onSuccess(getDuration(file.getAbsolutePath()));
                    }

                    @Override
                    public void onFailed(int errorCode) {
                        resultCallback.onFailed(errorCode);
                    }
                });
            } else {
                resultCallback.onSuccess(getDuration(file.getAbsolutePath()));
            }
        });


    }

    public long getDuration(String path) {
        android.media.MediaMetadataRetriever mmr = new android.media.MediaMetadataRetriever();
        long duration = 0;
        try {
            if (path != null) {
                mmr.setDataSource(path);
            }
            String time = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
            duration = Long.parseLong(time);
        } catch (Exception ex) {
        } finally {
            mmr.release();
        }
        return duration;
    }

    public void updateReadOffset(Long userId, Long msgid, ResultEventCallback<Void> callback) {
        Chat.ChatRequestMessage.Builder builder = Chat.ChatRequestMessage.newBuilder();
        Message.UpdateReadOffsetRequest build = Message.UpdateReadOffsetRequest.newBuilder().setContactUserId(userId).setMsgId(msgid).build();
        builder.setUpdateReadOffsetReq(build);
        int id = socketManager.sendRequest(builder);
        callBackManager.addCallBack(id, new ResultEventCallback<Chat.ChatResponseMessage>() {
            @Override
            public void onSuccess(Chat.ChatResponseMessage chatResponseMessage) {
                callback.onSuccess(null);
            }

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

    public Long getOfflineMessageCountByUserId(Long userId, Long msgId) {
        return db.messageDao().selectOfflineCount(userId, this.userId, msgId);
    }
}
