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

#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/assignments/include/aggregate.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/assessors/include/assessor.h>

#include <fstream>
#include <iostream>
#include <functional>
#include <unordered_map>

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

namespace {

std::string getSourceHelpStr() {
    std::stringstream ss;
    ss << "Type of results validation\n";
    ss << " 1) " << TolokersResult::getName() << " - validation by tolokers\n";
    ss << " 2) " << AssessorsResult::getName() << " - validation by assessors\n";
    return ss.str();
}

} // namespace

int main(int argc, const char** argv)
try {
    maps::cmdline::Parser parser("Aggregate validation results");

    auto resultJsonPath = parser.string("result_json")
        .required()
        .help("Path to input json file");

    auto source = parser.string("source")
        .required()
        .help(getSourceHelpStr());

    auto wikiOAuthToken = parser.string("wiki_token")
        .help("Token for wiki. See https://auth.yandex-team.ru");

    auto wikiTableName = parser.string("wiki_table")
        .help("Name of wiki table with logins of assessors. Example:\n"
              " * URL - https://wiki.yandex-team.ru/users/dolotov-e/Arkadija/\n"
              " * name - users/dolotov-e/Arkadija");

    auto aggregatedJsonPath = parser.string("aggregated_json")
        .required()
        .help("Path to output json file with aggregated results");

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

    json::Value assignments = json::Value::fromFile(resultJsonPath);

    std::ofstream ofs(aggregatedJsonPath);
    REQUIRE(ofs.is_open(), "Failed to open file: " + resultJsonPath);
    json::Builder aggregatedBuilder(ofs);

    if (TolokersResult::getName() == source) {
        aggregatedBuilder << [&](json::ArrayBuilder b) {
            for (const auto& result : aggregateTolokersAssignments(assignments)) {
                b << [&](json::ObjectBuilder b) {
                    result.toJson(b);
                };
            }
        };
    } else if (AssessorsResult::getName() == source) {
        REQUIRE(wikiTableName.defined() && wikiOAuthToken.defined(),
                "Path to wiki table and oauth should be specified for source - " << source);

        std::map<std::string, std::string> loginByWorkerId
            = loadLoginByWorkerIdFromWikiTable(wikiTableName, wikiOAuthToken);

        aggregatedBuilder << [&](json::ArrayBuilder b) {
            for (const auto& result : aggregateAssessorsAssignments(assignments, loginByWorkerId)) {
                b << [&](json::ObjectBuilder b) {
                    result.toJson(b);
                };
            }
        };
    } else {
        throw maps::RuntimeError("Unknown source: " + source);
    }

    ofs.close();

    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;
}
