#pragma once

#include <yandex/maps/mrc/toloka_client/assignment.h>
#include <yandex/maps/mrc/toloka_client/filter.h>
#include <yandex/maps/mrc/toloka_client/operation.h>
#include <yandex/maps/mrc/toloka_client/pool.h>
#include <yandex/maps/mrc/toloka_client/task.h>
#include <yandex/maps/mrc/toloka_client/task_suite.h>
#include <yandex/maps/mrc/toloka_client/requester.h>

#include <maps/libs/common/include/exception.h>
#include <maps/libs/common/include/pimpl_utils.h>

#include <chrono>
#include <map>
#include <memory>
#include <string>
#include <vector>

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

struct Error: maps::Exception
{
    using Exception::Exception;
};

struct ClientError: Error
{
    using Error::Error;
};

struct ServerError: Error
{
    using Error::Error;
};

using StringMap = std::map<std::string, std::string>;

/**
 * Client to create/get/update Toloka entities (Pool, Task, TaskSuite etc).
 * All methods except updating settings are thread-safe.
 */
class TolokaClient
{
public:
    TolokaClient(const std::string& host, const std::string& authHeader);

    // Settings
    const std::string& schema() const;
    TolokaClient& setSchema(const std::string& schema);

    std::chrono::milliseconds timeout() const;
    TolokaClient& setTimeout(std::chrono::milliseconds timeout);

    size_t maxRequestAttempts() const;
    TolokaClient& setMaxRequestAttempts(size_t maxRequestAttempts);

    std::chrono::milliseconds retryInitialTimeout() const;
    TolokaClient& setRetryInitialTimeout(std::chrono::milliseconds timeout);

    double retryTimeoutBackoff() const;
    TolokaClient& setRetryTimeoutBackoff(double retryTimeoutBackoff);

    /*******************/
    /* Pool operations */
    /*******************/

    /// Get all pools by filter, sorted by pool id
    PoolsResponse getPools(Filter /*move*/filter) const;

    /// Get maximum \p limit pools by filter, sorted by pool id
    /// To get next page, request with filter idGreaterThan(lastId),
    /// where lastId is the last pool id from the previous page
    PoolsResponse getPools(const Filter& filter, size_t limit) const;

    Pool getPool(const std::string& poolId) const;

    Pool createPool(const PoolCreationParams& poolCreationParams) const;

    Operation openPool(const std::string& poolId) const;
    Operation closePool(const std::string& poolId) const;

    /// Pool must be in status Closed
    Operation archivePool(const std::string& poolId) const;

    // Assume that pools shall be created manually,
    // so we don't have createPool() / updatePool() method

    /*******************/
    /* Task operations */
    /*******************/

    /// Get all tasks by filter, sorted by task id
    TasksResponse getTasks(Filter /*move*/filter) const;

    /// Get maximum \p limit tasks by filter, sorted by task id
    /// To get next page, request with filter idGreaterThan(lastId),
    /// where lastId is the last task id from the previous page
    TasksResponse getTasks(const Filter& filter, size_t limit) const;

    Task getTask(const std::string& taskId) const;

    Task postTask(const TaskCreationParams& taskCreationParams) const;

    /**
     * @param params - path parameters in http request according to
     * https://tech.yandex.ru/toloka/api/doc/concepts/create-task-docpage/
     */
    Task postTask(const TaskCreationParams& taskCreationParams,
                  const StringMap& params) const;

    TasksPostResponse postTasks(
        const std::vector<TaskCreationParams>& taskCreationParamsVec) const;

    TasksPostResponse postTasks(
        const std::vector<TaskCreationParams>& taskCreationParamsVec,
        const StringMap& params) const;

    /// @param overlap must be positive
    void setTaskOverlap(const std::string& taskId, size_t overlap) const;

    void stopAssigningTask(const std::string& taskId) const;

    /*************************/
    /* Task Suite operations */
    /*************************/

    /// Get all task suites by filter, sorted by task id
    TaskSuitesResponse getTaskSuites(Filter /*move*/filter) const;

    /// Get maximum \p limit task suites by filter, sorted by task suite id
    /// To get next page, request with filter idGreaterThan(lastId),
    /// where lastId is the last task suite id from the previous page
    TaskSuitesResponse getTaskSuites(const Filter& filter, size_t limit) const;

    TaskSuite getTaskSuite(const std::string& taskSuiteId) const;

    TaskSuite postTaskSuite(
        const TaskSuiteCreationParams& taskSuiteCreationParams) const;

    TaskSuitesPostResponse postTaskSuites(
        const std::vector<TaskSuiteCreationParams>& taskSuiteCreationParamsVec) const;

    /**
     * @param params - path parameters in http request according to
     * https://tech.yandex.ru/toloka/api/doc/concepts/create-task-suite-docpage/
     */
    TaskSuite postTaskSuite(
        const TaskSuiteCreationParams& taskSuiteCreationParams,
        const StringMap& params) const;

    TaskSuitesPostResponse postTaskSuites(
        const std::vector<TaskSuiteCreationParams>& taskSuiteCreationParamsVec,
        const StringMap& params) const;

    /// @param overlat must be positive
    void setTaskSuiteOverlap(const std::string& taskSuiteId,
                             size_t overlap) const;
    void stopAssigningTaskSuite(const std::string& taskSuiteId) const;

    /*************************/
    /* Assignment operations */
    /*************************/

    /// Get all assignments by filter, sorted by task id
    AssignmentsResponse getAssignments(Filter /*move*/filter) const;

    /// Get maximum \p limit assignments by filter, sorted by assignment id
    /// To get next page, request with filter idGreaterThan(lastId),
    /// where lastId is the last assignment id from the previous page
    AssignmentsResponse getAssignments(const Filter& filter,
                                       size_t limit) const;

    Assignment getAssignment(const std::string& assignmentId) const;

    void acceptAssignment(const std::string& assignmentId,
                          const std::string& comment = std::string()) const;

    void rejectAssignment(const std::string& assignmentId,
                          const std::string& comment) const;


    /*************************/
    /* Attachment operations */
    /*************************/

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


    /*************************/
    /* Requester operations */
    /*************************/

    Requester getRequester() const;

    MOVABLE_PIMPL_DECLARATIONS(TolokaClient)
};

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

