#include <maps/wikimap/mapspro/services/tasks_export/src/export_worker/client/garden_poster.h>

#include <maps/libs/log8/include/log8.h>

namespace maps::wiki::exporter {

GardenPoster::GardenPoster(std::string gardenContour, std::string gardenOauth)
    : contour_(std::move(gardenContour))
    , oauthToken_(std::move(gardenOauth))
{
    ASSERT(!contour_.empty());
    ASSERT(!oauthToken_.empty());
}

void GardenPoster::postToGarden(const Dataset& dataset, const std::string& nmapsBranchId)
{
    auto createBuildResponse = createBuild(dataset, nmapsBranchId);
    auto buildVersion = createBuildResponse["version"].as<std::string>();
    static const char prefix[] = "build_id:";
    constexpr int skipCount = sizeof(prefix)/sizeof(char) - 1;
    auto build = buildVersion.substr(skipCount);
    waitUntilBuildCreated(build);
    startBuild(buildVersion);
}

std::string GardenPoster::generateCreateBuildJson(
    const Dataset& dataset, const std::string& nmapsBranchId)
{
    // https://docs.yandex-team.ru/garden/experiments#scenarij:-zapusk-src-modulya
    // https://docs.yandex-team.ru/garden/playground_usage#sozdanie-ishodnyh-dannyh
    using namespace maps::json;
    using Clock = std::chrono::system_clock;
    Builder builder;
    builder << [&](ObjectBuilder builder) {
        builder["contour"] = contour_;
        builder["foreign_key"] = [&](ObjectBuilder builder) {
            builder["region"] = dataset.region();
            builder["dataset_id"] = dataset.id();
            builder["timestamp"] = Clock::now().time_since_epoch().count();
        };
        builder["resources"] = [&](ArrayBuilder builder) {
            builder << [&](ObjectBuilder builder) {
                builder["name"] = "src_url_" + dataset.region() + "_yandex";
                builder["version"] = [&](ObjectBuilder builder) {
                    builder["properties"] = [&](ObjectBuilder builder) {
                        builder["file_list"] = [&](ArrayBuilder builder) {
                            for (const auto& link : dataset.fileLinks()) {
                                builder << [&](ObjectBuilder builder) {
                                    builder["name"] = link.name();
                                    builder["url"] = link.readingUrl();
                                };
                            }
                        };
                        builder["region"] = dataset.region();
                        builder["shipping_date"] = dataset.id();
                        builder["vendor"] = "yandex";
                        builder["nmaps_branch_id"] = nmapsBranchId;
                    };
                };
            };
        };
    };
    DEBUG() << "Build creation request body: " << builder.str();
    return builder.str();
}

std::string GardenPoster::generateStartBuildJson(std::string_view buildVersion)
{
    using namespace maps::json;
    Builder builder;
    builder << [=](ObjectBuilder builder) {
        builder["contour"] = contour_;
        builder["sources"] = [=](ArrayBuilder builder) {
            builder << [=](ObjectBuilder builder) {
                builder["name"] = "ymapsdf_src";
                builder["version"] = buildVersion;
            };

            for (auto&& moduleName : {
                    "altay",
                    "extra_poi_bundle",
                    "flats_bundle",
                    "merge_masstransit",
                    "yellow_zones_bundle",
                }
            ) {
                builder << [=](ObjectBuilder builder) {
                    builder["name"] = moduleName;
                    builder["version"] = getModuleLastBuildVersion(moduleName);
                };
            }
        };
    };
    DEBUG() << "Build start request body: " << builder.str();
    return builder.str();
}

std::string GardenPoster::getModuleLastBuildVersion(const std::string& moduleName)
{
    maps::http::Request request(http_, maps::http::GET,
        "http://core-garden-server.maps.yandex.net/modules/" + moduleName + "/builds/");
    request.addHeader("Authorization", "OAuth " + oauthToken_);
    auto response = request.perform();
    REQUIRE(response.status() < 400,
        "Failed to get " + moduleName + " builds in Garden. Response status "
            << response.status() << "\nResponse body: " << response.readBody());
    auto responseJson = maps::json::Value::fromString(response.readBody());
    return responseJson[0]["version"].as<std::string>();
}

json::Value GardenPoster::createBuild(const Dataset& dataset, const std::string& nmapsBranchId)
{
    INFO() << "Sending request to create build in Garden...";
    maps::http::Request request(http_, maps::http::POST,
        "http://core-garden-server.maps.yandex.net/modules/ymapsdf_src/builds/");
    request.addHeader("Authorization", "OAuth " + oauthToken_);
    request.addHeader("Content-Type", "application/json");
    request.setContent(generateCreateBuildJson(dataset, nmapsBranchId));
    auto response = request.perform();
    REQUIRE(response.status() < 400,
        "Failed to create build in Garden. Response status " << response.status()
            << "\nResponse body: " << response.readBody());
    return maps::json::Value::fromString(response.readBody());
}

void GardenPoster::waitUntilBuildCreated(const std::string& buildId)
{
    while (true) {
        maps::http::Request request(http_, maps::http::GET,
            "http://core-garden-server.maps.yandex.net/modules/ymapsdf_src/builds/"
            + buildId + "/");
        request.addHeader("Authorization", "OAuth " + oauthToken_);
        auto response = request.perform();
        REQUIRE(response.status() < 400,
            "Failed to check build in Garden. Response status " << response.status()
                << "\nResponse body: " << response.readBody());
        auto responseJson = maps::json::Value::fromString(response.readBody());
        if (responseJson["progress"]["status"].as<std::string>() == "completed") {
            INFO() << "Build has been created successfully";
            return;
        }
        INFO() << "Waiting for 5s while build is being created in Garden...";
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
}

void GardenPoster::startBuild(const std::string& buildVersion)
{
    INFO() << "Sending request to start build in Garden...";
    maps::http::Request request(http_, maps::http::POST,
        "http://core-garden-server.maps.yandex.net/modules/ymapsdf/builds/");
    request.addHeader("Authorization", "OAuth " + oauthToken_);
    request.addHeader("Content-Type", "application/json");
    request.setContent(generateStartBuildJson(buildVersion));
    auto response = request.perform();
    REQUIRE(response.status() < 400,
        "Failed to start build in Garden. Response status " << response.status()
            << "\nResponse body: " << response.readBody());
}

} // namespace maps::wiki::exporter
