#include "commit_request.hpp"
#include "json_commit_request_parser.hpp"
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <unordered_set>
#include <vector>

struct ArenaDebugger {
  const CommitRequest &commit_request;
  const ArenaAllocator &arena;
  std::unordered_set<const void *> referenced_addresses;

  explicit ArenaDebugger(const CommitRequest &cr)
      : commit_request(cr), arena(cr.arena()) {}

  void analyze_references() {
    // Track all string_view data pointers from the parsed commit request
    if (commit_request.request_id().has_value()) {
      add_reference(commit_request.request_id()->data(),
                    commit_request.request_id()->size());
    }

    add_reference(commit_request.leader_id().data(),
                  commit_request.leader_id().size());

    for (const auto &precond : commit_request.preconditions()) {
      add_reference(precond.begin.data(), precond.begin.size());
      add_reference(precond.end.data(), precond.end.size());
    }

    for (const auto &op : commit_request.operations()) {
      add_reference(op.param1.data(), op.param1.size());
      add_reference(op.param2.data(), op.param2.size());
    }
  }

  void add_reference(const char *ptr, size_t size) {
    if (ptr && size > 0) {
      referenced_addresses.insert(ptr);
      // Also add end pointer to mark the range
      referenced_addresses.insert(ptr + size - 1);
    }
  }

  void visualize_arena() {
    std::cout << "=== Arena Visualization Debug Tool ===" << std::endl;
    std::cout << "Analyzing commit request and arena memory layout"
              << std::endl;
    std::cout << std::endl;

    // First, analyze what's referenced
    analyze_references();

    // Print basic arena statistics
    std::cout << "Arena Statistics:" << std::endl;
    std::cout << "- Total allocated: " << arena.total_allocated() << " bytes"
              << std::endl;
    std::cout << "- Currently used: " << arena.used_bytes() << " bytes"
              << std::endl;
    std::cout << "- Number of blocks: " << arena.num_blocks() << " blocks"
              << std::endl;
    std::cout << "- Referenced addresses: " << referenced_addresses.size()
              << std::endl;
    std::cout << std::endl;

    // Use the arena's debug_dump with content visualization
    std::cout << "Raw Arena Memory Layout:" << std::endl;
    arena.debug_dump(std::cout, true, true, 1024);

    std::cout << std::endl;
    std::cout << "=== Pointer Analysis ===" << std::endl;

    // Scan for potential pointers in arena memory using the arena's built-in
    // method
    scan_arena_pointers();

    std::cout << std::endl;
    std::cout << "=== Referenced Memory Regions ===" << std::endl;
    visualize_referenced_data();
  }

private:
  void scan_arena_pointers() {
    std::cout << "Arena memory analysis:" << std::endl;
    std::cout << "- Total scanned: " << arena.used_bytes() << " bytes across "
              << arena.num_blocks() << " blocks" << std::endl;
    std::cout << "- Memory utilization: "
              << (arena.total_allocated() > 0
                      ? (100.0 * arena.used_bytes() / arena.total_allocated())
                      : 0.0)
              << "%" << std::endl;
  }

  std::string_view find_string_view_for_data(const char *data) {
    if (commit_request.request_id().has_value() &&
        commit_request.request_id()->data() == data) {
      return *commit_request.request_id();
    }

    if (commit_request.leader_id().data() == data) {
      return commit_request.leader_id();
    }

    for (const auto &precond : commit_request.preconditions()) {
      if (precond.begin.data() == data)
        return precond.begin;
      if (precond.end.data() == data)
        return precond.end;
    }

    for (const auto &op : commit_request.operations()) {
      if (op.param1.data() == data)
        return op.param1;
      if (op.param2.data() == data)
        return op.param2;
    }

    return {};
  }

