#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>

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

namespace json = maps::json;

namespace {

template <typename Result>
void loadResultsFromJson(
    const std::string& resultsJsonPath, std::vector<Result>* results)
{
    json::Value resultsJson = json::Value::fromFile(resultsJsonPath);
    for (const json::Value& resultJson : resultsJson) {
        results->push_back(Result::fromJson(resultJson));
    }
}

} // namespace

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

    maps::cmdline::Parser parser("Save results to YT storage");

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

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

    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() << "Saving " << type << " results";
    if (DetectionResult::getName() == type) {
        std::vector<DetectionResult> results;
        loadResultsFromJson(resultsJsonPath, &results);
        ytStorageClient.saveResults(TString(region), issueId, results);
    } else if (TolokersResult::getName() == type) {
        std::vector<TolokersResult> results;
        loadResultsFromJson(resultsJsonPath, &results);
        ytStorageClient.saveResults(TString(region), issueId, results);
    } else if (AssessorsResult::getName() == type) {
        std::vector<AssessorsResult> results;
        loadResultsFromJson(resultsJsonPath, &results);
        ytStorageClient.saveResults(TString(region), issueId, results);
    } else if (PublicationResult::getName() == type) {
        std::vector<PublicationResult> results;
        loadResultsFromJson(resultsJsonPath, &results);
        ytStorageClient.saveResults(TString(region), issueId, 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;
}
