#ifndef QUICHE_HTTP2_ADAPTER_HEADER_VALIDATOR_H_
#define QUICHE_HTTP2_ADAPTER_HEADER_VALIDATOR_H_

#include <bitset>
#include <optional>
#include <string>
#include <vector>

#include "absl/strings/string_view.h"
#include "quiche/http2/adapter/header_validator_base.h"
#include "quiche/common/platform/api/quiche_export.h"

namespace http2 {
namespace adapter {

class QUICHE_EXPORT HeaderValidator : public HeaderValidatorBase {
 public:
  HeaderValidator() = default;

  void StartHeaderBlock() override;

  HeaderStatus ValidateSingleHeader(absl::string_view key,
                                    absl::string_view value) override;

  // Returns true if all required pseudoheaders and no extra pseudoheaders are
  // present for the given header type.
  bool FinishHeaderBlock(HeaderType type) override;

  // Returns whether `name` is valid according to RFC 9110 Section 5.1.
  // ':' is an invalid character, therefore HTTP/2 pseudo-headers must be
  // validated with the leading colon removed.
  static bool IsValidHeaderName(absl::string_view name);

  // Returns whether `value` is valid according to RFC 9110 Section 5.5 and
  // RFC 9113 Section 8.2.1.
  static bool IsValidHeaderValue(absl::string_view value,
                                 ObsTextOption ops_text_option);

  // Returns whether `authority` is valid according to RFC 3986 Section 3.2.
  static bool IsValidAuthority(absl::string_view authority);

  // Returns whether `path` is valid according to RFC 3986 Section 3.3. May
  // contain the query part of a URI.
  static bool IsValidPath(absl::string_view path, bool allow_fragment);

 private:
  enum ContentLengthStatus {
    CONTENT_LENGTH_OK,
    CONTENT_LENGTH_SKIP,  // Used to handle duplicate content length values.
    CONTENT_LENGTH_ERROR,
  };
  ContentLengthStatus HandleContentLength(absl::string_view value);
  bool ValidateAndSetAuthority(absl::string_view authority);

  enum PseudoHeaderTag {
    TAG_AUTHORITY = 0,
    TAG_METHOD,
    TAG_PATH,
    TAG_PROTOCOL,
    TAG_SCHEME,
    TAG_STATUS,
    TAG_UNKNOWN_EXTRA,
    TAG_ENUM_SIZE,
  };
  void RecordPseudoHeader(PseudoHeaderTag tag);

  using PseudoHeaderTagSet = std::bitset<TAG_ENUM_SIZE>;

  enum PseudoHeaderState {
    STATE_AUTHORITY_IS_NONEMPTY,
    STATE_METHOD_IS_OPTIONS,
    STATE_METHOD_IS_CONNECT,
    STATE_PATH_IS_EMPTY,
    STATE_PATH_IS_STAR,
    STATE_PATH_INITIAL_SLASH,
    STATE_ENUM_SIZE,
  };
  using PseudoHeaderStateSet = std::bitset<STATE_ENUM_SIZE>;

  static bool ValidateRequestHeaders(
      const PseudoHeaderTagSet& pseudo_headers,
      const PseudoHeaderStateSet& pseudo_header_state,
      bool allow_extended_connect);
  static bool ValidateRequestTrailers(const PseudoHeaderTagSet& pseudo_headers);
  static bool ValidateResponseHeaders(const PseudoHeaderTagSet& pseudo_headers);
  static bool ValidateResponseTrailers(
      const PseudoHeaderTagSet& pseudo_headers);

  PseudoHeaderTagSet pseudo_headers_;
  PseudoHeaderStateSet pseudo_header_state_;

  std::string authority_;
};

}  // namespace adapter
}  // namespace http2

#endif  // QUICHE_HTTP2_ADAPTER_HEADER_VALIDATOR_H_