  void visualize_referenced_data() {
    std::cout << "Visualizing parsed commit request data references:"
              << std::endl;
    std::cout << std::endl;

    // Show request_id
    if (commit_request.request_id().has_value()) {
      std::cout << "request_id: ";
      visualize_string_data(*commit_request.request_id());
    }

    // Show leader_id
    std::cout << "leader_id: ";
    visualize_string_data(commit_request.leader_id());

    // Show read_version
    std::cout << "read_version: " << commit_request.read_version() << std::endl;

    // Show preconditions
    std::cout << "preconditions (" << commit_request.preconditions().size()
              << "):" << std::endl;
    for (size_t i = 0; i < commit_request.preconditions().size(); ++i) {
      const auto &precond = commit_request.preconditions()[i];
      std::cout << "  [" << i
                << "] type: " << precondition_type_to_string(precond.type)
                << ", version: " << precond.version << std::endl;
      std::cout << "      begin: ";
      visualize_string_data(precond.begin, "      ");
      std::cout << "      end:   ";
      visualize_string_data(precond.end, "      ");
    }

    // Show operations
    std::cout << "operations (" << commit_request.operations().size()
              << "):" << std::endl;
    for (size_t i = 0; i < commit_request.operations().size(); ++i) {
      const auto &op = commit_request.operations()[i];
      std::cout << "  [" << i << "] type: " << operation_type_to_string(op.type)
                << std::endl;
      std::cout << "      param1: ";
      visualize_string_data(op.param1, "      ");
      std::cout << "      param2: ";
      visualize_string_data(op.param2, "      ");
    }
  }

  void visualize_string_data(std::string_view sv,
                             const std::string &indent = "") {
    if (sv.empty()) {
      std::cout << "(empty)" << std::endl;
      return;
    }

    const char *data = sv.data();
    size_t size = sv.size();

    std::cout << "\"" << sv << "\" @ " << static_cast<const void *>(data)
              << " [" << size << " bytes]";

    if (referenced_addresses.count(data)) {
      std::cout << " (REFERENCED)";
    }

    std::cout << std::endl;

    // Show hex dump of the string data
    if (size > 0 && size <= 64) { // Only show hex for reasonable sizes
      std::cout << indent << "Hex: ";
      for (size_t i = 0; i < size; ++i) {
        unsigned char byte = static_cast<unsigned char>(data[i]);
        std::cout << std::hex << std::setfill('0') << std::setw(2)
                  << static_cast<unsigned int>(byte) << std::dec;
        if ((i + 1) % 4 == 0 && i < size - 1)
          std::cout << " ";
      }
      std::cout << std::endl;
    }
  }

  const char *precondition_type_to_string(Precondition::Type type) {
    switch (type) {
    case Precondition::Type::PointRead:
      return "point_read";
    case Precondition::Type::RangeRead:
      return "range_read";
    default:
      return "unknown";
    }
  }

  const char *operation_type_to_string(Operation::Type type) {
    switch (type) {
    case Operation::Type::Write:
      return "write";
    case Operation::Type::Delete:
      return "delete";
    case Operation::Type::RangeDelete:
      return "range_delete";
    default:
      return "unknown";
    }
  }
};

int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << " <json_file>" << std::endl;
    std::cerr << "Debug tool to visualize arena memory layout from commit "
                 "request JSON"
              << std::endl;
    return 1;
  }

  const char *filename = argv[1];

  // Read JSON file
  std::ifstream file(filename);
  if (!file.is_open()) {
    std::cerr << "Error: Could not open file '" << filename << "'" << std::endl;
    return 1;
  }

  std::ostringstream ss;
  ss << file.rdbuf();
  std::string json_content = ss.str();
  file.close();

  if (json_content.empty()) {
    std::cerr << "Error: File is empty or could not be read" << std::endl;
    return 1;
  }

  std::cout << "Reading commit request from: " << filename << std::endl;
  std::cout << "JSON size: " << json_content.size() << " bytes" << std::endl;
  std::cout << std::endl;

  // Parse the commit request
  CommitRequest commit_request;
  JsonCommitRequestParser parser;

  // Make a mutable copy for parsing (weaseljson requires mutable data)
  std::vector<char> mutable_json(json_content.begin(), json_content.end());
  mutable_json.push_back('\0'); // Null terminate for safety

  auto parse_result = parser.parse(commit_request, mutable_json.data(),
                                   mutable_json.size() - 1);

  if (parse_result != CommitRequestParser::ParseResult::Success) {
    std::cerr << "Error: Failed to parse JSON" << std::endl;
    if (parser.has_parse_error()) {
      std::cerr << "Parse error: " << parser.get_parse_error() << std::endl;
    }
    return 1;
  }

  std::cout << "Successfully parsed commit request!" << std::endl;
  std::cout << std::endl;

  // Create debugger and visualize
  ArenaDebugger debugger(commit_request);
  debugger.visualize_arena();

  return 0;
}
