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

#include <maps/libs/json/include/value.h>
#include <maps/libs/json/include/builder.h>

#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/storage/include/detection_results.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/storage/include/tolokers_results.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/storage/include/assessors_results.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/storage/include/publication_results.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/storage/include/yt_storage.h>

#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/config/include/config.h>

#include <fstream>

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

namespace json = maps::json;

namespace {

template <typename Result>
void writeResultsToJson(
    const std::string& resultsJsonPath, const std::vector<Result>& results)
{
    std::ofstream ofs(resultsJsonPath);
    REQUIRE(ofs.is_open(), "Failed to open file: " + resultsJsonPath);
    json::Builder builder(ofs);
    builder << [&](json::ArrayBuilder b) {
        for (const Result& result : results) {
            b << [&](json::ObjectBuilder b) {
                result.toJson(b);
            };
        }
    };
    ofs.close();
}

} // namespace

int main(int argc, const char** argv)
try {
    NYT::Initialize(argc, argv);

    maps::cmdline::Parser parser("Load results from YT storage");

    maps::cmdline::Option<std::string> type = parser.string("type")
        .required()
        .help("type of results");

    maps::cmdline::Option<std::string> ytConfigPath = parser.string("yt_config")
        .required()
        .help("Path to YT config");

    maps::cmdline::Option<std::string> region = parser.string("region")
        .required()
        .help("Region name from MPRO");

    maps::cmdline::Option<size_t> issueId = parser.size_t("issue_id")
        .required()
        .help("issue id of satellite images");

    maps::cmdline::Option<std::string> resultsJsonPath = parser.string("results_json")
        .required()
        .help("path to output json file with results");

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

    INFO() << "Connecting to yt::hahn";
    NYT::IClientPtr ytClient = NYT::CreateClient("hahn");

    INFO() << "Loading YT config: " << ytConfigPath;
    YTConfig config(ytConfigPath);

    INFO() << "Connecting to YT storage: " << config.storagePath();
    YTStorageClient ytStorageClient(ytClient, config.storagePath());

    INFO() << "Loading " << type << " results";

    if (DetectionResult::getName() == type) {
        std::vector<DetectionResult> results;
        ytStorageClient.getResults(TString(region), issueId, &results);
        writeResultsToJson(resultsJsonPath, results);
    } else if (TolokersResult::getName() == type) {
        std::vector<TolokersResult> results;
        ytStorageClient.getResults(TString(region), issueId, &results);
        writeResultsToJson(resultsJsonPath, results);
    } else if (AssessorsResult::getName() == type) {
        std::vector<AssessorsResult> results;
        ytStorageClient.getResults(TString(region), issueId, &results);
        writeResultsToJson(resultsJsonPath, results);
    } else if (PublicationResult::getName() == type) {
        std::vector<PublicationResult> results;
        ytStorageClient.getResults(TString(region), issueId, &results);
        writeResultsToJson(resultsJsonPath, results);
    }

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