/*
 Copyright (C) 2020 - 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 type { StoredInvitationParams, ChatIdObj, ChatSystemMessageData } from './asmail-msgs.types';
import type { AddressCheckResult } from '~/services.types';
import type { ChatSettings } from '@bg/dataset/versions/v2/chats-db';

export type ChatMenuAction =
  'chat:info'
  | 'chat:rename'
  | 'history:export'
  | 'history:clean'
  | 'chat:close'
  | 'chat:timer'
  | 'chat:delete';

export interface ChatMenuItem {
  icon: string;
  action: string; // the composite action -> entity:action:value
  text: string;
  chatTypes: ('single' | 'group' | 'group&admin')[];
  disable?: ('chat-with-call' | 'incoming-call')[];
  isAccent?: boolean;
  margin?: boolean;
  subMenu?: ChatMenuItem[];
}

export interface ChatViewBase extends ChatIdObj {
  name: string;
  createdAt: number;
  lastUpdatedAt: number;
  callStart?: number;
  incomingCall?: {
    chatId: ChatIdObj;
    peerAddress: string;
  };
  settings: ChatSettings;
}

export interface SingleChatView extends ChatViewBase {
  isGroupChat: false;
  peerAddr: string;
  status: SingleChatStatus;
}

export type SingleChatStatus = 'initiated' | 'on' | 'invited' | 'accepted' | 'no-members';

export interface GroupChatView extends ChatViewBase {
  isGroupChat: true;
  members: Record<string, { hasAccepted: boolean }>;
  admins: string[];
  status: GroupChatStatus;
}

export type GroupChatStatus = 'initiated' | 'partially-on' | 'on' | 'invited' | 'accepted' | 'no-members';

export type ChatView = SingleChatView | GroupChatView;

export interface ChatMessageReaction {
  type?: 'emoji' | 'icon',
  name: string;
  color?: string;
}

export type ChatMessageHistoryErrors = Record<
  string,
  web3n.asmail.DeliveryException | web3n.RuntimeException | Error
>;

export interface ChatMessageHistoryChange {
  user: string;
  timestamp: number;
  type: 'body' | 'reaction' | 'error';
  value: string | Record<string, ChatMessageReaction> | ChatMessageHistoryErrors;
}

export interface ChatMessageHistory {
  changes?: ChatMessageHistoryChange[],
}

export interface ChatMessageAttachmentsInfo {
  id?: string;
  name: string;
  isFolder?: boolean;
  size: number;
}

export interface ChatMessageViewBase {
  chatId: ChatIdObj;
  /**
   * chatMessageId is generated by sender. It should be unique within a chat.
   * Expected form is an epoch in 100's of seconds, dash, short random part,
   * e.g. `${Math.floor(Date.now()/(100*1000))}-${randomString}` yielding
   * something like 17469720-eAkJmPpW.
   * A ballpark epoch extractable from chatMessageId allows for more efficient
   * lookup in implementation of message storage/dataset.
   */
  chatMessageId: string;
  timestamp: number;
  isIncomingMsg: boolean;
  /**
   * incomingMsgId presence indicates that this is an incoming message, and its
   * value identifies message in the inbox, as it is generated by receiving
   * ASMail server.
   * Database needs to know if message is incoming, but actual id value is
   * needed only when there are bytes in inbox, like with attachments.
   * Otherwise, message can be removed and id's value isn't needed.
   */
  incomingMsgId?: web3n.asmail.IncomingMessage['msgId'];
  sender: string;
}

export interface RegularMsgView extends ChatMessageViewBase {
  /**
   * chatMessageType indicates type of message
   */
  chatMessageType: 'regular';
  /**
   * relatedMessage reference other chat message.
   */
  relatedMessage?: {
    replyTo?: {
      chatMessageId: string;
      displayedText?: string;
    };
    forwardFrom?: {
      sender: string;
    },
    msgNotFound?: true;
  };
  body: string;
  status: MessageStatus;
  attachments?: ChatMessageAttachmentsInfo[];
  history?: ChatMessageHistory;
  reactions?: Record<string, ChatMessageReaction>;
  removeAfter: number;
  settings: {
    autoDeleteMessages: string;
    [key: string]: unknown;
  };
}

export interface ChatSysMsgView extends ChatMessageViewBase {
  /**
   * chatMessageType indicates type of message
   */
  chatMessageType: 'system';
  systemData: ChatSystemMessageData;
  status?: undefined;

}

export interface ChatInvitationMsgView extends ChatMessageViewBase {
  /**
   * chatMessageType indicates type of message
   */
  chatMessageType: 'invitation';
  inviteData: StoredInvitationParams;
  status?: undefined;
}

export type ChatMessageView = RegularMsgView | ChatSysMsgView | ChatInvitationMsgView;

export type IncomingMessageStatus = 'read' | 'unread';

export type OutgoingMessageStatus = 'sending' | 'sent' | 'error' | 'canceled' | 'read';

export type MessageStatus = IncomingMessageStatus | OutgoingMessageStatus;

export type ChatMessageActionType =
  'reaction'
  | 'reply'
  | 'copy'
  | 'forward'
  | 'edit'
  | 'download'
  | 'resend'
  | 'select'
  | 'info'
  | 'delete_message'
  | 'cancel_sending';

export interface ChatMessageAction {
  id: ChatMessageActionType;
  icon: {
    name: string;
    horizontalFlip?: boolean;
    rotateIcon?: 1 | 2 | 3,
  };
  title: string;
  conditions: string[];
  allowInReadonlyMode?: boolean;
  blockStart?: boolean;
  accent?: string;
  disabled?: boolean;
}

export type ChatListItemView = ChatView & {
  unread: number;
  lastMsg?: ChatMessageView | null;
};

export interface ChatException extends web3n.RuntimeException {
  type: 'chat';
  failedAddresses?: {
    addr: string;
    check?: AddressCheckResult;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    exc?: any;
  }[];
}

export interface LocalMetadataInDelivery {
  chatId: ChatIdObj;
  chatMessageId?: ChatMessageView['chatMessageId'];
  chatMessageType: ChatMessageView['chatMessageType'];
  chatSystemData?: ChatSystemMessageData;
}
