#pragma once

#include <memory>

#include <simdutf.h>
#include <weaseljson/weaseljson.h>

#include "commit_request_parser.hpp"
#include "json_token_enum.hpp"

/**
 * @brief JSON-specific implementation of CommitRequestParser.
 *
 * This parser uses the weaseljson library to parse JSON-formatted
 * commit requests into CommitRequest objects.
 */
struct JsonCommitRequestParser : CommitRequestParser {
public:
  // Parser state
  enum class ParseState {
    Root,
    PreconditionsArray,
    PreconditionObject,
    OperationsArray,
    OperationObject
  };

private:
  struct PreconditionParseState {
    Precondition::Type type;
    std::optional<int64_t> version;
    // These are owned by CommitRequest::arena
    std::optional<std::string_view> key;
    std::optional<std::string_view> begin;
    std::optional<std::string_view> end;
  };

  /**
   * @brief Internal state for parsing an operation during JSON processing.
   */
  struct OperationParseState {
    Operation::Type type;
    // These are owned by CommitRequest::arena
    std::optional<std::string_view> key;
    std::optional<std::string_view> value;
    std::optional<std::string_view> begin;
    std::optional<std::string_view> end;
  };

  struct ParserContext {
    using ArenaString = std::basic_string<char, std::char_traits<char>,
                                          ArenaStlAllocator<char>>;

    ParseState current_state = ParseState::Root;
    JsonTokenType current_key_token;
    // Only used if we need to accumulate the current key
    ArenaString current_key;
    ArenaString current_string;
    ArenaString current_number;
    const char *parse_error = nullptr;
    bool parse_complete = false;
    bool has_read_version_been_set = false;

    // Current objects being parsed
    PreconditionParseState current_precondition{};
    OperationParseState current_operation{};

    // Parsing state for nested structures
    ArenaString precondition_type;
    ArenaString operation_type;

    // Constructor to initialize arena-allocated containers
    explicit ParserContext(ArenaAllocator *arena)
        : current_key(ArenaStlAllocator<char>(arena)),
          current_string(ArenaStlAllocator<char>(arena)),
          current_number(ArenaStlAllocator<char>(arena)),
          precondition_type(ArenaStlAllocator<char>(arena)),
          operation_type(ArenaStlAllocator<char>(arena)) {
      has_read_version_been_set = false;
    }

    void attach_arena(ArenaAllocator *arena) {
      current_key = ArenaString{ArenaStlAllocator<char>(arena)};
      current_string = ArenaString{ArenaStlAllocator<char>(arena)};
      current_number = ArenaString{ArenaStlAllocator<char>(arena)};
      current_precondition = {};
      current_operation = {};
      precondition_type = ArenaString{ArenaStlAllocator<char>(arena)};
      operation_type = ArenaString{ArenaStlAllocator<char>(arena)};
      current_state = ParseState::Root;
    }
  };

  WeaselJsonParser *json_parser_ = nullptr;
  std::unique_ptr<ParserContext> parser_context_;
  CommitRequest *current_request_ = nullptr;
  static const WeaselJsonCallbacks json_callbacks;

public:
  /**
   * @brief Construct a new JsonCommitRequestParser.
   */
  JsonCommitRequestParser();

  /**
   * @brief Destructor - cleans up any active parser.
   */
  ~JsonCommitRequestParser();

  // Non-copyable but movable
  JsonCommitRequestParser(const JsonCommitRequestParser &) = delete;
  JsonCommitRequestParser &operator=(const JsonCommitRequestParser &) = delete;
  JsonCommitRequestParser(JsonCommitRequestParser &&other) noexcept;
  JsonCommitRequestParser &operator=(JsonCommitRequestParser &&other) noexcept;

  // CommitRequestParser interface implementation
  ParseResult parse(CommitRequest &request, char *data, int64_t len) override;
  bool begin_streaming_parse(CommitRequest &request) override;
  ParseStatus parse_chunk(char *data, int64_t len) override;
  ParseStatus finish_streaming_parse() override;
  const char *get_parse_error() const override;

  /**
   * @brief Check if read version has been explicitly set during parsing.
   *
   * @note This function is primarily used for testing to verify parser
   * behavior. It exposes internal parser state that is not typically needed in
   * production code.
   *
   * @return true if read version was set during parsing
   */
  bool has_read_version_been_set() const;

  // Weaseljson callbacks (public for global callbacks)
  static void on_begin_object(void *userdata);
  static void on_end_object(void *userdata);
  static void on_string_data(void *userdata, const char *buf, int len,
                             int done);
  static void on_key_data(void *userdata, const char *buf, int len, int done);
  static void on_begin_array(void *userdata);
  static void on_end_array(void *userdata);
  static void on_number_data(void *userdata, const char *buf, int len,
                             int done);
  static void on_true_literal(void *userdata);
  static void on_false_literal(void *userdata);
  static void on_null_literal(void *userdata);

private:
  /**
   * @brief Decode a base64 string and store it in the arena.
   * @param base64_str The base64 encoded string
   * @return String view of decoded data, or empty view if decoding failed
   */
  std::string_view decode_base64(std::string_view base64_str);

  void handle_completed_string(std::string_view s);
  void handle_completed_number(std::string_view s);
  void on_complete();
};
