// Copyright 2026 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { useCallback, type ReactNode } from 'react';
import { useSelector } from 'react-redux';
import { MediaContextMenu } from '../../components/conversation/media-gallery/MediaContextMenu.dom.tsx';
import { ForwardMessagesModalType } from '../../components/ForwardMessagesModal.dom.tsx';
import type {
  GenericMediaItemType,
  MediaItemType,
  LinkPreviewMediaItemType,
  ContactMediaItemType,
} from '../../types/MediaItem.std.ts';
import type { LocalizerType } from '../../types/Util.std.ts';
import { missingCaseError } from '../../util/missingCaseError.std.ts';
import { strictAssert } from '../../util/assert.std.ts';
import { startConversation } from '../../util/startConversation.dom.ts';
import { drop } from '../../util/drop.std.ts';
import {
  applyDeleteMessage,
  applyDeleteAttachmentFromMessage,
} from '../../util/deleteForMe.preload.ts';
import {
  getConversationIdentifier,
  getAddressableMessage,
} from '../../util/syncIdentifiers.preload.ts';
import { getMessageById } from '../../messages/getMessageById.preload.ts';
import { createLogger } from '../../logging/log.std.ts';
import { singleProtoJobQueue } from '../../jobs/singleProtoJobQueue.preload.ts';
import { MessageSender } from '../../textsecure/SendMessage.preload.ts';
import { getIntl } from '../selectors/user.std.ts';
import { useConversationsActions } from '../ducks/conversations.preload.ts';
import { useGlobalModalActions } from '../ducks/globalModals.preload.ts';
import { useLookupContact } from './ContactDetail.preload.tsx';

const log = createLogger('MediaContextMenu');

export type PropsType = Readonly<{
  mediaItem: GenericMediaItemType;
  children: ReactNode;
}>;

async function doRemoveAttachment(
  mediaItem: GenericMediaItemType
): Promise<void> {
  if (
    mediaItem.type !== 'media' &&
    mediaItem.type !== 'audio' &&
    mediaItem.type !== 'document'
  ) {
    log.error(
      `Invalid media item type ${mediaItem.type} for removing attachment`
    );
    return;
  }

  const message = await getMessageById(mediaItem.message.id);
  if (message == null) {
    log.error(
      `Missing message ${mediaItem.message.id} for removing attachment`
    );
    return;
  }

  const convo = window.ConversationController.get(
    mediaItem.message.conversationId
  );
  if (convo == null) {
    log.error(`Missing conversation ${mediaItem.message.conversationId}`);
    return;
  }

  const addressableMessage = getAddressableMessage(message.attributes);
  const conversationIdentifier = getConversationIdentifier(convo.attributes);
  if (addressableMessage == null) {
    log.error(`Message ${mediaItem.message.id} is not addressable`);
    return;
  }

  const { attachment } = mediaItem;

  if (message.get('attachments')?.length === 1) {
    log.info(`Deleting whole message ${mediaItem.message.id}`);
    await applyDeleteMessage(message.attributes);

    if (window.ConversationController.doWeHaveOtherDevices()) {
      await singleProtoJobQueue.add(
        MessageSender.getDeleteForMeSyncMessage([
          {
            type: 'delete-message',
            conversation: conversationIdentifier,
            message: addressableMessage,
            timestamp: Date.now(),
          },
        ])
      );
    }
  } else {
    log.info(`Deleting a single attachment for ${mediaItem.message.id}`);
    const attachmentData = {
      clientUuid: attachment.clientUuid,
      fallbackDigest: attachment.digest,
      fallbackPlaintextHash: attachment.plaintextHash,
    };

    const found = await applyDeleteAttachmentFromMessage(
      message,
      attachmentData,
      {
        shouldSave: true,
        logId: 'MediaItem.removeAttachment',
      }
    );

    if (!found) {
      log.error(`Missing attachment for ${mediaItem.message.id}`);
      return;
    }

    if (window.ConversationController.doWeHaveOtherDevices()) {
      await singleProtoJobQueue.add(
        MessageSender.getDeleteForMeSyncMessage([
          {
            type: 'delete-single-attachment',
            conversation: conversationIdentifier,
            message: addressableMessage,
            ...attachmentData,
            timestamp: Date.now(),
          },
        ])
      );
    }
  }
}

type SpecificMenuPropsType<ItemType extends GenericMediaItemType> = Readonly<{
  mediaItem: ItemType;
  children: ReactNode;

  i18n: LocalizerType;
  showMessage: () => void;
}>;

