#pragma once

#include "builder_base.h"
#include "http.h"

namespace NSrvKernel {

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

    explicit TResponseBuilder(TResponse response)
        : Response_(std::move(response))
    {}

public:
    TResponseBuilder() noexcept = default;

    constexpr auto Version11() {
        static_assert(!HaveTag<NPrivate::TVersionTag>,
                      "Response already have version");
        Response_.ResponseLine().MinorVersion = Response_.ResponseLine().MajorVersion = 1;
        return TResponseBuilder<Ts..., NPrivate::TVersionTag>(std::move(Response_));
    }

    constexpr auto Version10() {
        static_assert(!HaveTag<NPrivate::TVersionTag>,
                      "Response already have version");
        Response_.ResponseLine().MajorVersion = 1;
        Response_.ResponseLine().MinorVersion = 0;
        return TResponseBuilder<Ts..., NPrivate::TVersionTag>(std::move(Response_));
    }

    constexpr auto Code(const ui16 code) {
        static_assert(!HaveTag<NPrivate::TCodeTag>,
                      "Response already have status code");
        Response_.ResponseLine().StatusCode = code;
        return TResponseBuilder<Ts..., NPrivate::TCodeTag>(std::move(Response_));
    }

    template <class S>
    constexpr auto Reason(S&& reason) {
        static_assert(!HaveTag<NPrivate::TContentInfoTag>,
                      "Response already have reason string");
        Response_.ResponseLine().Reason = TStringStorage(TString(std::forward<S>(reason)));
        return TResponseBuilder<Ts..., NPrivate::TReasonTag>(std::move(Response_));
    }

    constexpr auto ChunkedTransfer() {
        static_assert(!HaveTag<NPrivate::TContentInfoTag>,
                      "Response already have information about content type");
        Response_.Props().ChunkedTransfer = true;
        Response_.Props().ContentLength.reset();
        return TResponseBuilder<Ts..., NPrivate::TContentInfoTag>(std::move(Response_));
    }

    constexpr auto ContentLength(const ui64 length) {
        static_assert(!HaveTag<NPrivate::TContentInfoTag>,
                      "Response already have information about content type");
        Response_.Props().ContentLength = length;
        Response_.Props().ChunkedTransfer = false;
        return TResponseBuilder<Ts..., NPrivate::TContentInfoTag>(std::move(Response_));
    }

    constexpr auto UpgradeRequested() {
        static_assert(!HaveTag<NPrivate::TContentInfoTag>,
                      "Response already have information about content type");
        Response_.Props().UpgradeRequested = true;
        Response_.Props().ContentLength.reset();
        Response_.Props().ChunkedTransfer = false;
        return TResponseBuilder<Ts..., NPrivate::TContentInfoTag>(std::move(Response_));
    }

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

    operator TResponse() {
        static_assert(HaveTag<NPrivate::TVersionTag> && HaveTag<NPrivate::TCodeTag>,
                      "Response not completed");
        if constexpr (!(... || std::is_same_v<NPrivate::TReasonTag, Ts>)) {
            Response_.ResponseLine().Reason.Clear();
            Response_.ResponseLine().Reason = TStringStorage(HttpCodeStr(Response_.ResponseLine().StatusCode));
        }
        return std::move(Response_);
    }

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

private:
    TResponse Response_;
};

inline TResponseBuilder<> BuildResponse() {
    return TResponseBuilder<>{};
}

}  // namespace NSrvKernel
