//
// 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/MessageReactor.h"

#include "td/telegram/ChatManager.h"
#include "td/telegram/Dependencies.h"
#include "td/telegram/DialogManager.h"
#include "td/telegram/MessageSender.h"
#include "td/telegram/StarManager.h"
#include "td/telegram/Td.h"

#include "td/utils/algorithm.h"
#include "td/utils/logging.h"

#include <algorithm>
#include <limits>

namespace td {

void MessageReactor::store_min_channel(Td *td) {
  if (dialog_id_.get_type() == DialogType::Channel && !td->dialog_manager_->have_dialog_info(dialog_id_)) {
    auto channel_id = dialog_id_.get_channel_id();
    auto min_channel = td->chat_manager_->get_min_channel(channel_id);
    if (min_channel == nullptr) {
      LOG(ERROR) << "Receive unknown reacted " << channel_id;
    } else {
      min_channel_ = make_unique<MinChannel>(*min_channel);
    }
  }
}

MessageReactor::MessageReactor(Td *td, telegram_api::object_ptr<telegram_api::messageReactor> &&reactor)
    : dialog_id_(reactor->peer_id_ == nullptr ? DialogId() : DialogId(reactor->peer_id_))
    , count_(reactor->count_)
    , is_top_(reactor->top_)
    , is_me_(reactor->my_)
    , is_anonymous_(reactor->anonymous_) {
  store_min_channel(td);
}

MessageReactor::MessageReactor(Td *td, telegram_api::object_ptr<telegram_api::groupCallDonor> &&donor)
    : dialog_id_(donor->peer_id_ == nullptr ? DialogId() : DialogId(donor->peer_id_))
    , count_(static_cast<int32>(
          td::min(static_cast<int64>(std::numeric_limits<int32>::max()), StarManager::get_star_count(donor->stars_))))
    , is_top_(donor->top_)
    , is_me_(donor->my_)
    , is_anonymous_() {
  store_min_channel(td);
}

td_api::object_ptr<td_api::paidReactor> MessageReactor::get_paid_reactor_object(Td *td) const {
  return td_api::make_object<td_api::paidReactor>(
      dialog_id_ == DialogId() ? nullptr : get_message_sender_object(td, dialog_id_, "paidReactor"), count_, is_top_,
      is_me_, is_anonymous_);
}

void MessageReactor::add_min_channel(Td *td) const {
  if (min_channel_ != nullptr && dialog_id_.get_type() == DialogType::Channel) {
    td->chat_manager_->add_min_channel(dialog_id_.get_channel_id(), *min_channel_);
  }
}

void MessageReactor::add_dependencies(Dependencies &dependencies) const {
  dependencies.add_message_sender_dependencies(dialog_id_);
}

PaidReactionType MessageReactor::get_paid_reaction_type(DialogId my_dialog_id) const {
  if (is_anonymous_ || !dialog_id_.is_valid()) {
    return PaidReactionType::legacy(true);
  }
  if (dialog_id_ == my_dialog_id) {
    return PaidReactionType::legacy(false);
  }
  return PaidReactionType::dialog(dialog_id_);
}

bool MessageReactor::fix_is_me(DialogId my_dialog_id) {
  if (dialog_id_ == my_dialog_id) {
    is_me_ = true;
    return true;
  }
  return false;
}

void MessageReactor::fix_message_reactors(vector<MessageReactor> &reactors, bool need_warning, bool for_group_call) {
  size_t TOP_REACTOR_COUNT = for_group_call ? 100u : 3u;
  if (reactors.size() > TOP_REACTOR_COUNT + 1) {
    LOG(ERROR) << "Have too many " << reactors;
    reactors.resize(TOP_REACTOR_COUNT + 1);
  }
  if (reactors.size() > TOP_REACTOR_COUNT && !reactors[TOP_REACTOR_COUNT].is_me()) {
    LOG(ERROR) << "Receive unexpected " << reactors;
    reactors.resize(TOP_REACTOR_COUNT);
  }
  if (need_warning) {
    for (size_t i = 0; i < reactors.size(); i++) {
      if (reactors[i].is_top_ != (i < TOP_REACTOR_COUNT)) {
        LOG(ERROR) << "Receive incorrect top " << reactors;
        break;
      }
    }
    for (size_t i = 0; i + 1 < reactors.size(); i++) {
      if (reactors[i].count_ < reactors[i + 1].count_) {
        LOG(ERROR) << "Receive unordered " << reactors;
        break;
      }
    }
  }
  bool was_me = false;
  td::remove_if(reactors, [&was_me](const MessageReactor &reactor) {
    if (!reactor.is_valid()) {
      return true;
    }
    if (reactor.is_me()) {
      if (was_me) {
        LOG(ERROR) << "Receive self multiple times";
        return true;
      }
      was_me = true;
    }
    return false;
  });
  std::sort(reactors.begin(), reactors.end());
  if (reactors.size() > TOP_REACTOR_COUNT && !reactors[TOP_REACTOR_COUNT].is_me()) {
    reactors.resize(TOP_REACTOR_COUNT);
  }
  for (size_t i = 0; i < reactors.size(); i++) {
    reactors[i].is_top_ = (i < TOP_REACTOR_COUNT);
  }
}

bool operator<(const MessageReactor &lhs, const MessageReactor &rhs) {
  if (lhs.count_ != rhs.count_) {
    return lhs.count_ > rhs.count_;
  }
  return lhs.dialog_id_.get() < rhs.dialog_id_.get();
}

bool operator==(const MessageReactor &lhs, const MessageReactor &rhs) {
  return lhs.dialog_id_ == rhs.dialog_id_ && lhs.count_ == rhs.count_ && lhs.is_top_ == rhs.is_top_ &&
         lhs.is_me_ == rhs.is_me_ && lhs.is_anonymous_ == rhs.is_anonymous_;
}

StringBuilder &operator<<(StringBuilder &string_builder, const MessageReactor &reactor) {
  return string_builder << "PaidReactor[" << reactor.dialog_id_ << " - " << reactor.count_
                        << (reactor.is_me_ ? " by me" : "") << ']';
}

}  // namespace td
