#include "irc/serializer.h"
#include "irc/types.h"
#include <cassert>
#include <iostream>

using namespace nix_irc;

int failures = 0;

#define TEST_CHECK(cond, msg)                                                  \
  do {                                                                         \
    if (!(cond)) {                                                             \
      std::cerr << "  FAIL: " << msg << std::endl;                             \
      failures++;                                                              \
    } else {                                                                   \
      std::cout << "  PASS: " << msg << std::endl;                             \
    }                                                                          \
  } while (0)

#define TEST_PASS(msg) std::cout << "  PASS: " << msg << std::endl
#define TEST_FAIL(msg)                                                         \
  do {                                                                         \
    std::cerr << "  FAIL: " << msg << std::endl;                               \
    failures++;                                                                \
  } while (0)

void test_enum_compatibility() {
  std::cout << "> Enum compatibility..." << std::endl;

  if (static_cast<uint8_t>(NodeType::WITH) == 0x32) {
    std::cout << "  PASS: WITH has correct value 0x32" << std::endl;
  } else {
    std::cerr << "  FAIL: WITH should be 0x32, got "
              << static_cast<uint8_t>(NodeType::WITH) << std::endl;
  }

  if (static_cast<uint8_t>(NodeType::HAS_ATTR) == 0x34) {
    std::cout << "  PASS: HAS_ATTR has value 0x34 (new slot after WITH bump)"
              << std::endl;
  } else if (static_cast<uint8_t>(NodeType::HAS_ATTR) == 0x33 &&
             static_cast<uint8_t>(NodeType::WITH) == 0x32) {
    std::cout << "  PASS: HAS_ATTR has value 0x33 (restored original with WITH "
                 "at 0x32)"
              << std::endl;
  } else {
    std::cerr << "  FAIL: HAS_ATTR value is "
              << static_cast<uint8_t>(NodeType::HAS_ATTR)
              << " (expected 0x34 or 0x33 with WITH=0x32)" << std::endl;
  }

  if (IR_VERSION == 2) {
    std::cout << "  PASS: IR_VERSION bumped to 2 for breaking change"
              << std::endl;
  } else if (static_cast<uint8_t>(NodeType::WITH) == 0x32) {
    std::cout << "  PASS: IR_VERSION unchanged but WITH restored to 0x32"
              << std::endl;
  } else {
    std::cerr << "  FAIL: Either bump IR_VERSION or fix enum values"
              << std::endl;
  }
}

void test_serializer_select_with_default() {
  std::cout << "> SELECT serialization with default_expr..." << std::endl;

  auto expr = std::make_shared<Node>(ConstIntNode(42));
  auto attr = std::make_shared<Node>(ConstStringNode("key"));
  auto default_val = std::make_shared<Node>(ConstIntNode(100));

  SelectNode select_node(expr, attr);
  select_node.default_expr = default_val;
  auto select = std::make_shared<Node>(select_node);

  IRModule module;
  module.entry = select;

  Serializer ser;
  auto bytes = ser.serialize_to_bytes(module);

  Deserializer deser;
  auto loaded = deser.deserialize(bytes);

  auto *loaded_select = loaded.entry->get_if<SelectNode>();
  if (loaded_select && loaded_select->default_expr &&
      *loaded_select->default_expr) {
    auto *def_val = (*loaded_select->default_expr)->get_if<ConstIntNode>();
    if (def_val && def_val->value == 100) {
      std::cout << "  PASS: SELECT with default_expr round-trips correctly"
                << std::endl;
    } else {
      std::cerr << "  FAIL: default_expr value incorrect" << std::endl;
    }
  } else {
    std::cerr << "  FAIL: default_expr not deserialized (missing u8 flag read)"
              << std::endl;
  }
}

void test_serializer_select_without_default() {
  std::cout << "> SELECT serialization without default_expr..." << std::endl;

  auto expr = std::make_shared<Node>(ConstIntNode(42));
  auto attr = std::make_shared<Node>(ConstStringNode("key"));

  SelectNode select_node(expr, attr);
  auto select = std::make_shared<Node>(select_node);

  IRModule module;
  module.entry = select;

  Serializer ser;
  auto bytes = ser.serialize_to_bytes(module);

  Deserializer deser;
  auto loaded = deser.deserialize(bytes);

  auto *loaded_select = loaded.entry->get_if<SelectNode>();
  if (loaded_select &&
      (!loaded_select->default_expr || !*loaded_select->default_expr)) {
    std::cout << "  PASS: SELECT without default_expr round-trips correctly"
              << std::endl;
  } else {
    std::cerr << "  FAIL: default_expr should be null/absent" << std::endl;
  }
}

void test_parser_brace_depth_in_strings() {
  std::cout << "> Parser brace depth handling in strings..." << std::endl;

  std::string test_input = R"(
        let s = "test}"; in ${s}
    )";

  std::cout << "  Test input contains '}' inside string - should not end "
               "interpolation"
            << std::endl;
  std::cout << "  NOTE: This test requires running through actual parser"
            << std::endl;
}

void test_parser_has_ellipsis_usage() {
  std::cout << "> Parser has_ellipsis usage..." << std::endl;

  std::cout << "  NOTE: LambdaNode should have strict_pattern field when "
               "has_ellipsis is false"
            << std::endl;
  std::cout << "  This requires checking the parser output for strict patterns"
            << std::endl;
}

void test_parser_expect_in_speculative_parsing() {
  std::cout << "> Parser expect() in speculative parsing..." << std::endl;

  std::cout << "  NOTE: try_parse_lambda should not throw on non-lambda input"
            << std::endl;
  std::cout << "  This requires testing parser with invalid lambda patterns"
            << std::endl;
}

int main() {
  std::cout << "=== Regression Tests for Nixir ===" << std::endl << std::endl;

  test_enum_compatibility();
  std::cout << std::endl;

  test_serializer_select_with_default();
  std::cout << std::endl;

  test_serializer_select_without_default();
  std::cout << std::endl;

  test_parser_brace_depth_in_strings();
  std::cout << std::endl;

  test_parser_has_ellipsis_usage();
  std::cout << std::endl;

  test_parser_expect_in_speculative_parsing();
  std::cout << std::endl;

  std::cout << "=== Tests Complete ===" << std::endl;
  std::cout << "Failures: " << failures << std::endl;
  return failures > 0 ? 1 : 0;
}
