#include "tool.h"
#include "print.h"

#include <maps/libs/common/include/retry.h>
#include <maps/libs/http/include/request.h>
#include <yandex/maps/proto/firmware_updater/storage/rollout.pb.h>

namespace proto = yandex::maps::proto::firmware_updater::storage;

namespace maps::fw_updater::storage::cli {

namespace {

void createRollout(
    Context& ctx,
    const std::string& hardware,
    const std::string& slot,
    const std::string& branch,
    const std::string& version)
{
    http::URL url(ctx.baseUrl());
    url.setPath("/v2/rollout/create")
        .addParam("hardware", hardware)
        .addParam("slot", slot)
        .addParam("branch", branch)
        .addParam("version", version);

    auto response = common::retry([&] {
            http::Request request(ctx.httpClient(), http::POST, url);
            addAuth(request, ctx);
            auto response = request.perform();
            return response;
        },
        ctx.retryPolicy(),
        ctx.responseValidator(),
        ctx.errorReporter());

    REQUIRE(response.status() == 201, errorMessage(response));
    proto::Rollout rollout;
    Y_PROTOBUF_SUPPRESS_NODISCARD rollout.ParseFromString(TString(response.readBody()));
    print(rollout);
}

void expandRollout(
    Context& ctx,
    const std::string& rolloutId,
    const uint16_t& percent)
{
    http::URL url(ctx.baseUrl());
    url.setPath("/v2/rollout/expand")
        .addParam("rollout_id", rolloutId)
        .addParam("percent", percent);

    auto response = common::retry([&] {
            http::Request request(ctx.httpClient(), http::POST, url);
            addAuth(request, ctx);
            auto response = request.perform();
            return response;
        },
        ctx.retryPolicy(),
        ctx.responseValidator(),
        ctx.errorReporter());

    REQUIRE(response.status() == 200, errorMessage(response));
    proto::Rollout rollout;
    Y_PROTOBUF_SUPPRESS_NODISCARD rollout.ParseFromString(TString(response.readBody()));
    print(rollout);
}

void changeRolloutState(
    Context& ctx,
    const std::string& rolloutId,
    const std::string& pathSuffix)
{
    http::URL url(ctx.baseUrl());
    url.setPath("/v2/rollout/" + pathSuffix)
        .addParam("rollout_id", rolloutId);

    auto response = common::retry([&] {
            http::Request request(ctx.httpClient(), http::POST, url);
            addAuth(request, ctx);
            auto response = request.perform();
            return response;
        },
        ctx.retryPolicy(),
        ctx.responseValidator(),
        ctx.errorReporter());

    REQUIRE(response.status() == 200, errorMessage(response));
    proto::Rollout rollout;
    Y_PROTOBUF_SUPPRESS_NODISCARD rollout.ParseFromString(TString(response.readBody()));
    print(rollout);
}

void getTargetRollout(
    Context& ctx,
    const std::string& hardware,
    const std::optional<std::string>& slot,
    const std::optional<std::string>& branch)
{
    http::URL url(ctx.baseUrl());
    url.setPath("/v2/rollout/target")
        .addParam("hardware", hardware);

    if (slot) {
        url.addParam("slot", *slot);
    }
    if (branch) {
        url.addParam("branch", *branch);
    }

    auto response = common::retry([&] {
            http::Request request(ctx.httpClient(), http::GET, url);
            addAuth(request, ctx);
            auto response = request.perform();
            return response;
        },
        ctx.retryPolicy(),
        ctx.responseValidator(),
        ctx.errorReporter());

    REQUIRE(response.responseClass() == http::ResponseClass::Success,
            errorMessage(response));
    proto::RolloutsList rolloutsList;
    Y_PROTOBUF_SUPPRESS_NODISCARD rolloutsList.ParseFromString(TString(response.readBody()));
    print(rolloutsList);
}

void getRolloutHistory(
    Context& ctx,
    const std::string& hardware,
    const std::optional<std::string>& slot,
    const std::optional<std::string>& branch,
    const std::optional<uint32_t>& results,
    const std::optional<uint32_t>& skip)
{
    http::URL url(ctx.baseUrl());
    url.setPath("/v2/rollout/history")
        .addParam("hardware", hardware);

    if (slot) {
        url.addParam("slot", *slot);
    }
    if (branch) {
        url.addParam("branch", *branch);
    }
    if (results) {
        url.addParam("results", *results);
    }
    if (skip) {
        url.addParam("skip", *skip);
    }

    auto response = common::retry([&] {
            http::Request request(ctx.httpClient(), http::GET, url);
            addAuth(request, ctx);
            auto response = request.perform();
            return response;
        },
        ctx.retryPolicy(),
        ctx.responseValidator(),
        ctx.errorReporter());

    REQUIRE(response.responseClass() == http::ResponseClass::Success,
            errorMessage(response));
    proto::RolloutsList rolloutsList;
    Y_PROTOBUF_SUPPRESS_NODISCARD rolloutsList.ParseFromString(TString(response.readBody()));
    print(rolloutsList);
}

} // namespace


void handleRolloutCommand(
    Context& ctx,
    const Options& options)
{
    ASSERT(options.command() == Command::Rollout);

    auto subCommand = options.subCommand<RolloutSubCommand>();

    switch(subCommand) {
        case RolloutSubCommand::Create:
            createRollout(ctx, options.hardware(), options.slot(), options.branch(), options.version());
            break;
        case RolloutSubCommand::Expand:
            expandRollout(ctx, options.rolloutId(), options.percent());
            break;
        case RolloutSubCommand::Pause:
            changeRolloutState(ctx, options.rolloutId(), "pause");
            break;
        case RolloutSubCommand::Resume:
            changeRolloutState(ctx, options.rolloutId(), "resume");
            break;
        case RolloutSubCommand::Target:
            getTargetRollout(ctx, options.hardware(),
                             options.slot.optional(), options.branch.optional());
            break;
        case RolloutSubCommand::History:
            getRolloutHistory(ctx, options.hardware(),
                              options.slot.optional(), options.branch.optional(),
                              options.results.optional(), options.skip.optional());
            break;
    }
}

} // namespace maps::fw_updater::storage::cli
