import { create } from 'zustand';
import { RealTimeAPI } from '@/channel/realTimeAPI';
import { olog, isEmpty, groupBy, sortArrayByOrder, logWebsocket } from '@/utils/commons';
import { receivedMsgTypeMapped, handleNotification } from '@/channel/bubbleMsgUtils';
import { fetchConversationsList, fetchTemplates, fetchConversationsSearch, UNREAD_MARK, fetchTags } from '@/actions/ConversationActions';
import { devtools } from 'zustand/middleware';
import { WS_URL, DATETIME_FORMAT } from '@/config';
import dayjs from 'dayjs';

const replaceObjectsByKey = (arr1, arr2, key) => {
  const map = new Map(arr2.map(ele => [ele[key], ele]));
  return arr1.map(item => map.has(item[key]) ? map.get(item[key]) : item);
}

// const WS_URL = 'ws://202.103.68.144:8888/whatever/';
// const WS_URL = 'ws://120.79.9.217:10022/whatever/';
const conversationRow = {
  sn: '',
  opi_sn: '',
  coli_sn: '',
  coli_id: '',
  last_received_time: '',
  last_send_time: '',
  unread_msg_count: '',
  whatsapp_name: '',
  customer_name: '',
  whatsapp_phone_number: '',
  top_state: 0,
};

const initialConversationState = {
  // websocket: null,
  // websocketOpened: null,
  // websocketRetrying: null,
  // websocketRetrytimes: null,

  errors: [], // 错误信息
  initialState: false,

  // templates: [],

  closedConversationsList: [], // 已关闭的对话列表
  conversationsList: [], // 对话列表
  topList: [],
  pageList: [],
  currentConversation: {}, // 当前对话

  activeConversations: {}, // 激活的对话的消息列表: { [conversationId]: <messageItem>[] }

  referenceMsg: {},
  complexMsg: {},

  totalNotify: 0,
  msgListLoading: false,

  detailPopupOpen: false,
};

const globalNotifySlice = (set) => ({
  globalNotify: [],
  setGlobalNotify: (notify) => set(() => ({ globalNotify: notify })),
  addGlobalNotify: (notify) => set((state) => ({ globalNotify: [notify, ...state.globalNotify] })),
  removeGlobalNotify: (id) => set((state) => ({ globalNotify: state.globalNotify.filter(item => item.id !== id) })),
  clearGlobalNotify: () => set(() => ({ globalNotify: [] })),
})

// 顾问的自定义标签
const tagsSlice = (set) => ({
  tags: [],
  setTags: (tags) => set({ tags }),
  addTag: (tag) => set((state) => ({ tags: [...state.tags, tag] })),
  removeTag: (tag) => set((state) => ({ tags: state.tags.filter((t) => t.key !== tag.key) })),
  updateTag: (tag) => set((state) => ({ tags: state.tags.map((t) => (t.key === tag.key ? tag : t)) })),
  resetTags: () => set({ tags: [] }),
});

// 会话筛选
const filterObj = {
  pagesize: '',
  search: '',
  otype: '',
  tags: [],
  loadNextPage: true,
  lastpagetime: '',
  lastactivetime: '', // dayjs().subtract(30, "days").format('YYYY-MM-DD 00:00'), // 30 days
};
const filterSlice = (set) => ({
  filter: structuredClone(filterObj),
  setFilter: (filter) => set(state => ({ filter: { ...state.filter, ...filter } })),
  setFilterSearch: (search) => set((state) => ({ filter: { ...state.filter, search } })),
  setFilterOtype: (otype) => set((state) => ({ filter: {...state.filter, otype } })),
  setFilterTags: (tags) => set((state) => ({ filter: {...state.filter, tags } })),
  setFilterLoadNextPage: (loadNextPage) => set((state) => ({ filter: {...state.filter, loadNextPage } })),
  resetFilter: () => set({ filter: structuredClone(filterObj) }),
})
// WABA 模板
const templatesSlice = (set) => ({
  templates: [],
  setTemplates: (templates) => set({ templates }),
});

