#include <maps/wikimap/mapspro/services/mrc/libs/db/include/common.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/feature_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/house_number.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/house_number_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/panorama_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/config/include/config.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/exif.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/image_box.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/panorama_utils.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/panorama_cropper.h>

#include <maps/libs/pgpool/include/pgpool3.h>
#include <maps/libs/log8/include/log8.h>
#include <maps/libs/common/include/exception.h>
#include <maps/libs/mds-client/include/yandex/maps/mds/mds.h>

#include <maps/libs/json/include/builder.h>
#include <util/generic/guid.h>

#include <maps/wikimap/mapspro/services/mrc/libs/nirvana/include/nirvana_workflow.h>
#include <maps/wikimap/mapspro/services/mrc/libs/toloka_validation/include/toloka_validation.h>

using namespace maps::mrc::nirvana;

namespace maps::mrc::toloka_validation {

namespace {

static const std::string BLOCK_PARAMETER_PARAMETER_NAME = "parameter";
static const std::string BLOCK_PARAMETER_VALUE_NAME     = "value";

static const std::string SH_WGET_OPERATION_ID = "3f8a4b71-861c-4abf-a34d-8ba6b50ea882";
static const std::string SH_WGET_OPERATION_URL_PARAMETER_NAME       = "url";
static const std::string SH_WGET_OPERATION_OUT_TYPE_PARAMETER_NAME  = "out_type";
static const std::string SH_WGET_OPERATION_OUT_TYPE_PARAMETER_VALUE = "json";
static const std::string SH_WGET_OPERATION_OUTPUT_JSON_NAME = "json";

static const std::string EXPORT_SINGLE_FILE_OPERATION_ID = "7306fbe8-3ae7-4eb0-b846-e132649b9033";
static const std::string EXPORT_SINGLE_FILE_OPERATION_ARCADIA_PATH_PARAMETER_NAME = "arcadia_path";
static const std::string EXPORT_SINGLE_FILE_OPERATION_REVISION_PARAMETER_NAME = "revision";
static const std::string EXPORT_SINGLE_FILE_OPERATION_PATH_PREFIX_PARAMETER_NAME = "path_prefix";
static const std::string EXPORT_SINGLE_FILE_OPERATION_TEXT_OUTPUT_NAME = "text";

static const std::string SCRIPT_ARCADIA_PATH = "dynamic_overlap.groovy";
static const size_t SCRIPT_REVISION = 6848549;
static const std::string SCRIPT_PREFIX_PATH = "trunk/arcadia/maps/wikimap/mapspro/services/mrc/libs/toloka_validation/comrade_script";

static const std::string SINGLE_OPTION_TO_JSON_OUTPUT_OPERATION_ID = "2fdd4bb4-4303-11e7-89a6-0025909427cc";
static const std::string SINGLE_OPTION_TO_JSON_OUTPUT_OPERATION_INPUT_PARAMETER_NAME = "input";
static const std::string SINGLE_OPTION_TO_JSON_OUTPUT_OPERATION_OUTPUT_OUTPUT_NAME = "output";

static const std::string HITMAN_COMRADE_REALTIME_OPERATION_ID = "fe2ff97e-7a5d-4c12-a47a-f8d8da346f70";
static const std::string HITMAN_COMRADE_REALTIME_OPERATION_STORAGE_INPUT_NAME = "storage";
static const std::string HITMAN_COMRADE_REALTIME_OPERATION_POOLS_INPUT_NAME = "pools";
static const std::string HITMAN_COMRADE_REALTIME_OPERATION_SCRIPT_INPUT_NAME = "script";
static const std::string HITMAN_COMRADE_REALTIME_OPERATION_STOP_CONDITION_INPUT_NAME = "stopCondition";
static const std::string HITMAN_COMRADE_REALTIME_OPERATION_POOLS_OUTPUT_NAME = "pools";

static const std::string HITMAN_EMPTY_STORAGE_PARAMS = R"({"params" : {}})";
static const std::string HITMAN_STOP_CONDITION_PARAMS =
R"({
    "duration": 10,
    "poolShouldBeClosed": true,
    "timeUnit": "MINUTES",
    "poolCloseReasons": [
        "EXPIRED",
        "COMPLETED",
        "MANUAL"
    ]
})";