function AttachmentContextMenu({
  mediaItem,
  children,
  ...rest
}: SpecificMenuPropsType<MediaItemType>) {
  const { saveAttachment: doSaveAttachment } = useConversationsActions();
  const { toggleForwardMessagesModal } = useGlobalModalActions();

  const saveAttachment = useCallback(() => {
    strictAssert(mediaItem.attachment.path, 'Attachment must be downloaded');

    doSaveAttachment(mediaItem.attachment, mediaItem.message.sentAt);
  }, [doSaveAttachment, mediaItem.attachment, mediaItem.message.sentAt]);

  const forwardAttachment = useCallback(() => {
    toggleForwardMessagesModal({
      type: ForwardMessagesModalType.ForwardAttachment,
      draft: {
        originalMessageId: mediaItem.message.id,
        hasContact: false,
        isSticker: false,
        attachments: [mediaItem.attachment],
        previews: [],
      },
    });
  }, [mediaItem.message.id, mediaItem.attachment, toggleForwardMessagesModal]);

  const removeAttachment = useCallback(() => {
    drop(doRemoveAttachment(mediaItem));
  }, [mediaItem]);

  return (
    <MediaContextMenu
      {...rest}
      saveAttachment={mediaItem.attachment.path ? saveAttachment : undefined}
      forwardAttachment={
        mediaItem.attachment.path ? forwardAttachment : undefined
      }
      removeAttachment={removeAttachment}
    >
      {children}
    </MediaContextMenu>
  );
}

function LinkPreviewContextMenu({
  mediaItem,
  children,
  ...rest
}: SpecificMenuPropsType<LinkPreviewMediaItemType>) {
  const { toggleForwardMessagesModal } = useGlobalModalActions();

  const forwardAttachment = useCallback(() => {
    toggleForwardMessagesModal({
      type: ForwardMessagesModalType.ForwardAttachment,
      draft: {
        originalMessageId: mediaItem.message.id,
        hasContact: false,
        isSticker: false,
        messageBody: mediaItem.preview.url,
        previews: [mediaItem.preview],
      },
    });
  }, [mediaItem.message.id, mediaItem.preview, toggleForwardMessagesModal]);

  const copyLink = useCallback(() => {
    drop(window.navigator.clipboard.writeText(mediaItem.preview.url));
  }, [mediaItem.preview.url]);

  return (
    <MediaContextMenu
      {...rest}
      forwardAttachment={forwardAttachment}
      copyLink={copyLink}
    >
      {children}
    </MediaContextMenu>
  );
}

function ContactContextMenu({
  mediaItem,
  children,
  ...rest
}: SpecificMenuPropsType<ContactMediaItemType>) {
  const { toggleForwardMessagesModal } = useGlobalModalActions();

  const contact = useLookupContact(mediaItem.contact);

  const forwardAttachment = useCallback(() => {
    toggleForwardMessagesModal({
      type: ForwardMessagesModalType.ForwardAttachment,
      draft: {
        originalMessageId: mediaItem.message.id,
        hasContact: true,
        isSticker: false,
        previews: [],
      },
    });
  }, [mediaItem.message.id, toggleForwardMessagesModal]);

  const messageContact = useCallback(() => {
    strictAssert(
      contact.firstNumber != null && contact.serviceId != null,
      'Expected service id for contact'
    );
    startConversation(contact.firstNumber, contact.serviceId);
  }, [contact]);

  return (
    <MediaContextMenu
      {...rest}
      messageContact={
        contact.firstNumber != null && contact.serviceId != null
          ? messageContact
          : undefined
      }
      forwardAttachment={forwardAttachment}
    >
      {children}
    </MediaContextMenu>
  );
}

export function SmartMediaContextMenu({
  mediaItem,
  children,
}: PropsType): React.JSX.Element {
  const i18n = useSelector(getIntl);

  const { showConversation } = useConversationsActions();

  const { message } = mediaItem;

  const showMessage = useCallback(() => {
    showConversation({
      conversationId: message.conversationId,
      messageId: message.id,
    });
  }, [showConversation, message.conversationId, message.id]);

  const common = {
    i18n,
    showMessage,
  };

  switch (mediaItem.type) {
    case 'media':
    case 'document':
    case 'audio':
      return (
        <AttachmentContextMenu {...common} mediaItem={mediaItem}>
          {children}
        </AttachmentContextMenu>
      );
    case 'link':
      return (
        <LinkPreviewContextMenu {...common} mediaItem={mediaItem}>
          {children}
        </LinkPreviewContextMenu>
      );
    case 'contact':
      return (
        <ContactContextMenu {...common} mediaItem={mediaItem}>
          {children}
        </ContactContextMenu>
      );
    default:
      throw missingCaseError(mediaItem);
  }
}