const websocketSlice = (set, get) => ({
  websocket: null,
  websocketOpened: null,
  websocketRetrying: null,
  websocketRetrytimes: null,

  setWebsocket: (websocket) => set({ websocket }),
  setWebsocketOpened: (opened) => set({ websocketOpened: opened }),
  setWebsocketRetrying: (retrying) => set({ websocketRetrying: retrying }),
  setWebsocketRetrytimes: (retrytimes) => set({ websocketRetrytimes: retrytimes, websocketRetrying: retrytimes > 0 }),

  connectWebsocket: (userId) => {
    const { setWebsocket, setWebsocketOpened, setWebsocketRetrytimes, addError, handleMessage, activeConversations } = get();

    const realtimeAPI = new RealTimeAPI(
      {
        url: `${WS_URL}?opisn=${userId || ''}&_spam=${Date.now().toString()}`,
        protocol: 'WhatsApp',
      },
      () => {
        setWebsocketOpened(true);
        setWebsocketRetrytimes(0);
      },
      () => {
        setWebsocketOpened(false);
        const newMsgList = Object.keys(activeConversations).reduce((acc, key) => {
          const newMsgList = activeConversations[key].slice(-10);
          acc[key] = newMsgList;
          return acc;
        }, {});
        set({ activeConversations: newMsgList, currentConversation: {} });
      },
      (n) => setWebsocketRetrytimes(n)
    );

    realtimeAPI.onError(() => addError('Error'));
    realtimeAPI.onMessage(handleMessage);
    realtimeAPI.onCompletion(() => addError('Connection broken'));

    olog('Connecting to websocket...');
    setWebsocket(realtimeAPI);
  },
  disconnectWebsocket: () => {
    const { websocket } = get();
    if (websocket) websocket.disconnect();
    return set({ websocket: null });
  },
  reconnectWebsocket: (userId) => {
    const { disconnectWebsocket, connectWebsocket } = get();
    disconnectWebsocket();
    setTimeout(() => {
      connectWebsocket(userId);
    }, 500);
  },
  handleMessage: (data) => {
    // olog('websocket Message IN ⬇', JSON.stringify(data, null, 2));
    logWebsocket(data, 'I');
    // olog('websocket Messages ----', data);
    // console.log(data);
    const { updateMessageItem, sentOrReceivedNewMessage, addGlobalNotify } = get();
    const { errcode, errmsg, result } = data;

    if (!result) {
      return false;
    }
    let resultType = result?.action || result.type;
    if (errcode !== 0) {
      // addError('Error Connecting to Server');
      resultType = 'error';
    }
    // console.log(resultType, 'result.type');
    const msgObj = receivedMsgTypeMapped[resultType].getMsg(result);
    const msgRender = receivedMsgTypeMapped[resultType].contentToRender(msgObj);
    const msgUpdate = receivedMsgTypeMapped[resultType].contentToUpdate(msgObj);
    // console.log('msgRender msgUpdate', msgRender, msgUpdate);
    if ([
      'whatsapp.message.updated', 'message', 'error',
      'email.updated',
    ].includes(resultType)) {
      updateMessageItem(msgUpdate);
    }
    if (!isEmpty(msgRender)) {
      sentOrReceivedNewMessage(msgRender.conversationid, msgRender);
      handleNotification(msgRender.senderName, {
        body: msgRender?.text || `[ ${msgRender.type} ]`,
        ...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}),
      });
    }
    // 其他通知, 不是消息
    if ([
      'email.action.received',
    ].includes(resultType)) {
      const msgNotify = receivedMsgTypeMapped[resultType].contentToNotify(msgObj);
      addGlobalNotify(msgNotify);
    }
    // console.log('handleMessage*******************');
  },
});

const referenceMsgSlice = (set) => ({
  referenceMsg: {},
  setReferenceMsg: (referenceMsg) => set({ referenceMsg }),
});

const complexMsgSlice = (set) => ({
  complexMsg: {},
  setComplexMsg: (complexMsg) => set({ complexMsg }),
});