static const std::string TOLOKA_OPERATION_TOKEN_PARAMETER_NAME = "encryptedOauthToken";
static const std::string TOLOKA_OPERATION_ENVIRONMENT_PARAMETER_NAME = "environment";
static const std::string TOLOKA_OPERATION_CLOSE_POOL_IF_OPERATION_WAS_CANCELLED  = "closePoolIfOperationWasCancelled";

static const std::string TOLOKA_CLONE_POOLS_OPERATION_ID = "72ff4bef-a2d5-4f0d-a02d-f429a021fc1b";
static const std::string TOLOKA_CLONE_POOLS_OPERATION_POOL_PARAMETER_NAME = "poolGroovyPredicate";
static const std::string TOLOKA_CLONE_POOLS_OPERATION_INPUT_TASKS_NAME = "tasks";
static const std::string TOLOKA_CLONE_POOLS_OPERATION_INPUT_HONEYPOTS_NAME = "honeypots";
static const std::string TOLOKA_CLONE_POOLS_OPERATION_OUTPUT_TASKS_NAME = "tasks";
static const std::string TOLOKA_CLONE_POOLS_OPERATION_OUTPUT_HONEYPOTS_NAME = "honeypots";

static const std::string TOLOKA_UPLOAD_POOLS_OPERATION_ID = "e3a7d5e7-1bda-446b-ad87-491bcc86da38";
static const std::string TOLOKA_CLONE_POOLS_OPERATION_DEFAULT_OVERLAP_FOR_HONEYPOTS_PARAMETER_NAME = "defaultOverlapForHoneypots";
static const std::string TOLOKA_UPLOAD_POOLS_OPERATION_INPUT_TASKS_NAME = "tasks";
static const std::string TOLOKA_UPLOAD_POOLS_OPERATION_INPUT_HONEYPOTS_NAME = "honeypots";
static const std::string TOLOKA_UPLOAD_POOLS_OPERATION_OUTPUT_POOLS_NAME = "pools";

static const std::string TOLOKA_OPEN_POOLS_OPERATION_ID = "570c4026-8489-4bbd-ba55-f8e62522e93b";
static const std::string TOLOKA_OPEN_POOLS_OPERATION_WAIT_FOR_MANUAL_OPENING_PARAMETER_NAME = "waitForManualOpening";
static const std::string TOLOKA_OPEN_POOLS_OPERATION_ALLOW_EMPTY_POOLS_PARAMETER_NAME = "allowEmptyPools";
static const std::string TOLOKA_OPEN_POOLS_OPERATION_INPUT_POOLS_NAME = "pools";
static const std::string TOLOKA_OPEN_POOLS_OPERATION_OUTPUT_POOLS_NAME = "pools";

static const std::string TOLOKA_WAIT_POOLS_OPERATION_ID = "80d0a2d0-eadf-46fd-a39a-3cbb60b17863";
static const std::string TOLOKA_WAIT_POOLS_OPERATION_INPUT_POOLS_NAME = "pools";
static const std::string TOLOKA_WAIT_POOLS_OPERATION_OUTPUT_POOLS_NAME = "pools";

static const std::string TOLOKA_GET_ASSIGMENTS_RESULTS_OPERATION_ID = "45d0bf20-036d-47f9-bb95-c11f49d90caf";
static const std::string TOLOKA_GET_ASSIGMENTS_RESULTS_OPERATION_INPUT_POOLS_NAME = "pools";
static const std::string TOLOKA_GET_ASSIGMENTS_RESULTS_OPERATION_OUTPUT_TASKS_NAME = "tasks";

maps::mrc::nirvana::BlockPattern
makeWgetBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const std::string& url
) {
    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(SH_WGET_OPERATION_ID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = SH_WGET_OPERATION_URL_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = url;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = SH_WGET_OPERATION_OUT_TYPE_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = SH_WGET_OPERATION_OUT_TYPE_PARAMETER_VALUE;
            };
        }
    );
    return opBlock;
}

