#include <maps/wikimap/mapspro/services/social/src/libs/feedback/serialize.h>
#include <maps/wikimap/mapspro/services/social/src/libs/feedback/common.h>

#include <maps/libs/geolib/include/bounding_box.h>
#include <maps/libs/geolib/include/serialization.h>
#include <yandex/maps/wiki/common/json_helpers.h>
#include <maps/libs/json/include/builder.h>
#include <maps/libs/json/include/std/set.h>
#include <maps/libs/json/include/std/vector.h>
#include <maps/wikimap/mapspro/libs/social_serv_serialize/include/jsonize_aggregated_counter.h>
#include <maps/wikimap/mapspro/libs/social_serv_serialize/include/jsonize_feedback_task.h>

using boost::lexical_cast;

namespace maps::wiki::social::feedback {

void json(
    const Preset& preset,
    json::ObjectBuilder builder)
{
    builder[socialsrv::jsids::ID] = std::to_string(preset.id);
    builder[socialsrv::jsids::NAME] = preset.name;
    if (preset.entries.types) {
        builder[socialsrv::jsids::TYPES] << [&](json::ArrayBuilder arrBuilder) {
            socialsrv::collectionToJsonArray(arrBuilder, preset.entries.types.value());
        };
    }
    if (preset.entries.workflows) {
        builder[socialsrv::jsids::WORKFLOWS] << [&](json::ArrayBuilder arrBuilder) {
            socialsrv::collectionToJsonArray(arrBuilder, preset.entries.workflows.value());
        };
    }
    if (preset.entries.sources) {
        builder[socialsrv::jsids::SOURCES] << [&](json::ArrayBuilder arrBuilder) {
            socialsrv::collectionToJsonArray(arrBuilder, preset.entries.sources.value());
        };
    }
    if (preset.entries.ageTypes) {
        builder[socialsrv::jsids::AGE_TYPES] << [&](json::ArrayBuilder arrBuilder) {
            socialsrv::collectionToJsonArray(arrBuilder, preset.entries.ageTypes.value());
        };
    }
    if (preset.entries.hidden) {
        builder[socialsrv::jsids::HIDDEN] = preset.entries.hidden.value();
    }
    if (preset.entries.status) {
        builder[socialsrv::jsids::STATUS] = toString(preset.entries.status.value());
    }
}

void json(
    const AoiTaskCounters& counters,
    json::ObjectBuilder builder)
{
    builder["old"] = counters.old;
    builder["total"] = counters.total;
}

void json(
    const AoiTaskStatCounters& counters,
    json::ObjectBuilder builder)
{
    builder["old"] = counters.old;
    builder["oldHidden"] = counters.oldHidden;
    builder["total"] = counters.total;
    builder["totalHidden"] = counters.totalHidden;
}

} // namespace maps::wiki::social::feedback

namespace maps::wiki::socialsrv {

namespace sf = social::feedback;

void json(
    const AoiRegion& region,
    json::ObjectBuilder builder)
{
    builder["id"] = common::idToJson(region.aoiId);
    builder["categoryId"] = "aoi";
    builder["title"] = region.title;
}

void jsonize(json::ArrayBuilder& arrayBuilder,
    const sf::TypeCatToTypes& typeCatToTypes)
{
    for (const auto& typeCatAndTypes : typeCatToTypes) {
        arrayBuilder << [&](json::ObjectBuilder builder) {
            builder[jsids::CATEGORY] << lexical_cast<std::string>(typeCatAndTypes.first);
            builder[jsids::TYPES] << [&](json::ArrayBuilder builder) {
                collectionToJsonArray(builder, typeCatAndTypes.second);
            };
        };
    }
}

void jsonize(json::ArrayBuilder& arrayBuilder,
    const AoiRegionToTaskCounters& regionsWithCounters)
{
    for (const auto& pair : regionsWithCounters) {
        const auto& region = pair.first;
        const auto& counters = pair.second;
        arrayBuilder << [&](json::ObjectBuilder builder) {
            builder["geoObject"] << region;
            builder["taskCounters"] << counters;
        };
    }
}

void jsonize(
    json::ObjectBuilder& builder,
    const RegionStatRow& regionStatRow,
    UserId uid)
{
    builder["geoObject"] << regionStatRow.region;
    builder["taskCounters"] << regionStatRow.counters;

    if (regionStatRow.oldestTask) {
        builder["oldestTask"] << [&](json::ObjectBuilder builder) {
            serialize::taskBriefToJson(builder,
                regionStatRow.oldestTask.value(), uid);
        };
    }
}

std::string toJson(const AoiRegionToTaskCounters& regionsToCounters)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder objectBuilder) {
        objectBuilder["regions"] << [&](json::ArrayBuilder arrayBuilder) {
            jsonize(arrayBuilder, regionsToCounters);
        };
    };
    return builder.str();
}

