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

#include <yandex/maps/wiki/common/robot.h>

#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/nmaps/include/publish_nmaps_blds.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/storage/include/base_results.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/storage/include/publication_results.h>

#include <fstream>

using namespace maps::wiki;
using namespace maps::wiki::autocart::pipeline;

namespace {

const std::unordered_map<std::string, std::string> READER_BACKEND_TYPE_TO_URL {
    {"testing", "http://core-nmaps-editor.common.testing.maps.yandex.net"},
    {"production", "http://core-nmaps-editor.maps.yandex.net"}
};

const std::unordered_map<std::string, std::string> WRITER_BACKEND_TYPE_TO_URL {
    {"testing", "http://core-nmaps-editor.common.testing.maps.yandex.net"},
    {"production", "http://core-nmaps-editor-writer.maps.yandex.net"}
};

std::string getBackendHelpStr() {
    std::stringstream ss;
    ss << "Backend type:\n";
    size_t i = 1;
    for (const auto& [backendType, writerURL] : WRITER_BACKEND_TYPE_TO_URL) {
        const std::string& readerURL = READER_BACKEND_TYPE_TO_URL.at(backendType);
        ss << "  " << i << ") " << backendType << ": "
           << "writer - " << writerURL << ", reader - " << readerURL << "\n";
        i++;
    }
    return ss.str();
}

std::string getUIDHelpStr() {
    std::stringstream ss;
    ss << "User id. Default user - \"wikimaps-bld\" : " << common::WIKIMAPS_BLD_UID;
    return ss.str();
}

} // namespace

int main(int argc, const char** argv)
try {
    maps::cmdline::Parser parser("Publication buildings to NMaps");

    auto bldsJsonPath = parser.string("blds_json")
        .required()
        .help("Path to input json file with detected buildings");

    auto outputJsonPath = parser.string("output_json")
        .required()
        .help("Path to output json file with results of publication");

    auto backendType = parser.string("backend")
        .required()
        .help(getBackendHelpStr());

    auto uid = parser.size_t("uid")
        .defaultValue(common::WIKIMAPS_BLD_UID)
        .help(getUIDHelpStr());

    auto startHour = parser.num("start_hour")
        .help("Start hour for publication time range in [0, 23]");

    auto endHour = parser.num("end_hour")
        .help("End hour for publibcation time range in [0, 23]");

    auto statePath = parser.string("state")
        .required()
        .help("Path to state with publication results");

    auto stateStep = parser.size_t("state_step")
        .defaultValue(3000)
        .help("Save state after each state_step steps");

    parser.parse(argc, const_cast<char**>(argv));

    REQUIRE(
        READER_BACKEND_TYPE_TO_URL.count(backendType) > 0 &&
        WRITER_BACKEND_TYPE_TO_URL.count(backendType) > 0,
        "Invalid backend type: " << backendType
    );
    editor_client::Instance readerClient(READER_BACKEND_TYPE_TO_URL.at(backendType), uid);
    editor_client::Instance writerClient(WRITER_BACKEND_TYPE_TO_URL.at(backendType), uid);

    INFO() << "Loading buildings from json: " << bldsJsonPath;
    maps::json::Value bldsJson = maps::json::Value::fromFile(bldsJsonPath);
    std::vector<BaseResult> blds;
    for (const maps::json::Value& bldJson : bldsJson) {
        blds.push_back(BaseResult::fromJson(bldJson));
    }
    INFO() << "Loaded " << blds.size() << " buildings";

    REQUIRE(
        (startHour.defined() && endHour.defined())
        ||
        (!startHour.defined() && !endHour.defined()),
        "Start and hours for publication time range"
        " must be defined or not defined at same time"
    );

    PublicationTimeRange timeRange
        = startHour.defined() && endHour.defined()
        ? PublicationTimeRange(startHour, endHour)
        : PublicationTimeRange::allDay();

    INFO() << "Publishing buildings to NMaps: " << WRITER_BACKEND_TYPE_TO_URL.at(backendType);
    std::vector<PublicationResult> publicationResults
        = publishBuildingsToNMaps(blds,
            readerClient, writerClient,
            uid,
            timeRange,
            statePath, stateStep
        );

    INFO() << "Dumping publication results to json: " << outputJsonPath;
    std::ofstream ofs(outputJsonPath);
    REQUIRE(ofs.is_open(), "Failed to open file:" + outputJsonPath);
    maps::json::Builder builder(ofs);
    builder << [&](maps::json::ArrayBuilder b) {
        for (const PublicationResult& result : publicationResults) {
            b << [&](maps::json::ObjectBuilder b) {
                result.toJson(b);
            };
        }
    };
    ofs.close();

    return EXIT_SUCCESS;
}
catch (const maps::Exception& e) {
    ERROR() << e;
    return EXIT_FAILURE;
}
catch (const std::exception& e) {
    ERROR() << e.what();
    return EXIT_FAILURE;
}
catch (...) {
    ERROR() << "unknown error";
    return EXIT_FAILURE;
}