maps::mrc::nirvana::BlockPattern
makeExportSingleFileBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const std::string& arcadiaPath,
    size_t revision,
    const std::string& pathPrefix
) {
    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(EXPORT_SINGLE_FILE_OPERATION_ID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = EXPORT_SINGLE_FILE_OPERATION_ARCADIA_PATH_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = arcadiaPath;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = EXPORT_SINGLE_FILE_OPERATION_REVISION_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = revision;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = EXPORT_SINGLE_FILE_OPERATION_PATH_PREFIX_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = pathPrefix;
            };
        }
    );
    return opBlock;
}

maps::mrc::nirvana::BlockPattern
makeSingleOptionToJsonOutputBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const std::string& input
) {
    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(SINGLE_OPTION_TO_JSON_OUTPUT_OPERATION_ID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = SINGLE_OPTION_TO_JSON_OUTPUT_OPERATION_INPUT_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = input;
            };
        }
    );
    return opBlock;
}

maps::mrc::nirvana::BlockPattern
makeClonePoolsBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const TolokaEnvironment& tolokaEnvironment,
    const std::string& basePoolID
) {

    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(TOLOKA_CLONE_POOLS_OPERATION_ID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_TOKEN_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = tolokaEnvironment.oathTokenName;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_ENVIRONMENT_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = tolokaEnvironment.name;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_CLONE_POOLS_OPERATION_POOL_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = basePoolID;
            };
        }
    );
    return opBlock;
}

// https://nirvana.yandex-team.ru/operation/fe2ff97e-7a5d-4c12-a47a-f8d8da346f70
maps::mrc::nirvana::BlockPattern
makeHitmanComradeBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const TolokaEnvironment& tolokaEnvironment
) {

    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(HITMAN_COMRADE_REALTIME_OPERATION_ID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_TOKEN_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = tolokaEnvironment.oathTokenName;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_ENVIRONMENT_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = tolokaEnvironment.name;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_CLOSE_POOL_IF_OPERATION_WAS_CANCELLED;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = true;
            };
        }
    );
    return opBlock;
}

maps::mrc::nirvana::BlockPattern
makeGeneralTolokaBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const TolokaEnvironment& tolokaEnvironment,
    const std::string& blockID
) {
    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(blockID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_TOKEN_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = tolokaEnvironment.oathTokenName;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_ENVIRONMENT_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = tolokaEnvironment.name;
            };
        }
    );
    return opBlock;
}

maps::mrc::nirvana::BlockPattern
makeOpenPoolsBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const TolokaEnvironment& tolokaEnvironment
) {
    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(TOLOKA_OPEN_POOLS_OPERATION_ID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_TOKEN_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = tolokaEnvironment.oathTokenName;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_ENVIRONMENT_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = tolokaEnvironment.name;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPEN_POOLS_OPERATION_WAIT_FOR_MANUAL_OPENING_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = false;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPEN_POOLS_OPERATION_ALLOW_EMPTY_POOLS_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME]     = false;
            };
        }
    );
    return opBlock;
}

maps::mrc::nirvana::BlockPattern
makeUploadPoolsBlock(
    maps::mrc::nirvana::WorkflowInstance &wi,
    const TolokaEnvironment& tolokaEnvironment
) {
    maps::mrc::nirvana::BlockPattern opBlock = wi.addOperationBlock(TOLOKA_UPLOAD_POOLS_OPERATION_ID);
    wi.setBlockParameters(opBlock,
        [&](maps::json::ArrayBuilder builder) {
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_TOKEN_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = tolokaEnvironment.oathTokenName;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_OPERATION_ENVIRONMENT_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = tolokaEnvironment.name;
            };
            builder << [&](maps::json::ObjectBuilder builder) {
                builder[BLOCK_PARAMETER_PARAMETER_NAME] = TOLOKA_CLONE_POOLS_OPERATION_DEFAULT_OVERLAP_FOR_HONEYPOTS_PARAMETER_NAME;
                builder[BLOCK_PARAMETER_VALUE_NAME] = -1;
            };
        }
    );
    return opBlock;
}

