#pragma once

#include "response_stream_utils.h"

#include <balancer/client/experimental/base/full_response.h>
#include <balancer/client/experimental/base/request_context.h>
#include <balancer/client/experimental/base/response_parser.h>

#include <library/cpp/threading/future/core/future.h>

namespace NHttp::NUtils {
NThreading::TFuture<IRequestContext::TReadResult> Read(IRequestContext& context, IResponseParser& parser, TInstant deadline);

template<typename T> using TReadTypedResult = TExpected<TMaybe<T>, TError>;
template<typename T> using TReadTypedHandler = std::function<void(TReadTypedResult<T>)>;

/// Метод ReadTyped возвращает только десериализованное представление блока данных.
/// В остальном логика повторяет Read.
/// \param parser реализация парсера
/// \param callback будет вызван, когда данные будут получены и пройдут через парсер
template<CResponseParser TParser>
void ReadTyped(IRequestContext& context, TParser& parser, TInstant deadline, TReadTypedHandler<typename TParser::TParsedType> callback) noexcept {
    context.Read(parser, deadline, [callback = std::move(callback)](IRequestContext::TReadResult result) {
        if (result.IsError()) {
            return callback(std::move(result.Error()));
        }

        if (result.Success().has_value()) {
            auto value = std::any_cast<typename TParser::TParsedType>(std::move(result.Success()));
            return callback(TMaybe<typename TParser::TParsedType>{std::move(value)});
        }

        callback(TMaybe<typename TParser::TParsedType>{});
    });
}

using TReadRawResult = TReadTypedResult<TResponseFrame>;

/// ReadRaw возвращает только сырые http фреймы.
void ReadRaw(IRequestContext& context, TInstant deadline, TReadTypedHandler<TResponseFrame> callback) noexcept;

/// Тот же ReadRaw, только ответ возвращает результат через TFuture
NThreading::TFuture<TReadTypedResult<TResponseFrame>> ReadRaw(IRequestContext& context, TInstant deadline) noexcept;

/// ReadAll читает все данные из IRequestContext
void ReadAll(IRequestContext& context, TInstant deadline, TReadAllHandler callback);

/// Тот же ReadAll, только ответ возвращает результат через TFuture
NThreading::TFuture<TReadAllResult> ReadAll(IRequestContext& context, TInstant deadline);

using TReadHeadResult = TExpected<TResponseHeadFrame, TError>;
using TReadHeadHandler = std::function<void(TReadHeadResult)>;

/// ReadHead читает заголовки из IRequestContext
void ReadHead(IRequestContext& context, TInstant deadline, TReadHeadHandler callback);

/// Тот же ReadHead, только ответ возвращает результат через TFuture
NThreading::TFuture<TReadHeadResult> ReadHead(IRequestContext& context, TInstant deadline);
}
