#include <maps/wikimap/mapspro/libs/social_serv_serialize/include/task_for_ui.h>

#include "task_data_substitution.h"

#include <maps/libs/common/include/exception.h>
#include <yandex/maps/wiki/common/robot.h>
#include <yandex/maps/wiki/social/feedback/agent.h>
#include <yandex/maps/wiki/social/feedback/consts.h>

#include <algorithm>

namespace sf = maps::wiki::social::feedback;

namespace maps::wiki::socialsrv::serialize {

namespace {

bool isHiddenFromUI(const sf::HistoryItem& item)
{
    static const sf::TaskOperations operationsForUi{
        sf::TaskOperation::Reveal,
        sf::TaskOperation::Open,
        sf::TaskOperation::NeedInfo,
        sf::TaskOperation::Accept,
        sf::TaskOperation::Reject,
        sf::TaskOperation::Deploy,
        sf::TaskOperation::Comment,
        sf::TaskOperation::Hide,
        sf::TaskOperation::Show,
        sf::TaskOperation::ChangeType,
        sf::TaskOperation::ProcessingLevelUp,
        sf::TaskOperation::ProcessingLevelDown,
        sf::TaskOperation::ChangeProcessingLvl,
        sf::TaskOperation::ChangePosition,
    };

    if (item.operation() == sf::TaskOperation::Create) {
        return common::isRobot(item.modifiedBy());
    }
    return !operationsForUi.contains(item.operation());
}

sf::HistoryItems getHistoryItemsForUI(sf::HistoryItems items)
{
    items.erase(
        std::remove_if(items.begin(), items.end(), isHiddenFromUI),
        items.end());
    return items;
}

HistoryEventsUI makeHistoryEventsUI(
    const sf::HistoryItems& historyItems, const social::Comments& comments)
{
    std::map<social::TId, social::Comment> commentsMap;
    for (const auto& comment: comments) {
        commentsMap.emplace(comment.id(), comment);
    }

    HistoryEventsUI eventsUI;
    for (const auto& item: historyItems) {
        auto& event = eventsUI.emplace_back(item);
        if (item.commentId()) {
            auto commentIt = commentsMap.find(item.commentId().value());
            if (commentIt != commentsMap.end()) {
                event.comment = commentIt->second;
            }
        }
    }

    return eventsUI;
}

} // namespace

bool isJustLeveledUpByUser(const sf::History& history, social::TUid uid)
{
    const auto& items = history.items();
    if (items.empty()) {
        return false;
    }

    auto it = items.rbegin();
    if (it->modifiedBy() == uid) {
        if (internal::isProcessingLevelUp(*it)) {
            return true;
        } else if (it->operation() == sf::TaskOperation::Release) {
            ++it;
            return it != items.rend() &&
                it->modifiedBy() == uid &&
                internal::isProcessingLevelUp(*it);
        }
    }
    return false;
}

TaskForUI::TaskForUI(
    sf::Task task,
    sf::History history,
    SubstitutionStrategy substitutionStrategy,
    social::Comments comments,
    sf::TaskOperations validOperations,
    social::TUid uid)
    : Task(std::move(task))
    , history_(std::move(history))
    , comments_(std::move(comments))
    , validOperations_(std::move(validOperations))
    , validRejectReasons_(sf::Agent::validRejectReasons(*this))
{
    if (substitutionStrategy == SubstitutionStrategy::ForZeroProcessingLine) {
        substitutionData_ =
            getSubstitutionData(
                history_.items(),
                Task::resolved(),
                Task::state());

        if (Task::processingLevel() > sf::PROCESSING_LEVEL_MIN) {
            validOperations_.clear();
            if (isJustLeveledUpByUser(history_, uid)) {
                validOperations_.insert(sf::TaskOperation::Open);
            }
        }

        validRejectReasons_.clear();
    }
}

const std::optional<sf::TaskResolved>& TaskForUI::resolved() const
{
    if (substitutionData_) {
        return substitutionData_->taskResolved;
    } else {
        return Task::resolved();
    }
}


sf::TaskState TaskForUI::state() const
{
    if (substitutionData_) {
        return substitutionData_->taskState;
    } else {
        return Task::state();
    }
}

int TaskForUI::processingLevel() const
{
    return substitutionData_
        ? substitutionData_->taskProcessingLevel
        : Task::processingLevel();
}

const sf::TaskOperations& TaskForUI::validOperations() const
{
    return validOperations_;
}

const social::feedback::RejectReasonsSet& TaskForUI::validRejectReasons() const
{
    return validRejectReasons_;
}

HistoryEventsUI TaskForUI::historyEventsUI() const
{
    auto itemsToShow = getHistoryItemsForUI(
        substitutionData_
            ? substitutionData_->historyItems
            : history_.items());

    return makeHistoryEventsUI(std::move(itemsToShow), comments_);
}

const std::optional<social::TId>& TaskForUI::nearbyObjectId() const
{
    return nearbyObjectId_;
}

void TaskForUI::setNearbyObjectId(std::optional<social::TId> objectId)
{
    nearbyObjectId_ = std::move(objectId);
}

} // namespace maps::wiki::socialsrv::serialize
