#ifndef _TILE_NET_HTTP_HTTP_HEADERS_H
#define _TILE_NET_HTTP_HTTP_HEADERS_H

#pragma once
#include "tile/base/internal/case_insensitive_hash_map.h"
#include "tile/base/optional.h"
#include "tile/base/slice.h"
#include "tile/base/string.h"
#include <deque>
#include <memory>
#include <unordered_map>
#include <vector>

namespace tile {
class HttpHeaders;

namespace http {
bool
ParseMessagePartial(std::pair<std::unique_ptr<char[]>, std::size_t> &&storage, Slice *start_line, HttpHeaders *headers);
}

class HttpHeaders {
    using NonowningFields = std::vector<std::pair<Slice, Slice>>;

public:
    using iterator                         = NonowningFields::iterator;
    using const_iterator                   = NonowningFields::const_iterator;

    HttpHeaders()                          = default;
    HttpHeaders(HttpHeaders &&)            = default;
    HttpHeaders &operator=(HttpHeaders &&) = default;

    HttpHeaders(const HttpHeaders &);
    HttpHeaders &operator=(const HttpHeaders &);

    iterator begin() noexcept { return fields_.begin(); }

    iterator end() noexcept { return fields_.end(); }

    const_iterator begin() const noexcept { return fields_.begin(); }

    const_iterator end() const noexcept { return fields_.end(); }

    bool contains(Slice key) const noexcept
    {
        if (header_idx_.find(key) != header_idx_.end()) { return true; }

        for (const auto &field : fields_) {
            if (EqualsIgnoreCase(field.first, key)) { return true; }
        }
        return false;
    }

    void clear() noexcept;

    std::optional<Slice> TryGet(Slice key) const noexcept
    {
        auto iter = header_idx_.find(key);
        if (iter == header_idx_.end()) { return std::nullopt; }
        return fields_[iter->second].second;
    }

    template<typename T>
    std::optional<T> TryGet(Slice key) const noexcept
    {
        auto p = TryGet(key);
        if (p) { return TryParse<T>(*p); }
        return std::nullopt;
    }

    std::vector<Slice> TryGetMultiple(Slice key) const noexcept;
    void Set(std::string key, std::string value) noexcept;
    void Append(std::string key, std::string value) noexcept;
    void Append(std::initializer_list<std::pair<Slice, Slice>> fields) noexcept;
    bool Remove(Slice key) noexcept;
    std::string ToString() const;

private:
    friend bool ::tile::http::ParseMessagePartial(std::pair<std::unique_ptr<char[]>, std::size_t> &&storage,
                                                  Slice *start_line,
                                                  HttpHeaders *headers);
    friend bool Test();
    Slice RetrieveHeaderStorage(std::pair<std::unique_ptr<char[]>, std::size_t> &&s);
    void RetrieveFields(NonowningFields &&fields);

private:
    std::pair<std::unique_ptr<char[]>, std::size_t> header_block_;
    std::deque<std::string> owning_strs_2_;
    // <HeaderName, FieldIndex>
    internal::CaseInsensitiveHashMap<Slice, std::size_t> header_idx_;
    NonowningFields fields_;
};
}// namespace tile

#endif// _TILE_NET_HTTP_HTTP_HEADERS_H