std::string download(const std::string& url)
{
    maps::http::Client client;
    maps::common::RetryPolicy retryPolicy;
    retryPolicy.setTryNumber(10)
        .setInitialCooldown(std::chrono::seconds(1))
        .setCooldownBackoff(2);

    auto validateResponse = [](const auto& maybeResponse) {
        return maybeResponse.valid() && maybeResponse.get().responseClass() != maps::http::ResponseClass::ServerError;
    };
    auto resp = maps::common::retry(
                [&]() {
                    return maps::http::Request(client, maps::http::GET, maps::http::URL(url)).perform();
                },
                retryPolicy,
                validateResponse
            );
    REQUIRE(resp.responseClass() == maps::http::ResponseClass::Success,
        "Unexpected response status " << resp.status() << " for url "
        << url);
    return resp.readBody();
}

}// namespace

TolokaCheckerInformation launchTolokaChecker(
    const std::string& oauthNirvanaToken,
    const std::string& niravanaQuotaID,
    const TolokaEnvironment& tolokaEnvironment,
    const std::string& taskURL,
    const std::string& workflowID,
    bool startWorkflowInstance
) {
    TolokaCheckerInformation result;

    result.nirvanaWorkflowID = workflowID;

    maps::mrc::nirvana::WorkflowInstance wi(oauthNirvanaToken, workflowID, "", niravanaQuotaID);
    result.nirvanaWorkflowInstanceID = wi.getWorkflowInstanceId();
    INFO() << "Created workflow instance: " << result.nirvanaWorkflowInstanceID;

    maps::mrc::nirvana::BlockPattern
        opWgetTaskBlock = makeWgetBlock(wi, taskURL);
    maps::mrc::nirvana::BlockPattern opWgetGoldenTaskBlock;
    if (!tolokaEnvironment.goldenTaskJsonURL.empty()) {
        opWgetGoldenTaskBlock = makeWgetBlock(wi, tolokaEnvironment.goldenTaskJsonURL);
    }
    maps::mrc::nirvana::BlockPattern
        opClonePoolsBlock = makeClonePoolsBlock(wi, tolokaEnvironment, tolokaEnvironment.basePoolID);
    maps::mrc::nirvana::BlockPattern
        opUploadPoolsBlock = makeUploadPoolsBlock(wi, tolokaEnvironment);
    maps::mrc::nirvana::BlockPattern
        opOpenPoolsBlock = makeOpenPoolsBlock(wi, tolokaEnvironment);

    maps::mrc::nirvana::BlockPattern
        opHitmanStorageBlock = makeSingleOptionToJsonOutputBlock(wi, HITMAN_EMPTY_STORAGE_PARAMS);
    maps::mrc::nirvana::BlockPattern
        opExportScriptBlock = makeExportSingleFileBlock(wi, SCRIPT_ARCADIA_PATH, SCRIPT_REVISION, SCRIPT_PREFIX_PATH);
    maps::mrc::nirvana::BlockPattern
        opStopConditionBlock = makeSingleOptionToJsonOutputBlock(wi, HITMAN_STOP_CONDITION_PARAMS);
    maps::mrc::nirvana::BlockPattern
        opHitmanComradeBlock = makeHitmanComradeBlock(wi, tolokaEnvironment);

    maps::mrc::nirvana::BlockPattern
        opGetResultsPoolsBlock = makeGeneralTolokaBlock(wi, tolokaEnvironment, TOLOKA_GET_ASSIGMENTS_RESULTS_OPERATION_ID);



    wi.connectOperationBlocks(
        {opWgetTaskBlock}, SH_WGET_OPERATION_OUTPUT_JSON_NAME,
        {opClonePoolsBlock}, TOLOKA_CLONE_POOLS_OPERATION_INPUT_TASKS_NAME);
    if (!tolokaEnvironment.goldenTaskJsonURL.empty()) {
        wi.connectOperationBlocks(
            { opWgetGoldenTaskBlock }, SH_WGET_OPERATION_OUTPUT_JSON_NAME,
            { opClonePoolsBlock }, TOLOKA_CLONE_POOLS_OPERATION_INPUT_HONEYPOTS_NAME);
    }

    wi.connectOperationBlocks(
        {opClonePoolsBlock}, TOLOKA_CLONE_POOLS_OPERATION_OUTPUT_TASKS_NAME,
        {opUploadPoolsBlock}, TOLOKA_UPLOAD_POOLS_OPERATION_INPUT_TASKS_NAME);
    if (!tolokaEnvironment.goldenTaskJsonURL.empty()) {
        wi.connectOperationBlocks(
            { opClonePoolsBlock }, TOLOKA_CLONE_POOLS_OPERATION_OUTPUT_HONEYPOTS_NAME,
            { opUploadPoolsBlock }, TOLOKA_UPLOAD_POOLS_OPERATION_INPUT_HONEYPOTS_NAME);
    }
    wi.connectOperationBlocks(
        {opUploadPoolsBlock}, TOLOKA_UPLOAD_POOLS_OPERATION_OUTPUT_POOLS_NAME,
        {opOpenPoolsBlock}, TOLOKA_OPEN_POOLS_OPERATION_INPUT_POOLS_NAME);

    wi.connectOperationBlocks(
        {opHitmanStorageBlock}, SINGLE_OPTION_TO_JSON_OUTPUT_OPERATION_OUTPUT_OUTPUT_NAME,
        {opHitmanComradeBlock}, HITMAN_COMRADE_REALTIME_OPERATION_STORAGE_INPUT_NAME);
    wi.connectOperationBlocks(
        {opOpenPoolsBlock}, TOLOKA_OPEN_POOLS_OPERATION_OUTPUT_POOLS_NAME,
        {opHitmanComradeBlock}, HITMAN_COMRADE_REALTIME_OPERATION_POOLS_INPUT_NAME);
    wi.connectOperationBlocks(
        {opExportScriptBlock}, EXPORT_SINGLE_FILE_OPERATION_TEXT_OUTPUT_NAME,
        {opHitmanComradeBlock}, HITMAN_COMRADE_REALTIME_OPERATION_SCRIPT_INPUT_NAME);
    wi.connectOperationBlocks(
        {opStopConditionBlock}, SINGLE_OPTION_TO_JSON_OUTPUT_OPERATION_OUTPUT_OUTPUT_NAME,
        {opHitmanComradeBlock}, HITMAN_COMRADE_REALTIME_OPERATION_STOP_CONDITION_INPUT_NAME);

    wi.connectOperationBlocks(
        {opHitmanComradeBlock}, HITMAN_COMRADE_REALTIME_OPERATION_POOLS_OUTPUT_NAME,
        {opGetResultsPoolsBlock}, TOLOKA_GET_ASSIGMENTS_RESULTS_OPERATION_INPUT_POOLS_NAME);

    result.nirvanaBlockGUID = opGetResultsPoolsBlock.GUID;
    REQUIRE(wi.validateWorkflow(),
        "Workflow instance validation failed");
    if (startWorkflowInstance) {
        REQUIRE(wi.startWorkflow(), "Unable to start workflow");
    }
    return result;
}

TolokaCheckerResults tryGetTolokaCheckerResults(
    const std::string& oauthNirvanaToken,
    const std::string& nirvanaWorkflowID,
    const std::string& nirvanaWorkflowInstanceID,
    const std::string& nirvanaBlockGUID
) {
    maps::mrc::nirvana::WorkflowInstance wi(oauthNirvanaToken, nirvanaWorkflowID, nirvanaWorkflowInstanceID);
    switch (wi.getExecutionState()) {
    case maps::mrc::nirvana::ExecutionState::Running:
        return {TolokaCheckerResultsType::Running, json::null};
    case maps::mrc::nirvana::ExecutionState::Failed:
        return {TolokaCheckerResultsType::Failed, json::null};
    case maps::mrc::nirvana::ExecutionState::Success:
        std::string resultsURL = wi.getBlockResultUrl({ nirvanaBlockGUID, "" }, TOLOKA_GET_ASSIGMENTS_RESULTS_OPERATION_OUTPUT_TASKS_NAME);
        return {TolokaCheckerResultsType::Success, json::Value::fromString(download(resultsURL))};
    }
}

} // namespace maps::mrc::toloka_validation
