import {
  InsertConversation,
  InsertConversationProperties,
  SelectConversation,
  SelectConversationProperties,
  SelectGroupMembership,
  SelectMessageWithAssets,
  SelectMessageWithAssetsReactions,
  SelectUser,
} from '@modules/chats/db/interfaces';
import {
  ChatEventSubtype,
  ChatEventType,
  ChatNotificationType,
  Conversation,
  ConversationFromServer,
  ConversationLatest,
  ConversationLatestFromServer,
  ConversationProperties,
  HuDataMember,
  ImCreatedEvent,
  MessageStatus,
} from '@modules/chats/interfaces';
import {LOCAL_INITIAL_VERSION} from '@modules/chats/constants';
import {persistedUserId} from '@services/localStorage';
import {getTimeFromUlid} from '@shared/utils';

import {mapUserFromDB} from './userMapper';
import {
  mapLatestMessageFromDB,
  mapMessageFromDB,
  EXCLUSIVE_SUBTYPES,
} from './messageMapper';
import {getTextWithHighlights} from '../mentions';
import {mapAssetFromService} from './assetMapper';

export const mapConversationToDB = (
  conversation: Conversation,
): InsertConversation => {
  return {
    ...conversation,
    is_im: !!conversation.is_im,
    is_private: !!conversation.is_private,
    is_user_deleted: !!conversation.is_user_deleted,
    is_channel: !!conversation.is_channel,
    user_id: conversation.user ? Number(conversation.user) : null,
    creator_id: conversation.creator,
    description: conversation.purpose?.value,
    mentioned: conversation.mentioned ?? false,
    latest_message_id: conversation.latest
      ? conversation.latest.client_msg_id
      : null,
  };
};

export const mapConversationPropertiesToDB = (
  conversation: Conversation,
): InsertConversationProperties => {
  return {
    conversation_id: conversation.id,
    version: conversation.properties?.version || -1,
    notification_type:
      conversation.properties?.notification_status?.notification_type,
    muted_until: conversation.properties?.notification_status?.until || null,
    pinned_at: conversation.properties?.pinned_at || null,
    marked_unread_at: conversation.properties?.marked_unread_at || null,
  };
};

export const mapConversationPropertiesFromDB = (
  data: SelectConversationProperties,
): ConversationProperties => {
  return {
    version: data.version || LOCAL_INITIAL_VERSION,
    notification_status: {
      notification_type: data.notification_type as ChatNotificationType,
      until: data.muted_until,
    },
    pinned_at: data.pinned_at,
    marked_unread_at: data.marked_unread_at,
  };
};
export const mapConversationFromDB = (
  data: SelectConversation & {
    latest_message: Nullable<SelectMessageWithAssets>;
    user: Nullable<SelectUser>;
    groupMemberships?: Nullable<SelectGroupMembership[]>;
    messages?: Nullable<SelectMessageWithAssetsReactions[]>;
    properties?: Nullable<SelectConversationProperties>;
  },
  /**
   * Current user id. Used to check if the current user is a member of the conversation
   */
  userId: number,
): Conversation => {
  const conversation: Conversation = {
    id: data.id,
    created: data.created,
    version: data.version || 0,
    other_user: data.user ? mapUserFromDB(data.user) : undefined,
    message_count: 0,
    is_im: data.is_im || false,
    is_user_deleted: data.is_user_deleted || false,
    latest: data.latest_message
      ? mapLatestMessageFromDB(data.latest_message)
      : null,
    readMembership: null,
    priority: data.priority || 0,
    user: data.user_id ? `${data.user_id}` : undefined,
    is_private: data.is_private || false,
    num_members: data.num_members || 0,
    creator: data.creator_id!,
    is_channel: data.is_channel || false,
    name: data.name || undefined,
    picture_url: data.picture_url || undefined,
    purpose: data.description ? {value: data.description} : undefined,
    is_active: data.is_active || true,
    mentioned: data.mentioned || false,
    isConversationWithCurrentUser: !data.is_channel && data.user_id === userId,
    messages:
      data.messages?.map(message =>
        mapMessageFromDB({
          message,
          assets: message.assets,
          reactions: message.reactions,
        }),
      ) || [],
    properties: data.properties
      ? mapConversationPropertiesFromDB(data.properties)
      : undefined,
  };

  return conversation;
};

export const mapImEventToConversation = (
  event: ImCreatedEvent,
  otherUser: HuDataMember,
  userId: Nullable<number>,
): Conversation => {
  return {
    id: event.channel,
    created: getTimeFromUlid(event.ts),
    is_im: true,
    is_private: true,
    is_user_deleted: false,
    user: `${otherUser.id}`,
    latest: null,
    readMembership: null,
    creator: event.user,
    version: event.hu_data.version,
    other_user: otherUser,
    message_count: 0,
    num_members: 2,
    priority: 0,
    is_channel: false,
    name: undefined,
    picture_url: undefined,
    is_active: true,
    purpose: undefined,
    isConversationWithCurrentUser: otherUser.id === userId,
    properties: {
      version: LOCAL_INITIAL_VERSION,
      notification_status: {
        notification_type: ChatNotificationType.All,
        until: null,
      },
      pinned_at: null,
      marked_unread_at: null,
    },
  };
};

export const mapLatestFromServer = async (
  latest: ConversationLatestFromServer,
  {version}: {version: number},
): Promise<ConversationLatest> => {
  const formattedText = latest.highlighted_spans?.length
    ? await getTextWithHighlights({
        message: latest.text,
        highlightedSpans: latest.highlighted_spans,
      })
    : latest.text;
  const hidden = latest.subtype === ChatEventSubtype.messageDelete;
  return {
    client_msg_id: latest.client_msg_id,
    text: latest.text,
    user: `${latest.sender_id}`,
    ts: latest.ts,
    blocks: latest.blocks,
    username: latest.username,
    attachments: mapAssetFromService(latest.attachments),
    deliver_status: latest.read_status,
    status:
      Number(latest.sender_id) === persistedUserId
        ? MessageStatus.Sent
        : undefined,
    version: latest.version ?? version ?? LOCAL_INITIAL_VERSION,
    edited: !!latest.edited,
    hidden: hidden,
    highlighted_spans: latest.highlighted_spans || [],
    formatted_text: formattedText,
    deleted_from_ui:
      hidden &&
      latest.type === ChatEventType.message &&
      EXCLUSIVE_SUBTYPES.includes(latest.subtype!),
    generated_by: latest.generated_by,
  };
};

export const mapConversationFromServer = async (
  conversation: ConversationFromServer,
): Promise<Conversation> => {
  return {
    ...conversation,
    picture_url: conversation.hu_data.picture_url,
    readMembership: conversation.hu_data.membership,
    is_active: true,
    version: conversation.hu_data.version,
    other_user: conversation.hu_data.other_membership?.user.hu_data.member,
    message_count: conversation.hu_data.message_count,
    latest: conversation.latest
      ? await mapLatestFromServer(conversation.latest, {
          version: conversation.hu_data.latest_msg_version,
        })
      : null,
    mentioned: !!conversation.hu_data.mentioned,
    isConversationWithCurrentUser:
      !conversation.is_channel &&
      conversation.hu_data.other_membership?.user.hu_data.member.id ===
        persistedUserId,
    properties: conversation.properties,
  };
};
