#include <maps/wikimap/mapspro/services/mrc/eye/experiments/signs_map/toloka/matches/lib/include/serialization.h>
#include <maps/wikimap/mapspro/services/mrc/eye/experiments/signs_map/toloka/matches/lib/include/strings.h>
#include <maps/wikimap/mapspro/services/mrc/eye/experiments/signs_map/toloka/matches/lib/include/feature.h>

#include <sstream>

namespace maps::mrc::eye {

namespace {

json::Value toJson(const geolib3::Point2& point) {
    return json::Value({
        json::Value(point.x()),
        json::Value(point.y())
    });
}

} // namespace

void toJson(const Object& object, json::ObjectBuilder& builder) {
    builder[FIELD_OBJECT_ID] = object.id;
    builder[FIELD_TYPE] = object.type;
    builder[FIELD_BBOX] = toJson(object.bbox);
}

void toJson(
    const db::Feature& feature, const Objects& objects,
    json::ObjectBuilder& builder)
{
    builder[FIELD_FEATURE_ID] = feature.id();
    builder[FIELD_FEATURE_URL] = makeFeatureUrl(feature.id(), objects.orientation);
    builder[FIELD_HEADING] = feature.heading().value();
    builder[FIELD_POSITION] = toJson(feature.geodeticPos());

    builder[FIELD_OBJECTS] = [&](json::ArrayBuilder b) {
        for (const auto& object : objects.items) {
            b << [&](json::ObjectBuilder b) {
                toJson(object, b);
            };
        }
    };
}

void toJson(const Task& task, json::ObjectBuilder& builder) {
    builder[FIELD_INPUT_VALUES] = [&](json::ObjectBuilder b) {
        b[FIELD_FEATURES] = [&](json::ArrayBuilder b) {
            b << [&](json::ObjectBuilder b) {
                toJson(task.feature1, task.objects1, b);
            };
            b << [&](json::ObjectBuilder b) {
                toJson(task.feature2, task.objects2, b);
            };
        };
    };

    if (!task.matches.has_value()) {
        return;
    }
    builder[FIELD_KNOWN_SOLUTIONS] = [&](json::ArrayBuilder b) {
        b << [&](json::ObjectBuilder b) {
            b[FIELD_WEIGHT] = 1;
            b[FIELD_OUTPUT_VALUES] = [&](json::ObjectBuilder) {
                b[FIELD_STATUS] = VALUE_STATUS_OK;
                b[FIELD_MATCHES] = [&](json::ArrayBuilder b) {
                    for (const auto& match : task.matches.value()) {
                        b << [&](json::ObjectBuilder b) {
                            const auto& [objectId1, objectId2] = match;
                            b[FIELD_OBJECT_ID_1] = objectId1;
                            b[FIELD_OBJECT_ID_2] = objectId2;
                        };
                    }
                };
            };
        };
    };
}


void dumpTasks(const Tasks& tasks, std::ostream& output) {
    json::Builder builder(output);
    builder << [&](json::ArrayBuilder b) {
        for (const auto& task : tasks) {
            b << [&](json::ObjectBuilder b) {
                toJson(task, b);
            };
        }
    };
}

void dumpTasks(const Tasks& tasks, const std::string& path) {
    std::ofstream ofs(path);
    REQUIRE(ofs.is_open(), "Failed to open file: " + path);

    json::Builder builder(ofs);

    dumpTasks(tasks, ofs);

    ofs.close();
}

} // namespace maps::mrc::eye