std::string toJson(const std::vector<social::feedback::Type>& types)
{
    json::Builder builder;
    builder << [&](json::ArrayBuilder arrBuilder) {
        collectionToJsonArray(arrBuilder, types);
    };
    return builder.str();
}

std::string toJson(const std::set<std::string>& sources)
{
    json::Builder builder;
    builder << [&](json::ArrayBuilder arrBuilder) {
        json::json(sources, arrBuilder);
    };
    return builder.str();
}

std::string toJson(const RegionsStatRows& regionsStatRows, UserId uid)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder objectBuilder) {
        objectBuilder["regions"] << [&](json::ArrayBuilder arrayBuilder) {
            for (const auto& pair : regionsStatRows) {
                const auto& regionStatRow = pair.second;
                arrayBuilder << [&](json::ObjectBuilder builder) {
                    jsonize(builder, regionStatRow, uid);
                };
            }
        };
    };
    return builder.str();
}

std::string toJson(const sf::Preset& preset, const std::string& token)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        builder[jsids::TOKEN] = token;
        builder[jsids::FEEDBACK_PRESET] << preset;
    };
    return builder.str();
}

std::string toJson(const sf::Presets& presets)
{
    json::Builder builder;
    builder << [&](json::ArrayBuilder arrBuilder) {
        json::json(presets, arrBuilder);
    };
    return builder.str();
}

std::string toJson(const std::string& token)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        builder[jsids::TOKEN] = token;
    };
    return builder.str();
}

std::string toJson(const TaskFeedForUI& feed, UserId uid)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder objectBuilder) {
        if (feed.totalCount) {
            objectBuilder[jsids::TOTAL_COUNT] = feed.totalCount.value();
        }

        objectBuilder[jsids::TASKS] << [&](json::ArrayBuilder arrBuilder) {
            for (const auto& task: feed.tasks) {
                arrBuilder << [&](json::ObjectBuilder builder) {
                    taskToJson(builder, task, uid);
                };
            }
        };
        objectBuilder[jsids::HAS_MORE] = feed.hasMore == social::HasMore::Yes;
    };
    return builder.str();
}

std::string toJson(
    const serialize::TaskExtended& taskExtended,
    const std::string& token,
    UserId uid)
{
    json::Builder builder;

    builder << [&](json::ObjectBuilder builder) {
        builder[jsids::TOKEN] = token;
        builder[jsids::FEEDBACK_TASK] << [&](json::ObjectBuilder builder) {
            serialize::taskToJson(builder, taskExtended, uid);
        };
    };
    return builder.str();
}

std::string toJson(
    const serialize::TaskExtended& taskExtended,
    UserId uid)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        serialize::taskToJson(builder, taskExtended, uid);
    };
    return builder.str();
}

std::string toJson(const PaidOperationCounters& paidOperationCounters)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        builder[jsids::PAID_OPERATION_COUNTERS] << [&](json::ObjectBuilder builder) {
            builder[jsids::TODAY] << paidOperationCounters.today;
        };
    };
    return builder.str();
}

std::string toJson(
    const std::vector<social::feedback::AggregatedCounter>& counters)
{
    json::Builder builder;
    builder << [&](json::ObjectBuilder builder) {
        serialize::jsonize(builder, counters);
    };
    return builder.str();
}

std::string toJson(const AssignedPresetsResponse& assignedPresetsResponse)
{
    std::map<social::TId, std::vector<AssignedPresetWithCounter>> assignedPresetsGrouped;
    for (const auto& assignedPreset : assignedPresetsResponse.assignedPresets()) {
        assignedPresetsGrouped[assignedPreset.presetId].emplace_back(assignedPreset);
    }

    json::Builder builder;
    builder << [&](json::ArrayBuilder builder) {
        for (const auto& presetData : assignedPresetsGrouped) {
            builder << [&](json::ObjectBuilder builder) {
                builder["preset"] << assignedPresetsResponse.presets().at(presetData.first);
                builder["regions"] << [&](json::ArrayBuilder builder) {
                    for (const auto& withCounter : presetData.second) {
                        builder << [&](json::ObjectBuilder builder) {
                            builder["aoi"] << [&](json::ObjectBuilder builder) {
                                auto& aoi = assignedPresetsResponse.aois().at(withCounter.aoiId);
                                builder["id"] << common::idToJson(aoi.aoiId);
                                builder["title"] << aoi.title;
                            };
                            builder["totalCount"] = withCounter.totalOpened;
                        };
                    }
                };
            };
        }
    };
    return builder.str();
}

} // maps::wiki::socialsrv
