#pragma once

#include "parse_options.h"

#include <balancer/kernel/http/parser/pico_parser_helpers/request_head_ending_scanner.h>

#include <balancer/kernel/helpers/errors.h>
#include <balancer/kernel/http/string_storage/string_storage.h>
#include <balancer/kernel/io/iobase.h>
#include <balancer/kernel/memory/chunks.h>

#include <array>

namespace NSrvKernel {

struct TBaseProperties;
class THeaders;
class TRequest;
class TRequestLine;
class TResponse;
class TResponseLine;

static constexpr size_t MaxHeaderCount = 2048;

namespace NHttpParserInternals {

template <typename TConcreteParser>
class TParseContext {
public:
    TParseContext(TChunkList* unparsed, TString* buffer, bool scanHeaders = false) noexcept
        : Unparsed_(unparsed)
        , Buffer_(buffer)
        , HeadScanner_(Buffer_, scanHeaders)
    {
        Y_ASSERT(Buffer_ && Buffer_->Empty());
    }

    TError Read(IIoInput* in, TInstant deadline) noexcept {
        TChunkList lst;
        TChunkList body;
        bool requestFinished = false;

        while (true) {
            Y_PROPAGATE_ERROR(in->Recv(lst, deadline));
            size_t read = lst.size();
            if (read == 0) {
                return OnIncompleteInput();
            }

            Y_PROPAGATE_ERROR(HeadScanner_.ProcessNewDataChunkAndSeparateBody(
                std::move(lst), body).AssignTo(requestFinished));
            if (requestFinished) {
                bool parsed;
                auto error = ParseChunk().AssignTo(parsed);
                Unparsed_->Append(std::move(body));
                if (error) {
                    return error;
                }
                if (parsed) {
                    return {};
                } else {
                    return Y_MAKE_ERROR(THttpParseError(
                        TChunkList("can not parse message"), HTTP_BAD_REQUEST));
                }
            }
        }

        return OnIncompleteInput();
    }

    TErrorOr<bool> ParseChunk() noexcept {
        return Derived().DoParseChunk(*Buffer_);
    }

    TError OnIncompleteInput() noexcept {
        return Derived().DoOnIncompleteInput();
    }

private:
    TConcreteParser& Derived() noexcept {
        return *static_cast<TConcreteParser*>(this);
    }

protected:
    TChunkList* const Unparsed_ = nullptr;
    TString* const Buffer_ = nullptr;

    // parser internals
    int cs = 0;
    TStringStorage* curTok = nullptr;

private:
    TMessageHeadEndingScanner HeadScanner_;
};

}  // namespace NHttpParserInternals

template <typename TMessage>
class TParseMessagePicoContext : public NHttpParserInternals::TParseContext<TParseMessagePicoContext<TMessage>> {
    using THeadLine = typename TMessage::THeadLine;

public:
    TParseMessagePicoContext(
        TChunkList* unparsed, TString* buffer, TMessage* message, THttpParseOptions options
    ) noexcept;

    TErrorOr<bool> DoParseChunk(TString buffer) noexcept;

    TError DoOnIncompleteInput() noexcept;

private:
    constexpr TStringBuf MessageTypeName() const noexcept;

private:
    TMessage* const Message_ = nullptr;
    THeadLine* const HeadLine_ = nullptr;
    TBaseProperties* const Properties_ = nullptr;
    THeaders* const Headers_ = nullptr;
    THttpParseOptions ParseOptions_;
};

}  // namespace NSrvKernel