const conversationSlice = (set, get) => ({
  conversationsListLoading: false,
  conversationsList: [],
  currentConversation: {},
  closedConversationsList: [],

  topList: [],
  pageList: [],

  setConversationsListLoading: (conversationsListLoading) => set({ conversationsListLoading }),

  /**
   * 首次加载
   * 搜索结果
   */
  setConversationsList: (conversationsList) => {
    const { activeConversations,  } = get();
    const conversationsMapped = conversationsList.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {});

    const conversationsTopStateMapped = groupBy(conversationsList, 'top_state');

    return set({
      topList: conversationsTopStateMapped[1] || [],
      // conversationsList: conversationsTopStateMapped[0],
      pageList: conversationsTopStateMapped[0] || [],
      conversationsList,
      activeConversations: { ...conversationsMapped, ...activeConversations }
    })
  },
  setClosedConversationList: (closedConversationsList) => {
    const { activeConversations, } = get();
    const listMapped = closedConversationsList.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {});
    return set({ closedConversationsList, activeConversations: { ...activeConversations, ...listMapped } });
  },
  addToConversationList: (newList, position='top') => {
    const { activeConversations, conversationsList, } = get();
    // const conversationsIds = Object.keys(activeConversations);
    const conversationsIds = conversationsList.map((chatItem) => `${chatItem.sn}`);
    const newConversations = newList.filter((conversation) => !conversationsIds.includes(`${conversation.sn}`));
    const newConversationsMapped = newConversations.reduce((r, v) => ({ ...r, [`${v.sn}`]: [] }), {});

    const newListIds = newList.map((chatItem) => `${chatItem.sn}`);
    const withoutNew = conversationsList.filter((item) => !newListIds.includes(`${item.sn}`));

    const updateList = replaceObjectsByKey(conversationsList, newList, 'sn');

    const mergedList = position==='top' ? [...newList, ...withoutNew] : [...updateList, ...newConversations];
    const mergedListMapped = groupBy(mergedList, 'top_state');

    const refreshTotalNotify = mergedList.reduce((r, c) => r+(c.unread_msg_count === UNREAD_MARK ? 0 : c.unread_msg_count), 0);

    return set((state) => ({
      topList: mergedListMapped[1] || [],
      pageList: mergedListMapped[0] || [],
      conversationsList: mergedList,
      activeConversations: { ...activeConversations, ...newConversationsMapped },
      totalNotify: refreshTotalNotify,
      // totalNotify: state.totalNotify + newConversations.map((ele) => ele.unread_msg_count).reduce((acc, cur) => acc + (cur || 0), 0),
    }));
  },
  delConversationitem: (conversation) => {
    const { conversationsList, activeConversations } = get();
    const targetId = conversation.sn;
    const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
    conversationsList.splice(targetIndex, 1);
    const mergedListMapped = groupBy(conversationsList, 'top_state');

    return set({
      topList: mergedListMapped['1'] || [],
      pageList: mergedListMapped['0'] || [],
      conversationsList: [...conversationsList],
      activeConversations: { ...activeConversations, [`${targetId}`]: [] },
      currentConversation: {},
    });
  },
  setCurrentConversation: (conversation) => {
    // 清空未读
    const { conversationsList } = get();
    const targetId = conversation.sn;
    const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
    const targetItemFromList = conversationsList.find((ele) => String(ele.sn) === String(targetId));
    targetIndex !== -1
      ? conversationsList.splice(targetIndex, 1, {
          ...conversationsList[targetIndex],
          unread_msg_count: 0,
        })
      : null;
      const mergedListMapped = groupBy(conversationsList, 'top_state');

    return set((state) => ({
      totalNotify: state.totalNotify - (conversation.unread_msg_count || 0),
      currentConversation: Object.assign({}, conversation, targetItemFromList),
      referenceMsg: {},
      // topList: mergedListMapped['1'] || [],
      // pageList: mergedListMapped['0'] || [],
      // conversationsList: [...conversationsList],
    }));
  },
  updateCurrentConversation: (conversation) => {
    const { updateConversationItem, currentConversation } = get();
    updateConversationItem({...currentConversation, ...conversation})

    return set((state) => ({ currentConversation: { ...state.currentConversation, ...conversation } }))
  },
  updateConversationItem: (conversation) => {
    const { conversationsList } = get();
    const targetId = conversation.sn;
    const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
    targetIndex !== -1
      ? conversationsList.splice(targetIndex, 1, {
          ...conversationsList[targetIndex],
          ...conversation,
        })
      : null;
    const mergedListMapped = groupBy(conversationsList, 'top_state');

    return set({
      topList: mergedListMapped['1'] || [],
      pageList: mergedListMapped['0'] || [],
      conversationsList: [...conversationsList]
    });
  },
});

