package org.thoughtcrime.securesms.conversation;

import androidx.annotation.NonNull;

import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection;
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart;
import org.thoughtcrime.securesms.database.model.MmsMessageRecord;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.util.MessageRecordUtil;
import org.thoughtcrime.securesms.util.MessageConstraintsUtil;

import java.util.Set;
import java.util.stream.Collectors;

public final class MenuState {

  private static final int MAX_FORWARDABLE_COUNT = 32;

  private final boolean forward;
  private final boolean reply;
  private final boolean details;
  private final boolean saveAttachment;
  private final boolean resend;
  private final boolean copy;
  private final boolean delete;
  private final boolean reactions;
  private final boolean paymentDetails;
  private final boolean edit;
  private final boolean pollTerminate;
  private final boolean pinMessage;
  private final boolean unpinMessage;
  private final boolean starMessage;
  private final boolean unstarMessage;

  private MenuState(@NonNull Builder builder) {
    forward        = builder.forward;
    reply          = builder.reply;
    details        = builder.details;
    saveAttachment = builder.saveAttachment;
    resend         = builder.resend;
    copy           = builder.copy;
    delete         = builder.delete;
    reactions      = builder.reactions;
    paymentDetails = builder.paymentDetails;
    edit           = builder.edit;
    pollTerminate  = builder.pollTerminate;
    pinMessage     = builder.pinMessage;
    unpinMessage   = builder.unpinMessage;
    starMessage    = builder.starMessage;
    unstarMessage  = builder.unstarMessage;
  }

  public boolean shouldShowForwardAction() {
    return forward;
  }

  public boolean shouldShowReplyAction() {
    return reply;
  }

  public boolean shouldShowDetailsAction() {
    return details;
  }

  public boolean shouldShowSaveAttachmentAction() {
    return saveAttachment;
  }

  public boolean shouldShowResendAction() {
    return resend;
  }

  public boolean shouldShowCopyAction() {
    return copy;
  }

  public boolean shouldShowDeleteAction() {
    return delete;
  }

  public boolean shouldShowReactions() {
    return reactions;
  }

  public boolean shouldShowPaymentDetails() {
    return paymentDetails;
  }

  public boolean shouldShowEditAction() {
    return edit;
  }

  public boolean shouldShowPollTerminateAction() {
    return pollTerminate;
  }

  public boolean shouldShowPinMessage() {
    return pinMessage;
  }

  public boolean showShowUnpinMessage() {
    return unpinMessage;
  }

  public boolean shouldShowStarMessage() {
    return starMessage;
  }

  public boolean shouldShowUnstarMessage() {
    return unstarMessage;
  }

