#include "client_impl.h"
#include "magic_strings.h"
#include "assignment_impl.h"
#include <yandex/maps/mrc/toloka_client/filter.h>
#include <yandex/maps/mrc/toloka_client/assignment.h>
#include <maps/libs/json/include/exception.h>

namespace maps {
namespace mrc {
namespace toloka {
namespace io {

namespace {

constexpr size_t DEFAULT_PAGE_LIMIT = 50;

} // anonymous namespace

json::Value TolokaClient::Impl::requestAssignmentsPage(
    const Filter& filter,
    size_t limit) const
{
    auto httpClient = makeHttpClient();
    http::URL url = schema_ + host_ + "/api/v1/assignments";

    auto response = retry([&](){
        http::Request request(*httpClient, http::GET, url);
        addAuthHeader(request);
        request.addParam(PARAM_SORT, FIELD_ID);
        request.addParam(PARAM_LIMIT, std::to_string(limit));
        filter.apply<Assignment>(request);

        auto response = performRequestChecked(request, url);
        return response;
    });

    try {
        return json::Value::fromStream(response.body());
    } catch (const maps::json::ParserError& e) {
        throw ServerError() << "Failed to parse server response, url: " << url
                << ": " << e.what();
    }
}


AssignmentsResponse
TolokaClient::Impl::getAssignments(Filter filter) const
{
    bool hasMore = true;
    std::string lastId;
    Assignments assignments;

    while (hasMore) {
        if (!lastId.empty()) {
            filter.idGreaterThan(lastId);
        }

        auto jsonBody = requestAssignmentsPage(filter, DEFAULT_PAGE_LIMIT);
        hasMore = jsonBody[FIELD_HAS_MORE].as<bool>();

        for (const auto& item : jsonBody[FIELD_ITEMS]) {
            assignments.push_back(PImplFactory::create<Assignment>(item));
        }
        if (!assignments.empty()) {
            lastId = assignments.back().id();
        }
    }
    return PImplFactory::create<AssignmentsResponse>(std::move(assignments), false);
}


AssignmentsResponse
TolokaClient::Impl::getAssignments(const Filter& filter, size_t limit) const
{
    Assignments assignments;
    auto jsonBody = requestAssignmentsPage(filter, limit);
    bool hasMore = jsonBody[FIELD_HAS_MORE].as<bool>();
    for (const auto& item : jsonBody[FIELD_ITEMS]) {
        assignments.push_back(PImplFactory::create<Assignment>(item));
    }
    return PImplFactory::create<AssignmentsResponse>(std::move(assignments), hasMore);
}

Assignment
TolokaClient::Impl::getAssignment(const std::string& assignmentId) const
{
    auto httpClient = makeHttpClient();
    http::URL url = schema_ + host_ + "/api/v1/assignments/" + assignmentId;

    auto response = retry([&](){
        http::Request request(*httpClient, http::GET, url);
        addAuthHeader(request);

        return performRequestChecked(request, url);
    });

    try {
        auto jsonBody = json::Value::fromStream(response.body());
        return PImplFactory::create<Assignment>(jsonBody);
    } catch (const maps::json::ParserError& e) {
        throw ServerError() << "Failed to parse server response, url: " << url
                << ": " << e.what();
    }
}

void TolokaClient::Impl::setAssignmentStatus(
    const std::string& assignmentId,
    AssignmentStatus status,
    const std::string& comment) const
{
    json::Builder builder;
    builder << [=](json::ObjectBuilder b) {
        b[FIELD_STATUS] = boost::lexical_cast<std::string>(status);
        b[FIELD_PUBLIC_COMMENT] = comment;
    };

    auto httpClient = makeHttpClient();
    http::URL url = schema_ + host_ + "/api/v1/assignments/" + assignmentId;

    retry([&](){
        http::Request request(*httpClient, http::PATCH, url);
        addAuthHeader(request);
        addContentTypeHeader(request);
        request.setContent(builder.str());
        performRequestChecked(request, url);
    });
}

} // namespace io
} // namespace toloka
} // namespace mrc
} // namespace maps

