#ifndef TILE_NET_INTERNAL_HTTP_TASK_H
#define TILE_NET_INTERNAL_HTTP_TASK_H

#pragma once

#include "tile/base/buffer.h"
#include "tile/base/expected.h"
#include "tile/base/status.h"
#include "tile/net/http/types.h"

#include "curl/curl.h"

namespace tile {

namespace internal {
class HttpTaskCallContext;

class HttpTask {
public:
    HttpTask();
    ~HttpTask();
    HttpTask(HttpTask &&) noexcept;

    void SetUserAgent(const std::string &user_agent);
    void SetMethod(HttpMethod method);
    void SetUrl(const std::string &url);
    void SetTimeout(std::chrono::nanoseconds timeout);
    void SetBody(std::string body);
    void SetBody(NoncontiguousBuffer body);
    void AddHeader(const std::string &header);

    CURL *GetNativeHandle() noexcept;

private:
    friend class HttpEngine;
    HttpMethod method_ = HttpMethod::Unspecified;
    PooledPtr<HttpTaskCallContext> ctx_;
    std::unique_ptr<curl_slist, decltype(&curl_slist_free_all)> hdrs_{nullptr, &curl_slist_free_all};
};

class HttpTaskCompletion {
public:
    explicit HttpTaskCompletion(HttpTaskCallContext *ctx);
    ~HttpTaskCompletion();
    HttpTaskCompletion(HttpTaskCompletion &&) noexcept;

    HttpStatus status() noexcept;
    HttpVersion version() noexcept;
    std::vector<std::string> *headers() noexcept;
    NoncontiguousBuffer *body() noexcept;

    CURL *GetNativeHandle() noexcept;

private:
    PooledPtr<HttpTaskCallContext> ctx_;
    NoncontiguousBuffer body_;
};

struct HttpTaskCallContext {
    HttpTaskCallContext();
    ~HttpTaskCallContext();
    void Reset();

    CURL *curl_handler;
    std::unique_ptr<NoncontiguousBufferBuilder> body;
    std::vector<std::string> headers;
    std::function<void(std::expected<HttpTaskCompletion, Status>)> done;
    std::unique_ptr<curl_slist, decltype(&curl_slist_free_all)> hdrs;

    struct RequestBody {
        NoncontiguousBuffer buffer;
        NoncontiguousBuffer::const_iterator current_block;
        std::size_t buffer_block_inner_pos = 0;
    };

    RequestBody request_body;
};

}// namespace internal
}// namespace tile

namespace tile {
template<>
struct PoolTraits<internal::HttpTaskCallContext> {
    static constexpr auto kType                          = PoolType::Disabled;
    static constexpr std::size_t kLowWaterMark           = 2;
    static constexpr std::size_t kHighWaterMark          = 4;
    static constexpr std::chrono::seconds kMaxIdle       = std::chrono::seconds(10);
    static constexpr std::size_t kMinimumThreadCacheSize = 1024;
    static constexpr std::size_t kTransferBatchSize      = 2048;

    static void OnPut(internal::HttpTaskCallContext *cw) { cw->Reset(); }
};
}// namespace tile
#endif// TILE_NET_INTERNAL_HTTP_TASK_H