  public static MenuState getMenuState(@NonNull Recipient conversationRecipient,
                                       @NonNull Set<MultiselectPart> selectedParts,
                                       boolean shouldShowMessageRequest,
                                       boolean isNonAdminInAnnouncementGroup,
                                       boolean canEditGroupInfo)
  {
    
    Builder builder          = new Builder();
    boolean actionMessage    = false;
    boolean hasText          = false;
    boolean sharedContact    = false;
    boolean viewOnce         = false;
    boolean remoteDelete     = false;
    boolean hasInMemory      = false;
    boolean hasPendingMedia  = false;
    boolean mediaIsSelected  = false;
    boolean hasGift          = false;
    boolean hasPayment       = false;
    boolean hasPoll          = false;
    boolean hasPollTerminate = false;
    boolean canPinMessage    = false;
    boolean canUnpinMessage  = false;
    boolean canStarMessage   = false;
    boolean canUnstarMessage = false;

    for (MultiselectPart part : selectedParts) {
      MessageRecord messageRecord = part.getMessageRecord();

      if (isActionMessage(messageRecord)) {
        actionMessage = true;
        if (messageRecord.isInMemoryMessageRecord()) {
          hasInMemory = true;
        }
      }

      if (!(part instanceof MultiselectPart.Attachments)) {
        if (messageRecord.getBody().length() > 0) {
          hasText = true;
        }
      } else {
        mediaIsSelected = true;
        if (messageRecord.isMediaPending()) {
          hasPendingMedia = true;
        }
      }

      if (messageRecord.isMms() && !((MmsMessageRecord) messageRecord).getSharedContacts().isEmpty()) {
        sharedContact = true;
      }

      if (messageRecord.isViewOnce()) {
        viewOnce = true;
      }

      if (messageRecord.isRemoteDelete()) {
        remoteDelete = true;
      }

      if (MessageRecordUtil.hasGiftBadge(messageRecord)) {
        hasGift = true;
      }

      if (messageRecord.isPaymentNotification() || messageRecord.isPaymentTombstone()) {
        hasPayment = true;
      }

      if (MessageRecordUtil.hasPoll(messageRecord)) {
        hasPoll = true;
      }

      if (MessageRecordUtil.hasPoll(messageRecord) && !MessageRecordUtil.getPoll(messageRecord).getHasEnded() && messageRecord.isOutgoing()) {
        hasPollTerminate = true;
      }

      if (!messageRecord.isPending() && messageRecord.getPinnedUntil() == 0 && !conversationRecipient.isReleaseNotes() && canEditGroupInfo && !hasGift && !conversationRecipient.isInactiveGroup()) {
        canPinMessage = true;
      }

      if (messageRecord.getPinnedUntil() != 0 && !conversationRecipient.isReleaseNotes() && canEditGroupInfo && !hasGift && !conversationRecipient.isInactiveGroup()) {
        canUnpinMessage = true;
      }

      if (SignalStore.labs().getStarredMessages() && !messageRecord.isUpdate() && !messageRecord.isRemoteDelete() && !messageRecord.isStarred()) {
        canStarMessage = true;
      }

      if (SignalStore.labs().getStarredMessages() && messageRecord.isStarred()) {
        canUnstarMessage = true;
      }
    }

    boolean shouldShowForwardAction = !actionMessage    &&
                                      !viewOnce         &&
                                      !remoteDelete     &&
                                      !hasPendingMedia  &&
                                      !hasGift          &&
                                      !hasPayment       &&
                                      !hasPoll          &&
                                      !hasPollTerminate &&
                                      selectedParts.size() <= MAX_FORWARDABLE_COUNT;

    int uniqueRecords = selectedParts.stream()
                                     .map(MultiselectPart::getMessageRecord)
                                     .collect(Collectors.toSet())
                                     .size();

    if (uniqueRecords > 1) {
      builder.shouldShowForwardAction(shouldShowForwardAction)
             .shouldShowReplyAction(false)
             .shouldShowDetailsAction(false)
             .shouldShowSaveAttachmentAction(false)
             .shouldShowResendAction(false)
             .shouldShowEdit(false)
             .shouldShowPollTerminate(false)
             .shouldShowPinMessage(false)
             .shouldShowUnpinMessage(false)
             .shouldShowStarMessage(false)
             .shouldShowUnstarMessage(false);
    } else {
      MultiselectPart multiSelectRecord = selectedParts.iterator().next();

      MessageRecord messageRecord = multiSelectRecord.getMessageRecord();

      builder.shouldShowResendAction(messageRecord.isFailed())
             .shouldShowSaveAttachmentAction(mediaIsSelected &&
                                             !actionMessage &&
                                             !viewOnce &&
                                             messageRecord.isMms() &&
                                             !hasPendingMedia &&
                                             !hasGift &&
                                             !messageRecord.isMmsNotification() &&
                                             ((MmsMessageRecord)messageRecord).containsMediaSlide() &&
                                             ((MmsMessageRecord)messageRecord).getSlideDeck().getStickerSlide() == null)
             .shouldShowForwardAction(shouldShowForwardAction)
             .shouldShowDetailsAction(!actionMessage && !conversationRecipient.isReleaseNotes())
             .shouldShowReplyAction(canReplyToMessage(conversationRecipient, actionMessage, messageRecord, shouldShowMessageRequest, isNonAdminInAnnouncementGroup));

      builder.shouldShowEdit(!actionMessage &&
                             hasText &&
                             !multiSelectRecord.getConversationMessage().getOriginalMessage().isFailed() &&
                             !hasPoll &&
                             !MessageRecordUtil.hasUndownloadedTextSlide(multiSelectRecord.getConversationMessage().getOriginalMessage()) &&
                             MessageConstraintsUtil.isValidEditMessageSend(multiSelectRecord.getConversationMessage().getOriginalMessage(), System.currentTimeMillis()));
    }

    return builder.shouldShowCopyAction(!actionMessage && !remoteDelete && hasText && !hasGift && !hasPayment && !hasPoll)
                  .shouldShowDeleteAction(!hasInMemory && onlyContainsCompleteMessages(selectedParts))
                  .shouldShowReactions(!conversationRecipient.isReleaseNotes() && !conversationRecipient.isInactiveGroup())
                  .shouldShowPaymentDetails(hasPayment && SignalStore.account().isPrimaryDevice())
                  .shouldShowPollTerminate(hasPollTerminate)
                  .shouldShowPinMessage(canPinMessage)
                  .shouldShowUnpinMessage(canUnpinMessage)
                  .shouldShowStarMessage(canStarMessage)
                  .shouldShowUnstarMessage(canUnstarMessage)
                  .build();
  }

