#include "parser_comparison.hpp"
#include <sstream>

std::string ParserComparison::last_error_;

ParserComparison::ComparisonResult
ParserComparison::compare_parsers(const std::string &json_str) {
  last_error_.clear();

  // Parse with weaseljson parser
  CommitRequest weasel_request;
  JsonCommitRequestParser weasel_parser;
  std::string mutable_json =
      json_str; // JsonCommitRequestParser needs mutable data
  auto weasel_result = weasel_parser.parse(weasel_request, mutable_json.data(),
                                           mutable_json.size());
  bool weasel_success =
      (weasel_result == CommitRequestParser::ParseResult::Success);

  // Parse with nlohmann reference parser
  CommitRequest nlohmann_request;
  NlohmannReferenceParser nlohmann_parser;
  auto nlohmann_result = nlohmann_parser.parse(nlohmann_request, json_str);
  bool nlohmann_success =
      (nlohmann_result == NlohmannReferenceParser::ParseResult::Success);

  // Compare results
  if (weasel_success && nlohmann_success) {
    // Both succeeded - check if they produce equivalent results
    if (requests_equal(weasel_request, nlohmann_request)) {
      return ComparisonResult::BothSuccess;
    } else {
      std::ostringstream oss;
      oss << "Parsers produced different results. ";
      oss << "Weasel: request_id="
          << (weasel_request.request_id().has_value()
                  ? weasel_request.request_id().value()
                  : "none");
      oss << ", leader_id='" << weasel_request.leader_id() << "'";
      oss << ", read_version=" << weasel_request.read_version();
      oss << ", preconditions=" << weasel_request.preconditions().size();
      oss << ", operations=" << weasel_request.operations().size() << ". ";
      oss << "Nlohmann: request_id="
          << (nlohmann_request.request_id().has_value()
                  ? nlohmann_request.request_id().value()
                  : "none");
      oss << ", leader_id='" << nlohmann_request.leader_id() << "'";
      oss << ", read_version=" << nlohmann_request.read_version();
      oss << ", preconditions=" << nlohmann_request.preconditions().size();
      oss << ", operations=" << nlohmann_request.operations().size();
      last_error_ = oss.str();
      return ComparisonResult::DifferentResults;
    }
  } else if (!weasel_success && !nlohmann_success) {
    // Both failed - this is expected for invalid JSON
    return ComparisonResult::BothFailure;
  } else if (weasel_success && !nlohmann_success) {
    // Weasel succeeded but nlohmann failed
    last_error_ = "Weasel parser succeeded but nlohmann failed: " +
                  nlohmann_parser.get_error();
    return ComparisonResult::WeaselSuccessNlohmannFail;
  } else {
    // Nlohmann succeeded but weasel failed
    last_error_ = "Nlohmann parser succeeded but weasel failed";
    if (weasel_parser.get_parse_error()) {
      last_error_ += ": " + std::string(weasel_parser.get_parse_error());
    }
    return ComparisonResult::NlohmannSuccessWeaselFail;
  }
}

std::string ParserComparison::result_to_string(ComparisonResult result) {
  switch (result) {
  case ComparisonResult::BothSuccess:
    return "Both parsers succeeded with equivalent results";
  case ComparisonResult::BothFailure:
    return "Both parsers failed (as expected)";
  case ComparisonResult::WeaselSuccessNlohmannFail:
    return "Weasel succeeded but nlohmann failed";
  case ComparisonResult::NlohmannSuccessWeaselFail:
    return "Nlohmann succeeded but weasel failed";
  case ComparisonResult::DifferentResults:
    return "Both succeeded but produced different results";
  default:
    return "Unknown result";
  }
}

bool ParserComparison::requests_equal(const CommitRequest &req1,
                                      const CommitRequest &req2) {
  // Compare request_id
  if (req1.request_id().has_value() != req2.request_id().has_value()) {
    return false;
  }
  if (req1.request_id().has_value() &&
      req1.request_id().value() != req2.request_id().value()) {
    return false;
  }

  // Compare leader_id
  if (req1.leader_id() != req2.leader_id()) {
    return false;
  }

  // Compare read_version
  if (req1.read_version() != req2.read_version()) {
    return false;
  }

  // Compare preconditions
  auto prec1 = req1.preconditions();
  auto prec2 = req2.preconditions();
  if (prec1.size() != prec2.size()) {
    return false;
  }
  for (size_t i = 0; i < prec1.size(); ++i) {
    if (!preconditions_equal(prec1[i], prec2[i])) {
      return false;
    }
  }

  // Compare operations
  auto ops1 = req1.operations();
  auto ops2 = req2.operations();
  if (ops1.size() != ops2.size()) {
    return false;
  }
  for (size_t i = 0; i < ops1.size(); ++i) {
    if (!operations_equal(ops1[i], ops2[i])) {
      return false;
    }
  }

  return true;
}

bool ParserComparison::preconditions_equal(const Precondition &p1,
                                           const Precondition &p2) {
  return p1.type == p2.type && p1.version == p2.version &&
         p1.begin == p2.begin && p1.end == p2.end;
}

bool ParserComparison::operations_equal(const Operation &op1,
                                        const Operation &op2) {
  return op1.type == op2.type && op1.param1 == op2.param1 &&
         op1.param2 == op2.param2;
}