#include "feedback_review_serialize.h"
#include "serialize.h"

#include <maps/libs/geolib/include/conversion.h>
#include <maps/libs/geolib/include/serialization.h>
#include <yandex/maps/wiki/revision/objectrevision.h>
#include <yandex/maps/wiki/revision/revisionsgateway.h>
#include <yandex/maps/wiki/revision/snapshot.h>
#include <yandex/maps/wiki/social/feedback/gateway_ro.h>
#include <maps/wikimap/mapspro/services/social/src/libs/yacare/error.h>
#include <maps/wikimap/mapspro/libs/acl_utils/include/feedback.h>
#include <maps/wikimap/mapspro/libs/social_serv_serialize/include/jsonize_feedback_task.h>

#include <maps/libs/json/include/exception.h>
#include <maps/libs/json/include/value.h>
#include <maps/infra/yacare/include/error.h>

using namespace maps::wiki::social::feedback;

namespace maps::wiki::socialsrv {

namespace {

void writeRegion(json::ObjectBuilder& builder, const RegionData& region)
{
    builder["id"] = std::to_string(region.id);
    builder["revisionId"] = std::to_string(region.id) + ":" + std::to_string(region.commitId);
    builder["categoryId"] = region.categoryId;
    builder["title"] = region.title;
    const auto geomForJson = geolib3::convertMercatorToGeodetic(region.polygon);
    builder["geometry"] = geolib3::geojson(geomForJson);
    const auto bb = geomForJson.boundingBox();
    builder["bounds"] << [&](json::ArrayBuilder builder) {
        builder << bb.minX();
        builder << bb.minY();
        builder << bb.maxX();
        builder << bb.maxY();
    };
}

void
writeBrief(json::ObjectBuilder& builder, const feedback::ReviewBrief& review)
{
    builder["id"] = std::to_string(review.id());
    builder["state"] = toString(review.state());
    builder["createdBy"] = std::to_string(review.createdBy());
    builder["createdAt"] = maps::chrono::formatIsoDateTime(review.createdAt());
    builder["reviewee"] = std::to_string(review.reviewee());
    if (review.state() == ReviewState::Published) {
        builder["reportUrl"] = review.reportData();
        ASSERT(review.publishedAt());
        builder["publishedAt"] = maps::chrono::formatIsoDateTime(*review.publishedAt());
    }
}

void
write(
    json::ObjectBuilder& builder,
    const feedback::Review& review,
    const ReviewExternData& reviewExternData,
    TUid uid)
{
    writeBrief(builder, review);
    builder["comment"] = review.comment();
    builder["region"] << [&](json::ObjectBuilder builder) {
        writeRegion(builder, reviewExternData.region);
    };
    const auto& tasksComments = review.tasksComments();
    builder["items"] << [&](json::ArrayBuilder builder) {
        for (const auto& task : reviewExternData.tasks) {
            const auto& comment = tasksComments.at(task.id());
            builder << [&](json::ObjectBuilder builder) {
                builder["comment"] = comment.comment;
                if (comment.topic) {
                    builder["topic"] = toString(*comment.topic);
                } else {
                    builder["topic"] = json::null;
                }
                builder["task"] << [&](json::ObjectBuilder builder) {
                    serialize::taskForReviewUIToJson(builder, task, uid);
                };
            };
        }
    };
}

} // namespace

std::string toJson(
    const feedback::Review& review,
    const std::string& token,
    TUid uid,
    const ReviewExternData& reviewExternData)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        builder["review"] << [&](json::ObjectBuilder builder) {
            write(builder, review, reviewExternData, uid);
        };
        builder["token"] = token;
    };
    return builder.str();
}

std::string toJson(
    const feedback::Review& review,
    TUid uid,
    const ReviewExternData& reviewExternData)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        write(builder, review, reviewExternData, uid);
    };
    return builder.str();
}

std::string toJson(
    const std::vector<feedback::ReviewBrief>& reviews)
{
    json::Builder builder;
    builder << [&](json::ArrayBuilder builder) {
        for (const auto& reviewBrief : reviews) {
            builder << [&](json::ObjectBuilder builder) {
                writeBrief(builder, reviewBrief);
            };
        };
    };
    return builder.str();
}

} // namespace maps::wiki::socialsrv
