#pragma once

#include <yandex/maps/mrc/toloka_client/client.h>
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/algorithm/retry.h>
#include <maps/libs/http/include/http.h>
#include <maps/libs/json/include/value.h>

#include <chrono>

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

namespace cr = std::chrono;

class TolokaClient::Impl
{
public:
    Impl(const std::string& host, const std::string& authHeader)
        : host_(host)
        , authHeader_(authHeader)
        , schema_("https://")
    {
    }

    PoolsResponse getPools(Filter /*move*/filter) const;
    PoolsResponse getPools(const Filter& filter, size_t limit) const;
    Pool getPool(const std::string& poolId) const;
    Pool createPool(const PoolCreationParams& poolCreationParams) const;
    Operation setPoolStatus(const std::string& poolId,
                            PoolStatus desiredStatus) const;

    TasksResponse getTasks(Filter /*move*/filter) const;
    TasksResponse getTasks(const Filter& filter, size_t limit) const;
    Task getTask(const std::string& taskId) const;
    Task postTask(const TaskCreationParams& taskCreationParams,
                  const StringMap& params) const;
    TasksPostResponse postTasks(
        const std::vector<TaskCreationParams>& taskCreationParamsVec,
        const StringMap& params) const;
    Task patchTask(const std::string& taskId, const std::string& data) const;
    Task stopAssigningTask(const std::string& taskId) const;

    TaskSuitesResponse getTaskSuites(Filter /*move*/filter) const;
    TaskSuitesResponse getTaskSuites(const Filter& filter, size_t limit) const;
    TaskSuite getTaskSuite(const std::string& taskSuiteId) const;
    TaskSuite postTaskSuite(const TaskSuiteCreationParams& taskSuiteCreationParams,
                            const StringMap& params) const;
    TaskSuitesPostResponse postTaskSuites(
        const std::vector<TaskSuiteCreationParams>& taskSuiteCreationParamsVec,
        const StringMap& params) const;
    TaskSuite patchTaskSuite(const std::string& taskSuiteId,
                             const std::string& data) const;
    TaskSuite stopAssigningTaskSuite(const std::string& taskSuiteId) const;

    AssignmentsResponse getAssignments(Filter /*move*/filter) const;
    AssignmentsResponse getAssignments(const Filter& filter, size_t limit) const;
    Assignment getAssignment(const std::string& assignmentId) const;
    void setAssignmentStatus(const std::string& assignmentId,
                             AssignmentStatus status,
                             const std::string& comment) const;

    std::string getAttachment(const std::string& attachmentId) const;

    Requester getRequester() const;

    std::string host_;
    std::string authHeader_;
    std::string schema_;
    cr::milliseconds timeout_ = cr::milliseconds(0);
    size_t maxRequestAttempts_ = 1;
    cr::milliseconds retryInitialTimeout_ = cr::milliseconds(0);
    double retryTimeoutBackoff_ = 1.0;

private:
    std::unique_ptr<http::Client> makeHttpClient() const;
    void addAuthHeader(http::Request& request) const;
    void addContentTypeHeader(http::Request& request) const;
    http::Response performRequestChecked(http::Request& request,
                                          const http::URL& url) const;

    template <typename Functor>
    auto retry(Functor&& func) const -> decltype(func())
    {
        return common::retryOnException<ServerError>(
            common::RetryPolicy()
                .setInitialTimeout(retryInitialTimeout_)
                .setMaxAttempts(maxRequestAttempts_)
                .setTimeoutBackoff(retryTimeoutBackoff_),
            func);
    }

    json::Value requestPoolsPage(const Filter& filter, size_t limit) const;
    json::Value requestTasksPage(const Filter& filter, size_t limit) const;
    json::Value requestTaskSuitesPage(const Filter& filter, size_t limit) const;
    json::Value requestAssignmentsPage(const Filter& filter, size_t limit) const;
};

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