const messageSlice = (set, get) => ({
  totalNotify: 0,
  msgListLoading: false,
  activeConversations: {},
  refreshTotalNotify: () => set((state) => ({ totalNotify: state.conversationsList.reduce((r, c) => r+(c.unread_msg_count === UNREAD_MARK ? 0 : c.unread_msg_count), 0) })),
  setMsgLoading: (msgListLoading) => set({ msgListLoading }),
  receivedMessageList: (conversationid, msgList) =>
    set((state) => ({
      // msgListLoading: false,
      activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList },
    })),
  updateMessageItem: (message) => {
    // msgUpdate
    // console.log('UPDATE_SENT_MESSAGE_ITEM-----------------------------------------------------------------', message);
    // 更新会话中的消息
    const { activeConversations, conversationsList, currentConversation, setFilter } = get();
    const targetId = message.conversationid;
    const targetMsgs = (activeConversations[String(targetId)] || []).map((ele) => {
      // 更新状态
      // * 已读的不再更新状态, 有时候投递结果在已读之后返回
      // if (ele.id === ele.actionId && ele.actionId === message.actionId) {
      if (ele.actionId === message.actionId) {
        // WABA: 同步返回, 根据actionId 更新消息的id
        return { ...ele, id: message.id, status: ele.status === 'read' ? ele.status : message.status, dateString: message.dateString };
      } else if (String(ele.id) === String(message.id)) {
        // WABA: 异步的后续状态更新, id已更新为wamid
        // console.log('old msg ele', ele);
        const renderStatus = message?.data?.status ? { status: { ...ele.data.status, loading: 0, download: true } } : {};
        const keepReply = ele.reply ? { reply: ele.reply } : {};
        const keepTemplate = ele.template ? { template: ele.template, template_origin: ele.template_origin, text: ele.text } : {};
        return { ...ele, ...message, id: message.id, status: ele.status === 'read' ? ele.status : message.status, dateString: message.dateString, data: { ...ele.data, ...renderStatus  }, ...keepReply, ...keepTemplate };
      }
      return ele;
    });
    // 显示会话中其他客户端发送的消息
    const targetMsgsIds = targetMsgs.map((ele) => ele.id);
    if (!targetMsgsIds.includes(message.id)) {
      targetMsgs.push(message);
    }

    const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
    let newConversations = [];
    if (targetIndex !== -1) { // 'delivered'
      // 更新列表的时间
      conversationsList.splice(targetIndex, 1, {
        ...conversationsList[targetIndex],
        last_received_time: message.status === 'received' ? dayjs(message.deliverTime).add(8, 'hours').format(DATETIME_FORMAT) : conversationsList[targetIndex].last_received_time,
        conversation_expiretime: message?.conversation?.expireTime || conversationsList[targetIndex].conversation_expiretime || '', // 保留使用UTC时间
      });
    } else if (targetIndex === -1) {
      // 当前客户端不存在的会话
      // todo: 设置为当前(在WhatsApp返回号码不一致时)
      newConversations = [{
        ...conversationRow,
        ...message,
        sn: targetId,
        opi_sn: currentConversation.opi_sn, // todo: coli sn
        last_received_time: message.date,
        unread_msg_count: 0,
        whatsapp_name: message.to, //message?.senderName || message?.sender || '',
        customer_name: message.to, // message?.senderName || message?.sender || '',
        conversation_expiretime: message?.conversation?.expireTime || '', // 保留使用UTC时间
        whatsapp_phone_number: message.type === 'email' ? null : message.to,
        show_default: message.to || '',
        last_message: message,
        channels: {
          "email": message.type === 'email' ? message.from : null,
          "phone_number": message.type === 'email' ? null : message.from,
          "whatsapp_phone_number": message.type === 'email' ? null : message.from,
        },
      }];
    }
    const mergedList = [...newConversations, ...conversationsList]
    const mergedListMapped = groupBy(mergedList, 'top_state');
    setFilter({ loadNextPage: true });

    return set({
      topList: mergedListMapped['1'] || [],
      pageList: mergedListMapped['0'] || [],
      conversationsList: mergedList,
      activeConversations: { ...activeConversations, [String(targetId)]: targetMsgs },
    });
  },
  sentOrReceivedNewMessage: (targetId, message) => {
    // msgRender:
    const { activeConversations, conversationsList, currentConversation, totalNotify, setFilter } = get();
    const targetMsgs = activeConversations[String(targetId)] || [];
    const targetIndex = conversationsList.findIndex((ele) => Number(ele.sn) === Number(targetId));
    const lastReceivedTime = (message.type !== 'system' && message.sender !== 'me') ? dayjs(message.date).add(8, 'hours').format(DATETIME_FORMAT) : null;
    const newConversation =
      targetIndex !== -1
        ? {
            ...conversationsList[targetIndex],
            last_received_time: lastReceivedTime || conversationsList[targetIndex].last_received_time,
            unread_msg_count:
              Number(targetId) !== Number(currentConversation.sn) && message.sender !== 'me'
                ? conversationsList[targetIndex].unread_msg_count + 1
                : conversationsList[targetIndex].unread_msg_count,
            last_message: message,
          }
        : {
            ...conversationRow,
            ...message,
            sn: targetId,
            opi_sn: currentConversation.opi_sn, // todo: coli sn
            last_received_time: dayjs(message.date).add(8, 'hours').format(DATETIME_FORMAT),
            unread_msg_count: message.sender === 'me' ? 0 : 1,
            whatsapp_name: message?.senderName || message?.sender || '',
            customer_name: message?.senderName || message?.sender || '',
            whatsapp_phone_number: message.type === 'email' ? null : message.from,
            show_default: message?.senderName || message?.sender || message.from || '',
            last_message: message,
            channels: {
              "email": message.type === 'email' ? message.from : null,
              "phone_number": message.type === 'email' ? null : message.from,
              "whatsapp_phone_number": message.type === 'email' ? null : message.from,
            },
          };
    if (String(targetId)!== '0') {
      conversationsList.splice(targetIndex, 1);
      conversationsList.unshift(newConversation);
    }
    // console.log('find in list, i:', targetIndex);
    // console.log('find in list, chat updated and Top: \n', JSON.stringify(newConversation, null, 2));
    // console.log('list updated : \n', JSON.stringify(conversationsList, null, 2));
    const mergedListMapped = groupBy(conversationsList, 'top_state');
    setFilter({ loadNextPage: true });

    const isCurrent = Number(targetId) === Number(currentConversation.sn);
    const updatedCurrent = isCurrent
      ? {
          ...currentConversation,
          last_received_time: dayjs(message.date).add(8, 'hours').format(DATETIME_FORMAT),
          conversation_expiretime: dayjs(message.date).add(24, 'hours').format(DATETIME_FORMAT),
          last_message: message,
        }
      : {...currentConversation, last_message: message,};
    return set({
      currentConversation: updatedCurrent,
      topList: mergedListMapped['1'] || [],
      pageList: mergedListMapped['0'] || [],
      conversationsList: [...conversationsList],
      totalNotify: totalNotify + (message.sender === 'me' ? 0 : 1),
      activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] },
    });
  },
});

