#pragma once

#include "builder_base.h"
#include "http.h"
#include <util/string/split.h>

namespace NSrvKernel {

template <class... Ts>
class TRequestBuilder {
private:
    template <class... Us>
    friend class TRequestBuilder;

    explicit TRequestBuilder(TRequest request)
        : Request_(std::move(request))
    {}

public:
    TRequestBuilder() noexcept = default;

    constexpr auto Version11() {
        static_assert(!HaveTags<NPrivate::TVersionTag>, "Request already have version");
        Request_.RequestLine().MinorVersion = Request_.RequestLine().MajorVersion = 1;
        return TRequestBuilder<Ts..., NPrivate::TVersionTag>(std::move(Request_));
    }

    constexpr auto Version10() {
        static_assert(!HaveTags<NPrivate::TVersionTag>, "Request already have version");
        Request_.RequestLine().MajorVersion = 1;
        Request_.RequestLine().MinorVersion = 0;
        return TRequestBuilder<Ts..., NPrivate::TVersionTag>(std::move(Request_));
    }

    constexpr auto Method(EMethod method) {
        static_assert(!HaveTags<NPrivate::TMethodTag>, "Request already have method");
        Request_.RequestLine().Method = method;
        return TRequestBuilder<Ts..., NPrivate::TMethodTag>(std::move(Request_));
    }

    template <class S>
    constexpr auto Path(S&& path) {
        static_assert(!HaveTags<NPrivate::TPathTag>, "Request already have path");
        Request_.RequestLine().Path = TStringStorage(TString(std::forward<S>(path)));
        return TRequestBuilder<Ts..., NPrivate::TPathTag>(std::move(Request_));
    }

    template <class S>
    constexpr auto Cgi(S&& cgi) {
        static_assert(!HaveTags<NPrivate::TCgiTag>, "Request already have cgi");
        Request_.RequestLine().CGI = TStringStorage(TString(std::forward<S>(cgi)));
        return TRequestBuilder<Ts..., NPrivate::TCgiTag>(std::move(Request_));
    }

    auto Uri(TStringBuf uri) {
        static_assert(!HaveTags<NPrivate::TPathTag>, "Request already have path");
        static_assert(!HaveTags<NPrivate::TCgiTag>, "Request already have cgi");
        const auto pos = uri.find('?');
        Request_.RequestLine().Path = TStringStorage(TString(uri.SubStr(0, pos)));
        Request_.RequestLine().CGI = TStringStorage(TString(uri.SubStr(pos)));
        return TRequestBuilder<Ts..., NPrivate::TCgiTag, NPrivate::TPathTag>(std::move(Request_));
    }

    template <class Key, class Value>
    TRequestBuilder& Header(Key&& key, Value&& value) {
        Request_.Headers().Add(std::forward<Key>(key), std::forward<Value>(value));
        return *this;
    }

    operator TRequest() {
        static_assert(HaveTags<NPrivate::TVersionTag, NPrivate::TMethodTag, NPrivate::TPathTag>,
                      "Request not completed");
        return std::move(Request_);
    }

private:
    template <class Tag>
    static constexpr bool HaveTag = (... || std::is_same_v<Tag, Ts>);

    template <class... Tags>
    static constexpr bool HaveTags = (... && HaveTag<Tags>);

private:
    TRequest Request_;
};

inline TRequestBuilder<> BuildRequest() {
    return TRequestBuilder<>{};
}

inline auto BuildRequest11() {
    return TRequestBuilder<>().Version11();
}

}  // namespace NSrvKernel
