import Vue from "vue";
import { Module } from "vuex";
import { dbController } from "../database";
import {
    ChatMember,
    ServiceType,
    MessageHandled,
    RawChatItem,
    BaseChatItemBusinessData,
    MessageType,
} from "../model";
import { isAccessibleUrl } from "../service/tools";
import { unique } from "../utils";
import { getChatModelInfo } from "../utils/chat-info";
import { getUserInfo } from "../utils/user-info";
import Chat from "../xim";
import { Chat as ChatType, Message } from "../xim/models/chat";
import xim, { ChatNotifyListener } from "../xim/xim";
import { decodeJwt } from "uniplat-sdk";

import { ChatStatus, ChatStore, ChatStoreState } from "./model";

import { RootStoreState } from "@/store/model";

export const ns = ChatStore.ns;

const UniplatChatModelName = "UniplatChat";
const model = () => Chat.getSdk().model(UniplatChatModelName);
const orgId = () => Chat.getOrgId() as string;

function uniqueMessages(
    messages: NonNullable<ChatStore.STATE_CHAT_MSG_HISTORY>
) {
    const arr = [...messages];
    return unique(arr, function (item, all) {
        return all.findIndex((k) => k.id === item.id);
    });
}

function filterMessages(
    messages: NonNullable<ChatStore.STATE_CHAT_MSG_HISTORY>,
    chatid: number
) {
    return uniqueMessages(Array.from(messages)).filter(
        (k) => k.chat_id === chatid
    );
}

let removeRegisterChatEvents: (() => void)[] = [];

async function preCacheImgs(msgs?: any[]) {
    if (!msgs) {
        return Promise.resolve();
    }
    await Promise.all(
        msgs
            .filter((i) => i.id > 0)
            .map((k) => {
                return new Promise((resolve: (p: void) => void) => {
                    if (k.type === "image") {
                        const msg = JSON.parse(k.msg);
                        const url = msg.url;
                        if (!isAccessibleUrl(url)) {
                            return resolve();
                        }
                        if (
                            url &&
                            isAccessibleUrl(url) &&
                            typeof Image !== "undefined"
                        ) {
                            const preCache = new Image();
                            preCache.src = url;
                            preCache.onload = () => resolve();
                            setTimeout(resolve, 2000);
                        } else {
                            resolve();
                        }
                    } else {
                        resolve();
                    }
                });
            })
    );
}

function buildChatItem(chat: RawChatItem) {
    if ((!chat.model_name || !chat.obj_id) && chat.business_data) {
        const b = JSON.parse(chat.business_data) as BaseChatItemBusinessData;
        chat.model_name = b.model_name;
        b.obj_id && (chat.obj_id = b.obj_id);
    }

    if (!chat.detail_name && chat.business_data) {
        const b = JSON.parse(chat.business_data) as BaseChatItemBusinessData;
        b.detail_name && (chat.detail_name = b.detail_name);
    }
    return { ...chat, chat_id: chat.id } as ChatType;
}

const chatType = "group";

const allowedChatTypes = [chatType, "notify"];

export const filterActiveChats = (items: RawChatItem[]) => {
    return items.filter(
        (i) =>
            !i.is_finish &&
            !i.is_exited &&
            !i.is_remove &&
            !i.is_deleted &&
            allowedChatTypes.includes(i.type)
    );
};

export function getLastMessageId(msgs: Message[] | any) {
    if (msgs && msgs.length) {
        const last = msgs[msgs.length - 1];
        let id = last.id;
        if (id < 0) {
            id = Math.max(...msgs.map((i: any) => i.id));
        }
        return id;
    }
    return 0;
}

let loadingChatList = false;
let cachedLoadingChatListAction: ((value: ChatType[]) => void)[] = [];
function clearAction(value: ChatType[]) {
    for (const item of cachedLoadingChatListAction) {
        item(value);
    }
    cachedLoadingChatListAction = [];
    return value;
}

