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

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

#include <util/string/cast.h>

#include <vector>
#include <chrono>

namespace maps::wiki::autocart::pipeline {

namespace {
// process params
constexpr const char* ST_ISSUE = "st_issue";
constexpr const char* POOL_ID = "pool_id";
constexpr const char* REGION = "region";
constexpr const char* ISSUE_ID = "issue_id";
constexpr const char* USE_TOLOKERS = "use_tolokers";
constexpr const char* USE_ASSESSORS = "use_assessors";
constexpr const char* REGION_ID = "region_id";

// requester robot
constexpr const char* ROBOT_MAPS_CORE_MRC = "robot-maps-core-mrc";

// parallel runs limit
constexpr std::chrono::hours AVAILABLE_RUNS_WAIT_TIMEOUT(/* one week */ 24 * 7);
constexpr std::chrono::minutes AVAILABLE_RUNS_RECHECK_INTERVAL(15);

static const std::set<std::string> AVAILABLE_PROCESS_CODES{
    LOAD_TOLOKERS_TASKS_PROCESS_CODE,
    LOAD_ASSESSORS_TASKS_PROCESS_CODE,
    PUBLICATION_REQUEST_PROCESS_CODE,
    PUBLICATION_PROCESS_CODE
};

static const std::set<std::string> AVAILABLE_VALIDATION_PROCESS_CODES{
    TOLOKERS_VALIDATION_PROCESS_CODE,
    ASSESSORS_VALIDATION_PROCESS_CODE
};

static const std::set<std::string> NOT_DETECTION_PROCESS_CODES{
    CREATE_ST_ISSUE_PROCESS_CODE,
    LOAD_TOLOKERS_TASKS_PROCESS_CODE,
    TOLOKERS_VALIDATION_PROCESS_CODE,
    LOAD_ASSESSORS_TASKS_PROCESS_CODE,
    ASSESSORS_VALIDATION_PROCESS_CODE,
    PUBLICATION_REQUEST_PROCESS_CODE,
    PUBLICATION_PROCESS_CODE
};

} // namespace


std::set<std::string> loadRegionsInProcessing(const hitman::HitmanClient& client) {
    static const std::vector<hitman::HitmanJobStatus> statuses{
        hitman::HitmanJobStatus::NEW,
        hitman::HitmanJobStatus::RUNNING,
        hitman::HitmanJobStatus::RESUMING,
        hitman::HitmanJobStatus::IN_CANCELLATION
    };

    std::set<std::string> names;
    for (const hitman::HitmanJobStatus& status : statuses) {
        for (const std::string& process : NOT_DETECTION_PROCESS_CODES) {
            std::vector<hitman::HitmanJob> jobs
                = client.getJobsByStatus(process, status);
            for (const hitman::HitmanJob& job : jobs) {
                names.insert(client.getProcessProperties(job.id).at(REGION));
            }
        }
    }

    return names;
}

bool hasFailedJobs(const hitman::HitmanClient& client) {
    static const std::vector<hitman::HitmanJobStatus> statuses{
        hitman::HitmanJobStatus::FAILED,
        hitman::HitmanJobStatus::START_FAILED
    };

    size_t failedJobsCount = 0;
    for (const hitman::HitmanJobStatus& status : statuses) {
        for (const std::string& process : NOT_DETECTION_PROCESS_CODES) {
            std::vector<hitman::HitmanJob> jobs
                = client.getJobsByStatus(process, status);
            failedJobsCount += jobs.size();
        }
    }

    return failedJobsCount != 0;
}

hitman::HitmanJobId runSTIssueProcess(
    const hitman::HitmanClient& client,
    uint64_t id, const std::string& name,
    bool useTolokers, bool useAssessors,
    uint64_t issueId)
{
    hitman::HitmanProcessProperties properties;
    properties[REGION_ID] = std::to_string(id);
    properties[REGION] = name;
    properties[ISSUE_ID] = std::to_string(issueId);
    properties[USE_TOLOKERS] = useTolokers ? "true" : "false";
    properties[USE_ASSESSORS] = useAssessors ? "true" : "false";

    return client.waitAvailableRunsAndRunProcess(
        CREATE_ST_ISSUE_PROCESS_CODE,
        ROBOT_MAPS_CORE_MRC,
        properties,
        AVAILABLE_RUNS_WAIT_TIMEOUT, AVAILABLE_RUNS_RECHECK_INTERVAL
    );
}

hitman::HitmanJobId runProcess(
    const hitman::HitmanClient& client, const std::string processCode,
    const std::string& stIssue,
    const std::string& name,
    bool useTolokers, bool useAssessors,
    uint64_t issueId)
{
    REQUIRE(AVAILABLE_PROCESS_CODES.count(processCode),
            "Unavailable process code: " + processCode);
    hitman::HitmanProcessProperties properties;
    properties[ST_ISSUE] = stIssue;
    properties[REGION] = name;
    properties[ISSUE_ID] = std::to_string(issueId);
    properties[USE_TOLOKERS] = useTolokers ? "true" : "false";
    properties[USE_ASSESSORS] = useAssessors ? "true" : "false";

    return client.waitAvailableRunsAndRunProcess(
        processCode,
        ROBOT_MAPS_CORE_MRC,
        properties,
        AVAILABLE_RUNS_WAIT_TIMEOUT, AVAILABLE_RUNS_RECHECK_INTERVAL
    );
}

hitman::HitmanJobId runValidationProcess(
    const hitman::HitmanClient& client, const std::string processCode,
    const std::string& stIssue,
    const std::string& name,
    bool useTolokers, bool useAssessors,
    uint64_t issueId,
    const std::string& poolId)
{
    REQUIRE(AVAILABLE_VALIDATION_PROCESS_CODES.count(processCode),
            "Unavailable validation process code: " + processCode);
    hitman::HitmanProcessProperties properties;
    properties[ST_ISSUE] = stIssue;
    properties[REGION] = name;
    properties[ISSUE_ID] = std::to_string(issueId);
    properties[USE_TOLOKERS] = useTolokers ? "true" : "false";
    properties[USE_ASSESSORS] = useAssessors ? "true" : "false";
    properties[POOL_ID] = poolId;

    return client.waitAvailableRunsAndRunProcess(
        processCode,
        ROBOT_MAPS_CORE_MRC,
        properties,
        AVAILABLE_RUNS_WAIT_TIMEOUT, AVAILABLE_RUNS_RECHECK_INTERVAL
    );
}

} // namespace maps::wiki::autocart::pipeline
