#include "request_context_utils.h"
#include "response_stream_utils.h"

#include <balancer/client/experimental/parsers/transparent/transparent_parser.h>

namespace NHttp::NUtils {
void ReadRaw(IRequestContext& context, TInstant deadline, TReadTypedHandler<TResponseFrame> callback) noexcept {
    ReadTyped(context, *Singleton<NParsers::TTransparentChunkParser>(), deadline, std::move(callback));
}

NThreading::TFuture<TReadTypedResult<TResponseFrame>> ReadRaw(IRequestContext& context, TInstant deadline) noexcept {
    auto promise = NThreading::NewPromise<TReadTypedResult<TResponseFrame>>();
    ReadRaw(context, deadline, [=](auto result) mutable {
        promise.SetValue(std::move(result));
    });
    return promise.GetFuture();
}

void ReadAll(IRequestContext& context, TInstant deadline, TReadAllHandler callback) {
    class TReadAllParser: public IResponseParser{
      public:
        using TParsedType = TFullResponse;

        void Parse(IResponseStream& stream, TInstant deadline, THandler callback) noexcept override {
            ReadAll(stream, deadline, [callback = std::move(callback)] (TReadAllResult result) {
                if (result.IsError()) {
                    return callback(std::move(result.Error()));
                }

                if (result.Success().Defined()) {
                    return callback(std::make_any<TFullResponse>(std::move(result.Success().GetRef())));
                }

                return callback(std::any{});
            });
        }
    };
    ReadTyped(context, *Singleton<TReadAllParser>(), deadline, std::move(callback));
}

NThreading::TFuture<TReadAllResult> ReadAll(IRequestContext& context, TInstant deadline) {
    auto promise = NThreading::NewPromise<TReadAllResult>();
    ReadAll(context, deadline, [promise](auto result) mutable {
        promise.SetValue(std::move(result));
    });

    return promise;
}

void ReadHead(IRequestContext& context, TInstant deadline, TReadHeadHandler callback) {
    ReadRaw(context, deadline, [callback = std::move(callback)](TReadRawResult result) {
        if (result.IsError()) {
            return callback(std::move(result.Error()));
        }

        if (!result.Success().Defined()) {
            return callback(TError{"end of stream"});
        }

        if (auto head = std::get_if<TResponseHeadFrame>(&result.Success().GetRef())) {
            return callback(TReadHeadResult{std::move(*head)});
        }

        return callback(TError{"wrong frame received"});
    });
}

NThreading::TFuture<TReadHeadResult> ReadHead(IRequestContext& context, TInstant deadline) {
    auto promise = NThreading::NewPromise<TReadHeadResult>();
    ReadHead(context, deadline, [=](auto result) mutable {
        promise.SetValue(std::move(result));
    });
    return promise.GetFuture();
}

NThreading::TFuture<IRequestContext::TReadResult> Read(IRequestContext& context, IResponseParser& parser, TInstant deadline) {
    auto promise = NThreading::NewPromise<IRequestContext::TReadResult>();
    context.Read(parser, deadline, [promise](auto result) mutable {
        promise.SetValue(std::move(result));
    });
    return promise.GetFuture();
}
}
