#include <maps/wikimap/mapspro/services/tasks_feedback/src/import_indoor_feedback_worker/lib/to_task.h>

#include <maps/libs/json/include/builder.h>
#include <maps/libs/json/include/value.h>
#include <maps/libs/geolib/include/bounding_box.h>
#include <maps/libs/geolib/include/conversion.h>
#include <maps/libs/geolib/include/polyline.h>
#include <yandex/maps/wiki/geom_tools/object_diff_builder.h>
#include <yandex/maps/wiki/social/feedback/attributes.h>
#include <yandex/maps/wiki/social/feedback/description.h>

#include <maps/libs/common/include/exception.h>

#include <sstream>

namespace maps::wiki::tasks_feedback::indoor_feedback {

namespace sf = social::feedback;
using sf::TaskNew;
using geolib3::Polyline2;
using geolib3::Point2;
using geom_tools::ObjectDiffBuilder;

namespace {

ObjectDiffBuilder<Polyline2> getObjectDiffBuilder(const std::vector<Wall>& walls)
{
    std::vector<Polyline2> after;
    std::vector<Polyline2> before;
    for (const auto& wall : walls) {
        switch (wall.color) {
        case Wall::Color::Red :
            before.emplace_back(wall.points);
            break;
        case Wall::Color::Green :
            after.emplace_back(wall.points);
            break;
        }
    }
    return ObjectDiffBuilder<Polyline2>()
        .setBefore(std::move(before))
        .setAfter(std::move(after));
}

json::Value jsonValue(const ObjectDiffBuilder<Polyline2>& diff) {
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        diff.json(builder);
    };
    return json::Value::fromString(builder.str());
}

sf::Description getWallsTaskDescription(const std::string& indoorLevel)
{
    std::stringstream ss;
    ss << "Indoor толокеры. Этаж: "
       << indoorLevel
       << ". (удалить - красные линии, добавить - бирюзовые линии)";
    return {ss.str()};
}

sf::Description getPoiTaskDescription(
    const std::string& comment,
    const std::string& indoorLevel)
{
    std::stringstream ss;
    ss << "Indoor толокеры. Этаж: "
       << indoorLevel
       << ". Комментарий: " << comment;
    // WARNING (efrolov89): maybe need to escape comment.
    return {ss.str()};
}

Point2 getCenter(const std::vector<Wall>& walls)
{
    REQUIRE(!walls.empty(), "Walls are empty");

    auto wallsIt = walls.begin();
    geolib3::BoundingBox bbox = geolib3::boundingBox(wallsIt->points);
    ++wallsIt;
    for (; wallsIt != walls.end(); ++wallsIt) {
        bbox = expand(bbox, geolib3::boundingBox(wallsIt->points));
    }

    return bbox.center();
}

} // namespace

TaskNew toTask(const Poi& poi)
{
    TaskNew taskNew{
        geolib3::geoPoint2Mercator(poi.point),
        sf::Type::Poi,
        "sprav-pedestrian-indoor-poi",
        getPoiTaskDescription(poi.description, poi.indoorLevel)
    };
    // TODO (efrolov89): maybe need to add indoor level attrs.
    taskNew.indoorLevel = poi.indoorLevel;
    return taskNew;
}

TaskNew toTask(const std::vector<Wall>& walls)
{
    REQUIRE(!walls.empty(), "No walls to create task from");
    const auto indoorLevel = walls.front().indoorLevel;
    TaskNew taskNew{
        geolib3::geoPoint2Mercator(getCenter(walls)),
        sf::Type::Poi,
        "sprav-pedestrian-indoor-walls",
        getWallsTaskDescription(indoorLevel)
    };
    // TODO (efrolov89): maybe need to add indoor level attrs.
    taskNew.indoorLevel = indoorLevel;
    taskNew.attrs.add(
        sf::AttrType::ObjectDiff,
        jsonValue(getObjectDiffBuilder(walls))
    );
    return taskNew;
}

} // namespace maps::wiki::tasks_feedback::indoor_feedback
