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

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

#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/hitman_client/include/client.h>
#include <maps/wikimap/mapspro/services/autocart/pipeline/libs/hitman_processes/include/processes.h>

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

namespace {

std::string getSTIssue(const std::string& jsonPath) {
    static const std::string ST_ISSUE = "key";
    return maps::json::Value::fromFile(jsonPath)[0][ST_ISSUE].as<std::string>();
}

static const std::set<std::string> SOURCE_PROCESS_CODES{
    CREATE_ST_ISSUE_PROCESS_CODE,
    TOLOKERS_VALIDATION_PROCESS_CODE,
    ASSESSORS_VALIDATION_PROCESS_CODE,
    PUBLICATION_REQUEST_PROCESS_CODE
};

} // namespace

int main(int argc, const char** argv)
try {
    maps::cmdline::Parser parser("Run next Hitman process");

    maps::cmdline::Option<std::string> sourceProcessCode = parser.string("source")
        .required()
        .help("Source Hitman process");

    maps::cmdline::Option<std::string> hitmanToken = parser.string("hitman_token")
        .required()
        .help("Token for Hitman client");

    maps::cmdline::Option<std::string> stIssueJsonPath = parser.string("st_issue")
        .required()
        .help("Input json file with ST issue");

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

    maps::cmdline::Option<bool> useTolokers = parser.flag("use_tolokers")
        .defaultValue(false)
        .help("Use tolokers validation");

    maps::cmdline::Option<bool> useAssessors = parser.flag("use_assessors")
        .defaultValue(false)
        .help("Use assessors validation");

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

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

    REQUIRE(SOURCE_PROCESS_CODES.count(sourceProcessCode),
            "Invalid source process: " + sourceProcessCode);

    INFO() << "Connecting to Hitman: " << PRODUCTION_HITMAN_HOST;
    HitmanClient hitmanClient(PRODUCTION_HITMAN_HOST, hitmanToken);

    hitmanClient.setSchema("https://")
        .setTimeout(std::chrono::seconds(10))
        .setMaxRequestAttempts(5)
        .setRetryInitialTimeout(std::chrono::seconds(1))
        .setRetryTimeoutBackoff(2.);

    std::string nextProcessCode = "";
    if (CREATE_ST_ISSUE_PROCESS_CODE == sourceProcessCode) {
        if (useTolokers) {
            nextProcessCode = LOAD_TOLOKERS_TASKS_PROCESS_CODE;
        } else if (useAssessors) {
            nextProcessCode = LOAD_ASSESSORS_TASKS_PROCESS_CODE;
        } else {
            nextProcessCode = PUBLICATION_REQUEST_PROCESS_CODE;
        }
    } else if (TOLOKERS_VALIDATION_PROCESS_CODE == sourceProcessCode) {
        if (useAssessors) {
            nextProcessCode = LOAD_ASSESSORS_TASKS_PROCESS_CODE;
        } else {
            nextProcessCode = PUBLICATION_REQUEST_PROCESS_CODE;
        }
    } else if (ASSESSORS_VALIDATION_PROCESS_CODE == sourceProcessCode) {
        nextProcessCode = PUBLICATION_REQUEST_PROCESS_CODE;
    } else if (PUBLICATION_REQUEST_PROCESS_CODE == sourceProcessCode) {
        nextProcessCode = PUBLICATION_PROCESS_CODE;
    }

    REQUIRE(!nextProcessCode.empty(), "Failed to find next process");
    HitmanJobId jobId = runProcess(
        hitmanClient, nextProcessCode,
        getSTIssue(stIssueJsonPath),
        region,
        useTolokers, useAssessors,
        issueId
    );

    INFO() << "Run process: code - " << nextProcessCode << ", id - " << jobId;

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