  private static boolean onlyContainsCompleteMessages(@NonNull Set<MultiselectPart> multiselectParts) {
    return multiselectParts.stream()
                           .allMatch(part -> {
                             MultiselectCollection collection = part.getConversationMessage().getMultiselectCollection();
                             return part instanceof MultiselectPart.Update || multiselectParts.containsAll(collection.toSet());
                           });
  }

  public static boolean canReplyToMessage(@NonNull Recipient conversationRecipient,
                                          boolean actionMessage,
                                          @NonNull MessageRecord messageRecord,
                                          boolean isDisplayingMessageRequest,
                                          boolean isNonAdminInAnnouncementGroup)
  {
    return !actionMessage &&
           !isNonAdminInAnnouncementGroup &&
           !messageRecord.isRemoteDelete() &&
           !messageRecord.isPending() &&
           !messageRecord.isFailed() &&
           !isDisplayingMessageRequest &&
           messageRecord.isSecure() &&
           (!conversationRecipient.isGroup() || conversationRecipient.isActiveGroup()) &&
           !messageRecord.getFromRecipient().isBlocked() &&
           !conversationRecipient.isReleaseNotes();
  }

  public static boolean isActionMessage(@NonNull MessageRecord messageRecord) {
    return messageRecord.isInMemoryMessageRecord() || messageRecord.isUpdate();
  }

  private final static class Builder {

    private boolean forward;
    private boolean reply;
    private boolean details;
    private boolean saveAttachment;
    private boolean resend;
    private boolean copy;
    private boolean delete;
    private boolean reactions;
    private boolean paymentDetails;
    private boolean edit;
    private boolean pollTerminate;
    private boolean pinMessage;
    private boolean unpinMessage;
    private boolean starMessage;
    private boolean unstarMessage;

    @NonNull Builder shouldShowForwardAction(boolean forward) {
      this.forward = forward;
      return this;
    }

    @NonNull Builder shouldShowReplyAction(boolean reply) {
      this.reply = reply;
      return this;
    }

    @NonNull Builder shouldShowDetailsAction(boolean details) {
      this.details = details;
      return this;
    }

    @NonNull Builder shouldShowSaveAttachmentAction(boolean saveAttachment) {
      this.saveAttachment = saveAttachment;
      return this;
    }

    @NonNull Builder shouldShowResendAction(boolean resend) {
      this.resend = resend;
      return this;
    }

    @NonNull Builder shouldShowCopyAction(boolean copy) {
      this.copy = copy;
      return this;
    }

    @NonNull Builder shouldShowDeleteAction(boolean delete) {
      this.delete = delete;
      return this;
    }

    @NonNull Builder shouldShowReactions(boolean reactions) {
      this.reactions = reactions;
      return this;
    }

    @NonNull Builder shouldShowPaymentDetails(boolean paymentDetails) {
      this.paymentDetails = paymentDetails;
      return this;
    }

    @NonNull Builder shouldShowEdit(boolean edit) {
      this.edit = edit;
      return this;
    }

    @NonNull Builder shouldShowPollTerminate(boolean pollTerminate) {
      this.pollTerminate = pollTerminate;
      return this;
    }

    @NonNull Builder shouldShowPinMessage(boolean pinMessage) {
      this.pinMessage = pinMessage;
      return this;
    }

    @NonNull Builder shouldShowUnpinMessage(boolean unpinMessage) {
      this.unpinMessage = unpinMessage;
      return this;
    }

    @NonNull Builder shouldShowStarMessage(boolean starMessage) {
      this.starMessage = starMessage;
      return this;
    }

    @NonNull Builder shouldShowUnstarMessage(boolean unstarMessage) {
      this.unstarMessage = unstarMessage;
      return this;
    }

    @NonNull
    MenuState build() {
      return new MenuState(this);
    }
  }
}