export default {
    namespaced: true,
    state: () => ({
        [ChatStore.STATE_CHAT_DIALOG_VISIBLE]: false,
        [ChatStore.STATE_CHAT_DIALOG_IS_SINGLE]: false,
        [ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER]: false,
        [ChatStore.STATE_CHAT_CURRENT_IS_CHAT_ERROR]: null,
        [ChatStore.STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID]: null,
        [ChatStore.STATE_CHAT_CURRENT_USER_UID]: null,
        [ChatStore.STATE_CHAT_MSG_HISTORY]: null,
        [ChatStore.STATE_CHAT_SENDING_MESSAGES]: [],
        [ChatStore.STATE_MY_CHAT_ROOM_LIST]: [],
        [ChatStore.STATE_SINGLE_CHAT]: null,
        [ChatStore.STATE_CHAT_CURRENT_CHAT_VERSION]: null,
        [ChatStore.STATE_CHAT_CURRENT_CHAT_ID]: null,
        [ChatStore.STATE_CHAT_MY_ID]: null,
        [ChatStore.STATE_CHAT_MY_UID]: null,
        [ChatStore.STATE_CHAT_SOURCE]: ServiceType.Frontend,
        [ChatStore.STATE_CURRENT_CHAT_MEMBERS]: null,
        [ChatStore.STATE_CURRENT_CHAT_TITLE]: "",
        [ChatStore.STATE_FUNC_SCROLL_TO_BOTTOM]: () => true,
        [ChatStore.STATE_FUNC_ON_NEW_MSG]: () => true,
        [ChatStore.STATE_CURRENT_CHAT_INPUTING]: [],
        [ChatStore.STATE_CURRENT_CHAT_INITING]: false,
        [ChatStore.STATE_CHAT_CURRENT_USER_TYPE]: null,
        [ChatStore.STATE_CHAT_SEND_FAIL_MESSAGE]: null,
        [ChatStore.STATE_CHAT_USERNAME]: {},
        [ChatStore.STATE_CURRENT_UNREAD_MESSAGE_COUNT]: 0,
    }),
    mutations: {
        [ChatStore.MUTATION_SHOW_CHAT](state, isSingle?: boolean) {
            state[ChatStore.STATE_CHAT_DIALOG_VISIBLE] = true;
            isSingle
                ? (state[ChatStore.STATE_CHAT_DIALOG_IS_SINGLE] = true)
                : (state[ChatStore.STATE_CHAT_DIALOG_IS_SINGLE] = false);
        },
        [ChatStore.MUTATION_HIDE_CHAT](state) {
            state[ChatStore.STATE_CHAT_DIALOG_VISIBLE] = false;
        },
        [ChatStore.MUTATION_SAVE_CHAT_LIST](
            state,
            data: ChatStore.STATE_MY_CHAT_ROOM_LIST
        ) {
            state[ChatStore.STATE_MY_CHAT_ROOM_LIST] = data;
        },
        [ChatStore.MUTATION_CLEAR_CHAT_MSG_HISTORY](state) {
            state[ChatStore.STATE_CHAT_MSG_HISTORY] = null;
        },
        [ChatStore.MUTATION_CLEAR_CURRENT_CHAT_ID](state) {
            state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID] = null;
        },
        [ChatStore.MUTATION_SAVE_CURRENT_CHAT_ID](
            state,
            id: ChatStore.STATE_CHAT_CURRENT_CHAT_ID
        ) {
            state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID] = id;
        },
        [ChatStore.MUTATION_CLEAR_CURRENT_CHAT_VERSION](state) {
            state[ChatStore.STATE_CHAT_CURRENT_CHAT_VERSION] = null;
        },
        [ChatStore.MUTATION_SAVE_CURRENT_CHAT_VERSION](
            state,
            v: ChatStore.STATE_CHAT_CURRENT_CHAT_VERSION
        ) {
            state[ChatStore.STATE_CHAT_CURRENT_CHAT_VERSION] = v;
        },
        [ChatStore.MUTATION_CLEAR_CURRENT_CHAT_UNIPLAT_ID](state) {
            state[ChatStore.STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID] = null;
        },
        [ChatStore.MUTATION_SAVE_CURRENT_CHAT_UNIPLAT_ID](
            state,
            id: ChatStore.STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID
        ) {
            state[ChatStore.STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID] = id;
        },
        [ChatStore.MUTATION_PUSH_CHAT_MSG_HISTORY](
            state,
            data: ChatStore.STATE_CHAT_MSG_HISTORY
        ) {
            const old = state[ChatStore.STATE_CHAT_MSG_HISTORY] || [];
            const chatid = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            if (chatid) {
                // 移除撤回的消息
                const newItems = data || [];
                const withdraw = newItems
                    .filter((i) => i.type === MessageType.Withdraw)
                    .map((i) => +i.msg);
                const filterout = newItems.filter(
                    (i) => !withdraw.includes(i.id)
                );
                state[ChatStore.STATE_CHAT_MSG_HISTORY] = Object.freeze(
                    filterMessages([...old, ...filterout], chatid)
                );
            }
        },
        [ChatStore.MUTATION_SAVE_MYSELF_ID](state) {
            if (!state[ChatStore.STATE_CHAT_MY_ID]) {
                Chat.getToken().then((token) => {
                    if (token) {
                        const eid =
                            decodeJwt<{ user_id: string; sub: string }>(token);
                        state[ChatStore.STATE_CHAT_MY_ID] = String(eid.user_id);
                        state[ChatStore.STATE_CHAT_MY_UID] = eid.sub;
                    }
                });
            }
        },
        [ChatStore.MUTATION_CLEAR_MYSELF_ID](state) {
            state[ChatStore.STATE_CHAT_MY_ID] = null;
            state[ChatStore.STATE_CHAT_MY_UID] = null;
        },
        [ChatStore.MUTATION_SAVE_SEND_FAIL_MESSAGE](state, param: string) {
            state[ChatStore.STATE_CHAT_SEND_FAIL_MESSAGE] = param;
        },
        [ChatStore.MUTATION_SET_CHAT_SOURCE](
            state,
            data: ChatStore.STATE_CHAT_SOURCE
        ) {
            state[ChatStore.STATE_CHAT_SOURCE] = data;
        },
        [ChatStore.MUTATION_UNSHIFT_CHAT_MSG_HISTORY](
            state,
            data: ChatStore.STATE_CHAT_MSG_HISTORY
        ) {
            const old = state[ChatStore.STATE_CHAT_MSG_HISTORY] || [];
            const chatid = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            if (chatid == null) return;
            state[ChatStore.STATE_CHAT_MSG_HISTORY] = Object.freeze(
                filterMessages([...(data || []), ...old], chatid)
            );
        },
        [ChatStore.MUTATION_SAVE_CURRENT_CHAT_MEMBERS](
            state,
            data: ChatStore.STATE_CURRENT_CHAT_MEMBERS
        ) {
            state[ChatStore.STATE_CURRENT_CHAT_MEMBERS] = Object.freeze(data);
        },
        [ChatStore.MUTATION_CLEAR_CURRENT_CHAT_MEMBERS](state) {
            state[ChatStore.STATE_CURRENT_CHAT_MEMBERS] = null;
        },
        [ChatStore.MUTATION_SAVE_CHAT_TITLE](
            state,
            data: ChatStore.STATE_CURRENT_CHAT_TITLE
        ) {
            state[ChatStore.STATE_CURRENT_CHAT_TITLE] = data;
        },
        [ChatStore.MUTATION_CLEAR_CHAT_TITLE](state) {
            state[ChatStore.STATE_CURRENT_CHAT_TITLE] = "";
        },
        [ChatStore.MUTATION_SAVE_SINGLE_CHAT](state, v: ChatType) {
            state[ChatStore.STATE_SINGLE_CHAT] = v;
        },
        [ChatStore.MUTATION_CLEAR_SINGLE_CHAT](state) {
            state[ChatStore.STATE_SINGLE_CHAT] = null;
        },
        [ChatStore.MUTATION_SCROLL_TO_BOTTOM](state) {
            state[ChatStore.STATE_FUNC_SCROLL_TO_BOTTOM]();
        },
        [ChatStore.MUTATION_SAVE_FUNC_SCROLL_TO_BOTTOM](
            state,
            data: ChatStore.STATE_FUNC_SCROLL_TO_BOTTOM
        ) {
            state[ChatStore.STATE_FUNC_SCROLL_TO_BOTTOM] = data;
        },
        [ChatStore.MUTATION_CLEAR_FUNC_SCROLL_TO_BOTTOM](state) {
            state[ChatStore.STATE_FUNC_SCROLL_TO_BOTTOM] = () => true;
        },
        [ChatStore.MUTATION_SAVE_FUNC_ON_NEW_MSG](
            state,
            data: ChatStore.STATE_FUNC_ON_NEW_MSG
        ) {
            state[ChatStore.STATE_FUNC_ON_NEW_MSG] = data;
        },
        [ChatStore.MUTATION_CLEAR_FUNC_ON_NEW_MSG](state) {
            state[ChatStore.STATE_FUNC_ON_NEW_MSG] = () => true;
        },
        [ChatStore.MUTATION_APPEND_SENDING_MESSAGE]: (
            state,
            payload: ChatStore.STATE_CHAT_SENDING_MESSAGE
        ) => {
            const current = state[
                ChatStore.STATE_CHAT_SENDING_MESSAGES
            ] as ChatStore.STATE_CHAT_SENDING_MESSAGE[];
            if (current) {
                current.push(payload);
                const chat = state[
                    ChatStore.STATE_CHAT_CURRENT_CHAT_ID
                ] as number;
                dbController.appendMessages(chat, [payload]);
            }
            preCacheImgs([payload]).then(() => {
                setTimeout(
                    () => state[ChatStore.STATE_FUNC_SCROLL_TO_BOTTOM](),
                    100
                );
            });
        },
        [ChatStore.MUTATION_REMOVE_SENDING_MESSAGE]: (
            state,
            payload: number
        ) => {
            const current = state[
                ChatStore.STATE_CHAT_SENDING_MESSAGES
            ] as ChatStore.STATE_CHAT_SENDING_MESSAGE[];
            if (current) {
                const target = current.find((i) => i.id === payload);
                if (target) {
                    const index = current.indexOf(target);
                    current.splice(index, 1);
                }
            }
        },
        [ChatStore.MUTATION_FAILED_SENDING_MESSAGE]: (
            state,
            payload: number
        ) => {
            const current = state[
                ChatStore.STATE_CHAT_SENDING_MESSAGES
            ] as ChatStore.STATE_CHAT_SENDING_MESSAGE[];
            if (current) {
                const target = current.find((i) => i.id === payload);
                if (target) {
                    target.status = -1;
                }

                state[ChatStore.STATE_CHAT_SENDING_MESSAGES] = [...current];
            }
        },
        [ChatStore.MUTATION_SAVE_CURRENT_CHAT_INPUTING]: (function () {
            const setTimeoutId: { [key: string]: number } = {};
            return (
                state: ChatStoreState,
                payload: Parameters<ChatStore.MUTATION_SAVE_CURRENT_CHAT_INPUTING>[0]
            ) => {
                const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
                if (chatId == null) return;
                if (payload.chat_id !== chatId) return;
                const arr = state[ChatStore.STATE_CURRENT_CHAT_INPUTING];
                const eid = payload.eid;
                if (eid === state[ChatStore.STATE_CHAT_MY_ID]) return;
                if (arr.includes(eid)) {
                    clearTimeout(setTimeoutId[eid]);
                } else {
                    arr.push(eid);
                }
                setTimeoutId[eid] = setTimeout(
                    () => arr.splice(arr.indexOf(eid), 1),
                    4000
                );
            };
        })(),
        [ChatStore.MUTATION_CLEAR_CURRENT_CHAT_INPUTING]: (state) => {
            state[ChatStore.STATE_CURRENT_CHAT_INPUTING] = [];
        },
        [ChatStore.MUTATION_INITING_CHAT]: (state) => {
            state[ChatStore.STATE_CURRENT_CHAT_INITING] = true;
        },
        [ChatStore.MUTATION_INITING_CHAT_DONE]: (state) => {
            state[ChatStore.STATE_CURRENT_CHAT_INITING] = false;
        },
        [ChatStore.MUTATION_CHAT_UPDATE_IS_MEMBER]: (state, v: boolean) => {
            state[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER] = v;
        },
        [ChatStore.MUTATION_SET_CURRENT_USER_UID]: (state, v: number) => {
            state[ChatStore.STATE_CHAT_CURRENT_USER_UID] = v;
        },
        [ChatStore.MUTATION_CHAT_UPDATE_USER_TYPE]: (state, v: string) => {
            state[ChatStore.STATE_CHAT_CURRENT_USER_TYPE] = v;
        },
        [ChatStore.MUTATION_SAVE_USERNAME]: (
            state,
            param: { id: string; name: string }
        ) => {
            Vue.set(state[ChatStore.STATE_CHAT_USERNAME], param.id, param.name);
        },
        [ChatStore.MUTATION_WITHDRAW]: (state, id: number) => {
            const old = state[ChatStore.STATE_CHAT_MSG_HISTORY] || [];
            const chatid = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            if (chatid == null) return;
            state[ChatStore.STATE_CHAT_MSG_HISTORY] = old.filter(
                (i) => i.id !== id
            );
        },
    },
    actions: {
        async [ChatStore.ACTION_GET_MY_CHAT_LIST]({ commit, state }) {
            commit(ChatStore.MUTATION_SAVE_MYSELF_ID);

            if (loadingChatList) {
                return new Promise<ChatType[]>((resolve) =>
                    cachedLoadingChatListAction.push(resolve)
                );
            }

            loadingChatList = true;

            let cache = await dbController.getChatList();

            cache.sort((x, y) => (x.last_msg_ts < y.last_msg_ts ? 1 : -1));

            for (const item of cache) {
                if (item.business_data && !item.detail_name) {
                    const d = JSON.parse(
                        item.business_data
                    ) as BaseChatItemBusinessData;
                    if (d) {
                        if (d.detail_name) {
                            item.detail_name = d.detail_name;
                        }
                        if (!item.obj_id && d.obj_id) {
                            item.obj_id = d.obj_id;
                        }
                        if (!item.model_name && d.model_name) {
                            item.model_name = d.model_name;
                        }
                    }
                }
            }

            const buildUnreadMessage = (items: ChatType[]) => {
                let sum = 0;
                items.forEach((i) => (sum += i.unread_msg_count));
                state[ChatStore.STATE_CURRENT_UNREAD_MESSAGE_COUNT] = sum;
                return items.sort((x, y) =>
                    x.last_msg_ts < y.last_msg_ts ? 1 : -1
                );
            };

            if (cache && cache.length) {
                commit(ChatStore.MUTATION_SAVE_CHAT_LIST, cache);
                const ts = cache
                    .map((i) => Math.max(i.last_msg_ts, i.update_time))
                    .sort();
                const last = ts[ts.length - 1];
                const execute = () =>
                    new Promise<ChatType[]>((resolve) => {
                        Chat.onReady(() => {
                            xim.fetchChatListAfter(last)!.then((r) => {
                                const list = filterActiveChats(
                                    r.args[0] as RawChatItem[]
                                );
                                const items = list.map((i) => buildChatItem(i));
                                if (items && items.length) {
                                    cache = dbController.mergeChatList(
                                        cache,
                                        items
                                    );
                                }
                                resolve(buildUnreadMessage(cache));
                            });
                        });
                    }).finally(() => (loadingChatList = false));

                return await execute().then((d) => clearAction(d));
            }

            const execute = () =>
                new Promise<ChatType[]>((resolve) => {
                    Chat.onReady(() => {
                        xim.fetchChatList().then((data) => {
                            if (!data) {
                                return resolve([]);
                            }
                            const chatList = filterActiveChats(
                                data.args[0] as RawChatItem[]
                            );
                            const items = chatList.map((chat) =>
                                buildChatItem(chat)
                            );
                            dbController.saveChatList(items);
                            commit(ChatStore.MUTATION_SAVE_CHAT_LIST, items);
                            resolve(buildUnreadMessage(items));
                        });
                    });
                }).finally(() => (loadingChatList = false));

            return await execute().then((d) => clearAction(d));
        },
        async [ChatStore.ACTION_FORCE_RELOAD_CHAT_LIST]({ commit }) {
            return new Promise<ChatType[]>((resolve) => {
                Chat.onReady(() => {
                    xim.fetchChatList().then((data) => {
                        if (!data) {
                            return resolve([]);
                        }
                        const chatList = filterActiveChats(
                            data.args[0] as RawChatItem[]
                        );
                        const items = chatList.map((chat) =>
                            buildChatItem(chat)
                        );
                        dbController.saveChatList(items);
                        commit(ChatStore.MUTATION_SAVE_CHAT_LIST, items);
                        resolve(items);
                    });
                });
            });
        },
        async [ChatStore.ACTION_REBUILD_UNREAD_MESSAGE_COUNT]({ state }) {
            let items = await dbController.getChatList();
            let sum = 0;
            items.forEach((i) => (sum += i.unread_msg_count));
            state[ChatStore.STATE_CURRENT_UNREAD_MESSAGE_COUNT] = sum;
        },
        async [ChatStore.ACTION_UPDATE_CHAT_UNREAD_MESSAGE_COUNT](
            { dispatch },
            p: { chat: number; unread: number }
        ) {
            dbController
                .updateChat4UnreadCount(p.chat, p.unread)
                .then(() =>
                    dispatch(ChatStore.ACTION_REBUILD_UNREAD_MESSAGE_COUNT)
                );
        },
        async [ChatStore.ACTION_GET_CHAT_MESSAGES]({ state, commit, getters }) {
            const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            const chat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            const isMember = state[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER];
            if (chatId == null) return;
            let data: Message[] = [];
            const getMessages = async () => {
                const o = {
                    model: chat.model_name,
                    obj: chat.obj_id,
                    isMember,
                };
                data = await xim.queryLastPageMsg(chatType, chatId, 20, o);
            };
            if (isMember) {
                const cache = await dbController.getChatMessages(chatId);
                if (cache && cache.length) {
                    data = cache;
                } else {
                    await getMessages();
                }
            } else {
                await getMessages();
            }

            try {
                commit(ChatStore.MUTATION_PUSH_CHAT_MSG_HISTORY, data);
                dbController.saveChatMessages(chatId, data);
                await preCacheImgs(data);
                commit(ChatStore.MUTATION_SCROLL_TO_BOTTOM);
                return data;
            } catch (error) {
                // eslint-disable-next-line no-console
                console.error(error);
            }
        },
        async [ChatStore.ACTION_GET_CHAT_MESSAGES_BEFORE_SPECIFIC_ID](
            { state, commit, getters },
            msgId: Parameters<ChatStore.ACTION_GET_CHAT_MESSAGES_BEFORE_SPECIFIC_ID>[0]
        ) {
            const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            if (chatId == null) return;
            const chat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            const o = {
                model: chat.model_name,
                obj: chat.obj_id,
                isMember: state[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER],
            };
            const data = await xim.queryPrevPageMsg(
                chatType,
                chatId,
                msgId,
                10,
                o
            );
            commit(ChatStore.MUTATION_UNSHIFT_CHAT_MSG_HISTORY, data);
            dbController.appendMessages(chatId, data);
            return data;
        },
        async [ChatStore.ACTION_GET_CHAT_MESSAGES_AFTER_SPECIFIC_ID](
            { state, commit, getters },
            msgId: Parameters<ChatStore.ACTION_GET_CHAT_MESSAGES_AFTER_SPECIFIC_ID>[0]
        ) {
            const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            if (chatId == null) return;
            const chat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            const o = {
                model: chat.model_name,
                obj: chat.obj_id,
                isMember: state[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER],
            };
            const data = await xim.queryNextPageMsg(
                chatType,
                chatId,
                msgId,
                10,
                o
            );
            commit(ChatStore.MUTATION_PUSH_CHAT_MSG_HISTORY, data);
            dbController.appendMessages(chatId, data);
            return data;
        },
        async [ChatStore.ACTION_SEND_MESSAGE](
            { state, dispatch, getters, commit },
            params: Parameters<ChatStore.ACTION_SEND_MESSAGE>[0]
        ) {
            if (Chat.getServiceType() === ServiceType.Backend) {
                if (!state[ChatStore.STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID]) {
                    return Promise.reject(`No Uniplat Id Found`);
                }
            }
            try {
                const chat = getters[
                    ChatStore.GETTER_CURRENT_CURRENT_CHAT
                ] as ChatType;
                const data = await Chat.getSdk()
                    .model(chat.model_name)
                    .chat(chat.obj_id, orgId())
                    .sendMsg(params.msgType, params.msg);
                await dispatch(ChatStore.ACTION_GET_FRESH_MESSAGE);
                return data;
            } catch (error) {
                // eslint-disable-next-line no-console
                commit(
                    ChatStore.MUTATION_SAVE_SEND_FAIL_MESSAGE,
                    JSON.stringify(params)
                );
                return Promise.reject(error);
            }
        },
        async [ChatStore.ACTION_GET_FRESH_MESSAGE]({
            state,
            dispatch,
            commit,
        }) {
            const msgs = state[ChatStore.STATE_CHAT_MSG_HISTORY];
            let newMsgsArr;
            if (msgs == null || msgs.length === 0) {
                newMsgsArr = await dispatch(ChatStore.ACTION_GET_CHAT_MESSAGES);
            } else {
                const id = getLastMessageId(msgs);
                newMsgsArr = await dispatch(
                    ChatStore.ACTION_GET_CHAT_MESSAGES_AFTER_SPECIFIC_ID,
                    id
                );
            }
            // const lastMsg = newMsgsArr[newMsgsArr.length - 1];
            await preCacheImgs(newMsgsArr);
            commit(ChatStore.MUTATION_SCROLL_TO_BOTTOM);
        },
        async [ChatStore.ACTION_CREATE_NEW_CHAT_BY_SERVICE_MAN](
            { commit, dispatch },
            params: Parameters<ChatStore.ACTION_CREATE_NEW_CHAT_BY_SERVICE_MAN>[0]
        ) {
            const { imChatId, catalog } = await Chat.getSdk()
                .model(params.modelName)
                .chat(params.selectedListId, orgId())
                .createChat();
            const chatId = +imChatId;
            await dispatch(ChatStore.ACTION_GET_MY_CHAT_LIST);
            commit(ChatStore.MUTATION_SHOW_CHAT, !params.showByPage);
            await dispatch(
                ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION,
                chatId
            );
            return { chatId, catalog };
        },
        async [ChatStore.ACTION_CREATE_NEW_CHAT_BY_CLIENT](
            { commit, dispatch },
            params: Parameters<ChatStore.ACTION_CREATE_NEW_CHAT_BY_CLIENT>[0]
        ) {
            const { imChatId } = await Chat.getSdk()
                .model(params.modelName)
                .chat(params.selectedListId, orgId())
                .createChat(true, params.title);
            const chatId = +imChatId;
            await commit(ChatStore.MUTATION_SHOW_CHAT, true);
            await dispatch(
                ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION,
                chatId
            );
            // 打开会话后获取一下会话列表,刷新未读消息
            dispatch(ChatStore.ACTION_GET_MY_CHAT_LIST);
            return chatId;
        },
        async [ChatStore.ACTION_REGISTER_EVENT]({
            dispatch,
            commit,
            state,
            getters,
        }) {
            const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            const onNewMsg = (e: Message) => {
                const thenAction = () => {
                    if (e.type === MessageType.Withdraw) {
                        commit(ChatStore.MUTATION_WITHDRAW, +e.msg);
                    }
                    const scroll = () =>
                        state[ChatStore.STATE_FUNC_ON_NEW_MSG](e);
                    if (e.chat_id === chatId) {
                        dispatch(ChatStore.ACTION_GET_FRESH_MESSAGE).finally(
                            () => scroll()
                        );
                    } else {
                        scroll();
                    }
                };
                if (e.type === MessageType.Withdraw) {
                    dbController
                        .removeMessage(e.chat_id, +e.msg)
                        .finally(() => thenAction());
                } else {
                    thenAction();
                }
            };
            if (!chatId) {
                xim.off("msg", onNewMsg);
                xim.on("msg", onNewMsg);
                return;
            }
            const onMsgRead: ChatNotifyListener = async (e) => {
                if (chatId !== e.chat_id) return;
                const msgs = state[ChatStore.STATE_CHAT_MSG_HISTORY];
                if (msgs == null) return;
                const oldestMsgId = msgs[0].id - 1;
                const lastMsgId = msgs[msgs.length - 1].id + 1;
                const chat = getters[
                    ChatStore.GETTER_CURRENT_CURRENT_CHAT
                ] as ChatType;
                const o = {
                    model: chat.model_name,
                    obj: chat.obj_id,
                    isMember:
                        state[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_MEMBER],
                };
                const start = oldestMsgId < 1 ? 1 : oldestMsgId;
                const freshMsgs = await xim.queryMsgs(
                    chatType,
                    chatId,
                    start,
                    lastMsgId,
                    20,
                    true,
                    o
                );
                dbController.setMessageReaded(chatId, {
                    start,
                    end: lastMsgId,
                });
                commit(ChatStore.MUTATION_CLEAR_CHAT_MSG_HISTORY);
                commit(
                    ChatStore.MUTATION_PUSH_CHAT_MSG_HISTORY,
                    msgs.map((msg) => {
                        msg = { ...msg };
                        const newMsg = freshMsgs.find(
                            (freshMsg) => freshMsg.id === msg.id
                        );
                        if (newMsg != null) {
                            msg.read_count = newMsg.read_count;
                        }
                        return msg;
                    })
                );
            };
            const onInputing: ChatNotifyListener = (e) => {
                commit(ChatStore.MUTATION_SAVE_CURRENT_CHAT_INPUTING, e);
            };
            removeRegisterChatEvents.push(() => {
                xim.off("msg", onNewMsg);
                xim.off("chat_notify", "read", onMsgRead);
                xim.off("chat_notify", "user.input", onInputing);
            });
            xim.on("msg", onNewMsg);
            xim.on("chat_notify", "read", onMsgRead);
            xim.on("chat_notify", "user.input", onInputing);
        },
        async [ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION](
            { state, commit, dispatch },
            chatId: ChatStore.STATE_CHAT_CURRENT_CHAT_ID
        ) {
            if (!chatId) {
                return;
            }
            const chatList = state[
                ChatStore.STATE_MY_CHAT_ROOM_LIST
            ] as ChatType[];
            let wantedChatRoom = chatList.find((k) => k.chat_id === chatId);

            if (!wantedChatRoom) {
                const data = await xim.fetchChat(chatId);
                if (!data) {
                    return;
                }
                const chat = data.args[0] as RawChatItem;
                commit(
                    ChatStore.MUTATION_SAVE_SINGLE_CHAT,
                    (wantedChatRoom = buildChatItem(chat))
                );
            } else {
                commit(ChatStore.MUTATION_CLEAR_SINGLE_CHAT);
            }

            if (!state[ChatStore.STATE_CHAT_CURRENT_USER_UID]) {
                const userInfo = await Chat.getSdk().getUserInfo();
                commit(ChatStore.MUTATION_SET_CURRENT_USER_UID, userInfo.id);
            }

            commit(ChatStore.MUTATION_CLEAR_CHAT_MSG_HISTORY);
            commit(ChatStore.MUTATION_SAVE_CURRENT_CHAT_ID, chatId);

            await getChatModelInfo(
                wantedChatRoom.model_name,
                wantedChatRoom.obj_id,
                wantedChatRoom.detail_name
            )
                .then((info) => {
                    commit(
                        ChatStore.MUTATION_SAVE_CURRENT_CHAT_VERSION,
                        info.uniplat_version
                    );
                    commit(
                        ChatStore.MUTATION_SAVE_CURRENT_CHAT_UNIPLAT_ID,
                        info.uniplatId
                    );
                })
                .catch(console.error);
            commit(ChatStore.MUTATION_INITING_CHAT);
            removeRegisterChatEvents.forEach((k) => k());
            removeRegisterChatEvents = [];
            await dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS);
            await Promise.all([
                dispatch(ChatStore.ACTION_REGISTER_EVENT),
                dispatch(ChatStore.ACTION_GET_CHAT_MESSAGES),
            ]);
            commit(
                ChatStore.MUTATION_SAVE_CHAT_TITLE,
                wantedChatRoom.title || `会话${chatId}`
            );
            commit(ChatStore.MUTATION_INITING_CHAT_DONE);
            commit(ChatStore.MUTATION_SCROLL_TO_BOTTOM);
            (<any>state)[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_ERROR] = null;
        },
        async [ChatStore.ACTION_CLEAR_CURRENT_CHAT_DATA]({ commit, state }) {
            commit(ChatStore.MUTATION_CLEAR_CURRENT_CHAT_ID);
            commit(ChatStore.MUTATION_CLEAR_MYSELF_ID);
            commit(ChatStore.MUTATION_CLEAR_CHAT_MSG_HISTORY);
            commit(ChatStore.MUTATION_CLEAR_CHAT_TITLE);
            commit(ChatStore.MUTATION_CLEAR_CURRENT_CHAT_MEMBERS);
            (<any>state)[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_ERROR] = null;
        },
        async [ChatStore.ACTION_GET_CHAT_MEMBERS]({ commit, state }) {
            const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            if (!chatId) return;
            const getChatMembersResult = await xim.fetchChatMembers(chatId);
            if (!getChatMembersResult) return;
            const chatMembers = getChatMembersResult.args[0] as ChatMember[];
            let newChatMembers = await Promise.all(
                chatMembers.map(async (member) => {
                    let result: NonNullable<ChatStore.STATE_CURRENT_CHAT_MEMBERS>[number];
                    try {
                        const data = await getUserInfo(member.eid);
                        result = {
                            ...member,
                            ...data,
                        };
                    } catch (error) {
                        // eslint-disable-next-line no-console
                        console.error(error);
                        result = member;
                    }
                    return result;
                })
            );
            newChatMembers = newChatMembers.filter((it) => !it.is_exited);
            const member = newChatMembers.find(
                (it) =>
                    it.eid ===
                    String(state[ChatStore.STATE_CHAT_CURRENT_USER_UID])
            );
            if (member) {
                commit(ChatStore.MUTATION_CHAT_UPDATE_IS_MEMBER, true);
                commit(ChatStore.MUTATION_CHAT_UPDATE_USER_TYPE, member.type);
            } else {
                commit(ChatStore.MUTATION_CHAT_UPDATE_IS_MEMBER, false);
                commit(ChatStore.MUTATION_CHAT_UPDATE_USER_TYPE, "0");
            }
            commit(
                ChatStore.MUTATION_SAVE_CURRENT_CHAT_MEMBERS,
                unique(newChatMembers, function (item, all) {
                    return all.findIndex((k) => k.eid === item.eid);
                })
            );
        },
        async [ChatStore.ACTION_TERINATE_CHAT]({ state, dispatch }) {
            const v = state[ChatStore.STATE_CHAT_CURRENT_CHAT_VERSION];
            if (v == null) return;
            const id = Number(
                state[ChatStore.STATE_CHAT_CURRENT_CHAT_UNIPLAT_ID]
            );
            await model()
                .action("update")
                .updateInitialParams({
                    selected_list: [{ v, id }],
                })
                .addInputs_parameter({
                    Status: ChatStatus.terminated,
                })
                .execute();
            await dispatch(ChatStore.ACTION_GET_MY_CHAT_LIST);
            const firstChat = state[ChatStore.STATE_MY_CHAT_ROOM_LIST][0];
            if (firstChat == null) return;
            await getChatModelInfo(
                firstChat.model_name,
                firstChat.obj_id,
                firstChat.detail_name
            );
            await dispatch(
                ChatStore.ACTION_SAVE_CURRENT_CHAT_ID_VERSION,
                firstChat.chat_id
            );
        },
        async [ChatStore.ACTION_CHAT_START_RECEPTION]({ getters, dispatch }) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            return new Promise<void>((resolve, reject) => {
                Chat.getSdk()
                    .model(currentChat.model_name)
                    .chat(currentChat.obj_id, orgId())
                    .startChat()
                    .catch(reject)
                    .finally(() =>
                        dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS)
                            .then(resolve)
                            .catch(reject)
                    );
            });
        },
        async [ChatStore.ACTION_CHAT_FINISH_RECEPTION]({ getters, dispatch }) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            return await Chat.getSdk()
                .model(currentChat.model_name)
                .chat(currentChat.obj_id, orgId())
                .finishChat()
                .finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS));
        },
        async [ChatStore.ACTION_CHAT_USER_EXIT]({ getters, dispatch }) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            await dbController.removeChatFromList(currentChat.id);
            return await Chat.getSdk()
                .model(currentChat.model_name)
                .chat(currentChat.obj_id, orgId())
                .userExitChat()
                .finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS));
        },
        async [ChatStore.ACTION_CHAT_CS_EXIT]({ getters, dispatch }) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            await dbController.removeChatFromList(currentChat.id);
            return await Chat.getSdk()
                .model(currentChat.model_name)
                .chat(currentChat.obj_id, orgId())
                .csExitChat()
                .finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS));
        },
        async [ChatStore.ACTION_CHAT_ADD_MEMBERS](
            { getters, dispatch },
            uids: Parameters<ChatStore.ACTION_CHAT_ADD_MEMBERS>[0]
        ) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            return await Chat.getSdk()
                .model(currentChat.model_name)
                .chat(currentChat.obj_id, orgId())
                .addMember(uids.map((id) => +id))
                .finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS));
        },
        async [ChatStore.ACTION_CHAT_REMOVE_MEMBER](
            { getters, dispatch },
            uids: Parameters<ChatStore.ACTION_CHAT_REMOVE_MEMBER>[0]
        ) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            return await Chat.getSdk()
                .model(currentChat.model_name)
                .chat(currentChat.obj_id, currentChat.org_id)
                .removeMember(uids.map((id) => +id))
                .finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS));
        },
        async [ChatStore.ACTION_CHAT_ADD_CS](
            { getters, dispatch },
            uids: Parameters<ChatStore.ACTION_CHAT_ADD_CS>[0]
        ) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            return await Chat.getSdk()
                .model(currentChat.model_name)
                .chat(currentChat.obj_id, orgId())
                .addCs(uids.map((id) => Number(id)))
                .finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS));
        },
        async [ChatStore.ACTION_CHAT_REMOVE_CS](
            { getters, dispatch },
            uids: Parameters<ChatStore.ACTION_CHAT_REMOVE_CS>[0]
        ) {
            const currentChat = getters[
                ChatStore.GETTER_CURRENT_CURRENT_CHAT
            ] as ChatType;
            if (
                !currentChat ||
                !currentChat.model_name ||
                !currentChat.obj_id
            ) {
                return Promise.reject();
            }
            return await Chat.getSdk()
                .model(currentChat.model_name)
                .chat(currentChat.obj_id, orgId())
                .removeCs(uids.map((id) => Number(id)))
                .finally(() => dispatch(ChatStore.ACTION_GET_CHAT_MEMBERS));
        },
        [ChatStore.ACTION_SET_HANDLED](
            { state },
            p: {
                id: number;
                value: MessageHandled;
            }
        ) {
            const msgs = state[ChatStore.STATE_CHAT_MSG_HISTORY];
            if (msgs) {
                const t = msgs.find((i) => i.id === p.id);
                if (t) {
                    t.handled = p.value;
                    const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
                    chatId &&
                        dbController.updateMessageStatus(chatId, p.id, p.value);
                }
            }
        },
        [ChatStore.ACTION_UPDATE_CHAT]: (
            { dispatch },
            p: ChatStore.ChatUpdateParameter
        ) => {
            dbController
                .updateChat(p)
                .finally(() => dispatch(ChatStore.ACTION_GET_MY_CHAT_LIST));
        },
        [ChatStore.ACTION_SET_CHAT_ERROR]: ({ state }, chat: number) => {
            (<any>state)[ChatStore.STATE_CHAT_CURRENT_IS_CHAT_ERROR] = chat;
        },
        [ChatStore.ACTION_GET_USERINFO]: ({ state }, id: string) => {
            const cache = state[ChatStore.STATE_CHAT_USERNAME] || {};
            if (cache[id]) {
                return Promise.resolve({ id, name: cache[id] });
            }
            return new Promise<{ id: string; name: string }>(
                (resolve, reject) =>
                    getUserInfo(id)
                        .then((d) => {
                            Vue.set(
                                state[ChatStore.STATE_CHAT_USERNAME],
                                id,
                                d.name
                            );
                            resolve({ id, name: d.name });
                        })
                        .catch(reject)
            );
        },
        [ChatStore.ACTION_UPDATE_MESSAGE_READ_STATUS]: (
            { state },
            option: {
                chat: number;
                start: number;
                end?: number;
                all?: boolean;
                readed?: number;
            }
        ) => {
            const items = state[ChatStore.STATE_CHAT_MSG_HISTORY] as Message[];
            if (items) {
                if (option.end && option.end > option.start) {
                    for (let i = option.start; i <= option.end; i++) {
                        const p = items.find((m) => m.id === i);
                        if (p) {
                            p.read_count = option.all
                                ? p.total_read_count
                                : option.readed
                                ? option.readed
                                : p.read_count + 1;
                        }
                    }
                } else {
                    const p = items.find((i) => i.id === option.start);
                    if (p) {
                        p.read_count = option.all
                            ? p.total_read_count
                            : option.readed
                            ? option.readed
                            : p.read_count + 1;
                    }
                }
            }

            return dbController
                .setMessageReaded(option.chat, {
                    start: option.start,
                    end: option.end,
                    allRead: option.all,
                })
                .then(() => (state[ChatStore.STATE_CHAT_MSG_HISTORY] = items));
        },
    },
    getters: {
        [ChatStore.STATE_CHAT_MSG_HISTORY](state) {
            return state[ChatStore.STATE_CHAT_MSG_HISTORY] ?? [];
        },
        [ChatStore.STATE_CHAT_SENDING_MESSAGES](state) {
            return state[ChatStore.STATE_CHAT_SENDING_MESSAGES] || [];
        },
        [ChatStore.STATE_CURRENT_CHAT_MEMBERS](state) {
            return state[ChatStore.STATE_CURRENT_CHAT_MEMBERS] ?? [];
        },
        [ChatStore.GETTER_CURRENT_CHAT_PRESENT_MEMBERS](state) {
            return (state[ChatStore.STATE_CURRENT_CHAT_MEMBERS] ?? []).filter(
                (member) => !member.is_exited
            );
        },
        [ChatStore.STATE_CURRENT_CHAT_TITLE](state) {
            return state[ChatStore.STATE_CURRENT_CHAT_TITLE] || "";
        },
        [ChatStore.STATE_CHAT_SOURCE](state) {
            return state[ChatStore.STATE_CHAT_SOURCE] || 0;
        },
        [ChatStore.GETTER_CURRENT_CURRENT_CHAT](state) {
            const chatId = state[ChatStore.STATE_CHAT_CURRENT_CHAT_ID];
            const singleChat = state[ChatStore.STATE_SINGLE_CHAT];
            if (singleChat && singleChat.chat_id === chatId) {
                return singleChat;
            }
            const chatList = state[ChatStore.STATE_MY_CHAT_ROOM_LIST];
            return chatList.find((chat) => chat.chat_id === chatId);
        },
    },
} as Module<ChatStoreState, RootStoreState>;