/**
 * Email
 */
const emailSlice = (set, get) => ({
  emailMsg: { id: -1, conversationid: '', actionId: '', order_opi: '', coli_sn: '', msgOrigin: {} },
  setEmailMsg: (emailMsg) => set({ emailMsg }),
  detailPopupOpen: false,
  setDetailOpen: (v) => set({ detailPopupOpen: v }),
  openDetail: () => set(() => ({ detailPopupOpen: true })),
  closeDetail: () => set(() => ({ detailPopupOpen: false })),
})

export const useConversationStore = create(
  devtools((set, get) => ({
    ...initialConversationState,
    ...websocketSlice(set, get),
    ...conversationSlice(set, get),
    ...templatesSlice(set, get),
    ...messageSlice(set, get),
    ...referenceMsgSlice(set, get),
    ...complexMsgSlice(set, get),
    ...tagsSlice(set, get),
    ...filterSlice(set, get),
    ...globalNotifySlice(set, get),
    ...emailSlice(set, get),

    // state actions
    addError: (error) => set((state) => ({ errors: [...state.errors, error] })),
    setInitial: (v) => set({ initialState: v }),

    // side effects
    fetchInitialData: async ({userId, whatsAppBusiness, ...loginUser}) => {
      const { addToConversationList, setTemplates, setInitial, setTags } = get();

      const conversationsList = await fetchConversationsList({ opisn: userId });
      addToConversationList(conversationsList);

      const templates = await fetchTemplates({ waba: whatsAppBusiness });
      setTemplates(templates);

      const myTags = await fetchTags({ opisn: userId});
      setTags(myTags);

      setInitial(true);
    },

    reset: () => set(initialConversationState),
  }), { name: 'cStore' })
);

export default useConversationStore;
