//
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2026
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include "td/telegram/MessageInputReplyTo.h"

#include "td/telegram/AccessRights.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/DialogId.h"
#include "td/telegram/DialogManager.h"
#include "td/telegram/InputDialogId.h"
#include "td/telegram/MessageTopic.h"
#include "td/telegram/ServerMessageId.h"
#include "td/telegram/StoryId.h"
#include "td/telegram/Td.h"
#include "td/telegram/telegram_api.h"

#include "td/utils/buffer.h"
#include "td/utils/logging.h"
#include "td/utils/utf8.h"

namespace td {

MessageInputReplyTo::~MessageInputReplyTo() = default;

// only for draft messages
MessageInputReplyTo::MessageInputReplyTo(Td *td,
                                         telegram_api::object_ptr<telegram_api::InputReplyTo> &&input_reply_to) {
  if (input_reply_to == nullptr) {
    return;
  }
  switch (input_reply_to->get_id()) {
    case telegram_api::inputReplyToStory::ID: {
      auto reply_to = telegram_api::move_object_as<telegram_api::inputReplyToStory>(input_reply_to);
      auto dialog_id = InputDialogId(reply_to->peer_).get_dialog_id();
      auto story_id = StoryId(reply_to->story_id_);
      if (dialog_id.is_valid() && story_id.is_valid()) {
        td->dialog_manager_->force_create_dialog(dialog_id, "MessageInputReplyTo", true);
        story_full_id_ = {dialog_id, story_id};
      }
      break;
    }
    case telegram_api::inputReplyToMessage::ID: {
      auto reply_to = telegram_api::move_object_as<telegram_api::inputReplyToMessage>(input_reply_to);
      MessageId message_id(ServerMessageId(reply_to->reply_to_msg_id_));
      if (!message_id.is_valid() && !message_id_.is_valid_scheduled()) {
        return;
      }
      DialogId dialog_id;
      if (reply_to->reply_to_peer_id_ != nullptr) {
        dialog_id = InputDialogId(reply_to->reply_to_peer_id_).get_dialog_id();
        if (!dialog_id.is_valid() || !td->dialog_manager_->have_input_peer(dialog_id, false, AccessRights::Read)) {
          return;
        }
        td->dialog_manager_->force_create_dialog(dialog_id, "inputReplyToMessage");
      }
      message_id_ = message_id;
      dialog_id_ = dialog_id;
      debug_source_ = "inputReplyToMessage";

      quote_ = MessageQuote(td, reply_to);
      todo_item_id_ = reply_to->todo_item_id_;
      poll_option_id_ = reply_to->poll_option_.as_slice().str();
      if (!check_utf8(poll_option_id_)) {
        poll_option_id_.clear();
      }
      break;
    }
    case telegram_api::inputReplyToMonoForum::ID: {
      // auto reply_to = telegram_api::move_object_as<telegram_api::inputReplyToMonoForum>(input_reply_to);
      break;
    }
    case telegram_api::inputReplyToEphemeralMessage::ID: {
      // auto reply_to = telegram_api::move_object_as<telegram_api::inputReplyToEphemeralMessage>(input_reply_to);
      break;
    }
    default:
      UNREACHABLE();
  }
}

MessageInputReplyTo MessageInputReplyTo::regular(MessageId message_id) {
  MessageInputReplyTo result;
  result.message_id_ = message_id;
  result.debug_source_ = "regular";
  return result;
}

void MessageInputReplyTo::add_dependencies(Dependencies &dependencies) const {
  dependencies.add_dialog_and_dependencies(dialog_id_);
  quote_.add_dependencies(dependencies);
  dependencies.add_dialog_and_dependencies(story_full_id_.get_dialog_id());  // just in case
}

telegram_api::object_ptr<telegram_api::InputReplyTo> MessageInputReplyTo::get_input_reply_to(
    Td *td, const MessageTopic &message_topic, bool for_draft, DialogId for_dialog_id, int32 with_flags) const {
  if (story_full_id_.is_valid()) {
    CHECK(message_topic.is_empty());
    if (for_draft) {
      return nullptr;
    }
    auto dialog_id = story_full_id_.get_dialog_id();
    auto input_peer = td->dialog_manager_->get_input_peer(dialog_id, AccessRights::Read);
    if (input_peer == nullptr) {
      LOG(INFO) << "Failed to get input peer for " << story_full_id_;
      return nullptr;
    }
    return telegram_api::make_object<telegram_api::inputReplyToStory>(std::move(input_peer),
                                                                      story_full_id_.get_story_id().get());
  }
  CHECK(!message_topic.is_saved_messages());
  if (ephemeral_message_id_.is_valid()) {
    return telegram_api::make_object<telegram_api::inputReplyToEphemeralMessage>(ephemeral_message_id_.get());
  }
  auto reply_to_message_id = message_id_;
  if (reply_to_message_id == MessageId()) {
    if (message_topic.is_monoforum()) {
      auto saved_input_peer = message_topic.get_saved_input_peer(td);
      if (saved_input_peer != nullptr) {
        return telegram_api::make_object<telegram_api::inputReplyToMonoForum>(std::move(saved_input_peer));
      }
      return nullptr;
    }
    if (message_topic.is_empty()) {
      return nullptr;
    }
    if (!for_draft) {
      reply_to_message_id = message_topic.get_implicit_reply_to_message_id(td);
    }
  }
  int32 flags = 0;
  auto top_msg_id = message_topic.get_input_top_msg_id();
  if (top_msg_id != 0) {
    flags |= telegram_api::inputReplyToMessage::TOP_MSG_ID_MASK;
  }
  auto saved_input_peer = message_topic.get_saved_input_peer(td);
  if (saved_input_peer != nullptr) {
    flags |= telegram_api::inputReplyToMessage::MONOFORUM_PEER_ID_MASK;
  }
  telegram_api::object_ptr<telegram_api::InputPeer> input_peer;
  if (dialog_id_ != DialogId()) {
    input_peer = td->dialog_manager_->get_input_peer(dialog_id_, AccessRights::Read);
    if (input_peer == nullptr) {
      LOG(INFO) << "Failed to get input peer for " << dialog_id_;
      return nullptr;
    }
    flags |= telegram_api::inputReplyToMessage::REPLY_TO_PEER_ID_MASK;
  }
  if (todo_item_id_ != 0) {
    flags |= telegram_api::inputReplyToMessage::TODO_ITEM_ID_MASK;
  }
  if (!poll_option_id_.empty()) {
    flags |= telegram_api::inputReplyToMessage::POLL_OPTION_MASK;
  }
  if (reply_to_message_id != MessageId() && !reply_to_message_id.is_server()) {
    LOG(FATAL) << *this << " in " << message_topic << " in " << for_dialog_id << " for "
               << (for_draft ? "draft" : "message") << " with flags " << with_flags << " from "
               << (debug_source_ == nullptr ? "null" : debug_source_);
  }
  auto result = telegram_api::make_object<telegram_api::inputReplyToMessage>(
      flags, reply_to_message_id.get_server_message_id().get(), top_msg_id, std::move(input_peer), string(), Auto(), 0,
      std::move(saved_input_peer), todo_item_id_, BufferSlice(poll_option_id_));
  quote_.update_input_reply_to_message(td, result.get());
  return std::move(result);
}

// only for draft messages
td_api::object_ptr<td_api::InputMessageReplyTo> MessageInputReplyTo::get_input_message_reply_to_object(Td *td) const {
  if (story_full_id_.is_valid()) {
    return td_api::make_object<td_api::inputMessageReplyToStory>(
        td->dialog_manager_->get_chat_id_object(story_full_id_.get_dialog_id(), "inputMessageReplyToStory"),
        story_full_id_.get_story_id().get());
  }
  if (!message_id_.is_valid() && !message_id_.is_valid_scheduled()) {
    return nullptr;
  }
  if (dialog_id_ != DialogId()) {
    return td_api::make_object<td_api::inputMessageReplyToExternalMessage>(
        td->dialog_manager_->get_chat_id_object(dialog_id_, "inputMessageReplyToExternalMessage"), message_id_.get(),
        quote_.get_input_text_quote_object(td->user_manager_.get()), todo_item_id_, poll_option_id_);
  }
  return td_api::make_object<td_api::inputMessageReplyToMessage>(
      message_id_.get(), quote_.get_input_text_quote_object(td->user_manager_.get()), todo_item_id_, poll_option_id_);
}

MessageId MessageInputReplyTo::get_same_chat_reply_to_message_id() const {
  return dialog_id_ == DialogId() && (message_id_.is_valid() || message_id_.is_valid_scheduled()) ? message_id_
                                                                                                  : MessageId();
}

MessageFullId MessageInputReplyTo::get_reply_message_full_id(DialogId owner_dialog_id) const {
  if (!message_id_.is_valid() && !message_id_.is_valid_scheduled()) {
    return {};
  }
  return {dialog_id_ != DialogId() ? dialog_id_ : owner_dialog_id, message_id_};
}

bool operator==(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) {
  return lhs.message_id_ == rhs.message_id_ && lhs.ephemeral_message_id_ == rhs.ephemeral_message_id_ &&
         lhs.dialog_id_ == rhs.dialog_id_ && lhs.story_full_id_ == rhs.story_full_id_ && lhs.quote_ == rhs.quote_ &&
         lhs.todo_item_id_ == rhs.todo_item_id_ && lhs.poll_option_id_ == rhs.poll_option_id_;
}

bool operator!=(const MessageInputReplyTo &lhs, const MessageInputReplyTo &rhs) {
  return !(lhs == rhs);
}

StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReplyTo &input_reply_to) {
  if (input_reply_to.ephemeral_message_id_.is_valid()) {
    return string_builder << input_reply_to.ephemeral_message_id_;
  }
  if (input_reply_to.message_id_.is_valid() || input_reply_to.message_id_.is_valid_scheduled()) {
    string_builder << input_reply_to.message_id_;
    if (input_reply_to.dialog_id_ != DialogId()) {
      string_builder << " in " << input_reply_to.dialog_id_;
    }
    if (input_reply_to.todo_item_id_ != 0) {
      string_builder << " to task " << input_reply_to.todo_item_id_;
    }
    if (!input_reply_to.poll_option_id_.empty()) {
      string_builder << " to poll option " << input_reply_to.poll_option_id_;
    }
    if (!input_reply_to.quote_.is_empty()) {
      string_builder << input_reply_to.quote_;
    }
    return string_builder;
  }
  if (input_reply_to.story_full_id_.is_valid()) {
    return string_builder << input_reply_to.story_full_id_;
  }
  return string_builder << "nothing";
}

StringBuilder &operator<<(StringBuilder &string_builder, const MessageInputReplyTo *input_reply_to_ptr) {
  if (input_reply_to_ptr == nullptr) {
    return string_builder << "nothing";
  }
  return string_builder << *input_reply_to_ptr;
}

}  // namespace td
