/*
Copyright (C) 2024 - 2025 3NSoft Inc.

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { appChatsSrvProxy, appDeliverySrvProxy } from '@main/services/services-provider';
import { ChatsStore } from '@main/store/chats/types';
import { ContactsStore } from '@main/store/contacts/types';
import { includesAddress, toCanonicalAddress } from '@shared/address-utils';
import { storeToRefs } from 'pinia';
import { ChatIncomingMessage, ChatMessageView, ChatSystemEvent, ChatSystemMessageData } from '~/index';
import without from 'lodash/without';
import { getAttachmentFilesInfo } from '@main/helpers/chats.helper';
import { Nullable } from '@v1nt1248/3nclient-lib';

export type HandleChatIncomingMessage = (
  msg: ChatIncomingMessage
) => Promise<void>;

export function makeIncomingMessageHandler(
  chatsStore: ChatsStore, contactsStore: ContactsStore, ownAddr: string
): HandleChatIncomingMessage {

  const { currentChatId, chatList } = storeToRefs(chatsStore);

  return async msg => {

    const { jsonBody, msgId } = msg;
    const { chatMessageType, chatId, members = [] } = jsonBody;

    const membersFromDb = chatList.value[chatId]?.members;
    const realMembers = membersFromDb || members;
    if (
      chatMessageType === 'system' ||
      (chatMessageType === 'regular' && includesAddress(realMembers, ownAddr))
    ) {
      processMessage(
        chatsStore, contactsStore, ownAddr, msg, currentChatId.value
      );
    } else {
      appDeliverySrvProxy.removeMessageFromInbox([msgId]);
    }

  };
}

function isGroupChat(members: string[], ownAddr: string): boolean {
  const all = members.map(toCanonicalAddress);
  const other = without(all, toCanonicalAddress(ownAddr));
  return (other.length > 1);
}

const systemMessageHandlers: Partial<Record<ChatSystemEvent, string>> = {
  'update:status': 'handleUpdateMessageStatus',
  'update:chatName': 'handleUpdateChatName',
  'delete:members': 'handlerDeleteChatMembers',
  'add:members': 'handlerAddChatMembers',
  'remove:members': 'handlerRemoveChatMembers',
  'delete:message': 'handlerDeleteChatMessage',
};

async function processMessage(
  chatsStore: ChatsStore, contactsStore: ContactsStore, me: string,
  msg: ChatIncomingMessage, currentChatId: Nullable<string>
): Promise<void> {
  await contactsStore.fetchContactList();

  const {
    jsonBody,
    msgId,
    attachments,
    plainTxtBody,
    htmlTxtBody,
    sender,
  } = msg;
  const {
    chatId,
    chatMessageType = 'regular',
    chatMessageId,
    members,
    admins = [],
    chatName,
    initialMessageId,
    chatSystemData = {} as ChatSystemMessageData,
  } = jsonBody;
  const isChatPresent = Object.keys(chatsStore.chatList).includes(chatId!);

  if (!isChatPresent) {
    const name = isGroupChat(members, me)
      ? chatName || 'Group chat'
      : chatName || sender;
    await chatsStore.createChat({ chatId, members, admins, name });
  }

  switch (chatMessageType) {
    case 'system':
      const { event, value, displayable = false } = chatSystemData;
      const systemMessageHandler = systemMessageHandlers[event];
      //@ts-ignore
      systemMessageHandler && chatsStore[systemMessageHandler] && await chatsStore[systemMessageHandler]({
        chatId, msgId, sender, chatMessageId, value, displayable,
      });
      break;
    default:
      const msgView: ChatMessageView<'incoming'> = {
        msgId,
        attachments: await getAttachmentFilesInfo({
          incomingAttachments: attachments
        }),
        messageType: 'incoming',
        sender,
        body: plainTxtBody! || htmlTxtBody! || '',
        chatId,
        chatMessageType,
        chatMessageId,
        initialMessageId: initialMessageId || null,
        timestamp: Date.now(),
        status: 'received',
      };

      await appChatsSrvProxy.upsertMessage(msgView);
      if (chatId === currentChatId) {
        chatsStore.currentChatMessages.push(msgView);
      }
      await chatsStore.getChatList();
      chatsStore.sendSystemMessage({
        chatId,
        chatMessageId,
        recipients: [sender],
        event: 'update:status',
        value: 'received',
      });
      if (!attachments) {
        appDeliverySrvProxy.removeMessageFromInbox([msgId]);
      }
      break;
  }
};
