// fuzzgen.cm — generates self-checking .cm programs for fuzz testing
// Each generated program returns a record of test functions that
// validate their own expected results.

// Newline constant — backtick strings don't interpret \n as escape
var NL = "\n"

// Simple seeded PRNG (xorshift32)
var _seed = 1
function seed(s) {
  _seed = s != 0 ? s : 1
}

function rand() {
  _seed = _seed ^ (_seed << 13)
  _seed = _seed ^ (_seed >> 17)
  _seed = _seed ^ (_seed << 5)
  if (_seed < 0) _seed = -_seed
  return _seed
}

function rand_int(lo, hi) {
  return lo + (rand() % (hi - lo + 1))
}

function rand_float() {
  return rand_int(-10000, 10000) / 100
}

function rand_bool() {
  return rand() % 2 == 0
}

function pick(arr) {
  return arr[rand() % length(arr)]
}

// Expression generators — each returns {src: "code", val: expected_value}
// depth is decremented to prevent infinite recursion

function gen_int_literal() {
  var v = rand_int(-10000, 10000)
  return {src: text(v), val: v}
}

function gen_float_literal() {
  var v = rand_float()
  return {src: text(v), val: v}
}

function gen_bool_literal() {
  var v = rand_bool()
  var s = "false"
  if (v) s = "true"
  return {src: s, val: v}
}

function gen_text_literal() {
  var words = ["alpha", "beta", "gamma", "delta", "epsilon"]
  var w = pick(words)
  return {src: `"${w}"`, val: w}
}

function gen_null_literal() {
  return {src: "null", val: null}
}

function gen_int_expr(depth) {
  var a = null
  var b = null
  var op = null
  var result = null

  if (depth <= 0) return gen_int_literal()

  a = gen_int_expr(depth - 1)
  b = gen_int_expr(depth - 1)

  // Avoid division by zero
  if (b.val == 0) b = {src: "1", val: 1}

  op = pick(["+", "-", "*"])
  if (op == "+") {
    result = a.val + b.val
  } else if (op == "-") {
    result = a.val - b.val
  } else {
    result = a.val * b.val
  }

  // Guard against overflow beyond safe integer range
  if (result > 9007199254740991 || result < -9007199254740991) {
    return gen_int_literal()
  }

  return {src: `(${a.src} ${op} ${b.src})`, val: result}
}

function gen_float_expr(depth) {
  var a = null
  var b = null
  var op = null
  var result = null

  if (depth <= 0) return gen_float_literal()

  a = gen_float_expr(depth - 1)
  b = gen_float_expr(depth - 1)

  if (b.val == 0) b = {src: "1.0", val: 1.0}

  op = pick(["+", "-", "*"])
  if (op == "+") {
    result = a.val + b.val
  } else if (op == "-") {
    result = a.val - b.val
  } else {
    result = a.val * b.val
  }

  return {src: `(${a.src} ${op} ${b.src})`, val: result}
}

function gen_text_expr(depth) {
  var a = null
  var b = null

  if (depth <= 0) return gen_text_literal()

  a = gen_text_literal()
  b = gen_text_literal()

  return {src: `(${a.src} + ${b.src})`, val: a.val + b.val}
}

function gen_comparison_expr(depth) {
  var a = null
  var b = null
  var op = null
  var result = null

  a = gen_int_expr(depth > 0 ? depth - 1 : 0)
  b = gen_int_expr(depth > 0 ? depth - 1 : 0)

  op = pick(["==", "!=", "<", ">", "<=", ">="])
  if (op == "==") {
    result = a.val == b.val
  } else if (op == "!=") {
    result = a.val != b.val
  } else if (op == "<") {
    result = a.val < b.val
  } else if (op == ">") {
    result = a.val > b.val
  } else if (op == "<=") {
    result = a.val <= b.val
  } else {
    result = a.val >= b.val
  }

  return {src: `(${a.src} ${op} ${b.src})`, val: result}
}

// Generate an if-else expression test
function gen_if_else_test() {
  var cond = gen_comparison_expr(1)
  var then_val = gen_int_literal()
  var else_val = gen_int_literal()
  var expected = cond.val ? then_val.val : else_val.val

  var body = "var result = null" + NL
  body = body + "    if (" + cond.src + ") {" + NL
  body = body + "      result = " + then_val.src + NL
  body = body + "    } else {" + NL
  body = body + "      result = " + else_val.src + NL
  body = body + "    }" + NL
  body = body + "    if (result != " + text(expected) + ") return \"if_else: expected " + text(expected) + " got \" + text(result)"

  return body
}

// Generate a loop accumulator test
function gen_loop_test() {
  var count = rand_int(1, 50)
  var step = rand_int(1, 10)
  var expected = 0
  var i = 0
  while (i < count) {
    expected = expected + step
    i = i + 1
  }

  var body = "var acc = 0" + NL
  body = body + "    var i = 0" + NL
  body = body + "    while (i < " + text(count) + ") {" + NL
  body = body + "      acc = acc + " + text(step) + NL
  body = body + "      i = i + 1" + NL
  body = body + "    }" + NL
  body = body + "    if (acc != " + text(expected) + ") return \"loop: expected " + text(expected) + " got \" + text(acc)"

  return body
}

// Generate a closure test
function gen_closure_test() {
  var init_val = rand_int(1, 100)
  var inc = rand_int(1, 10)
  var calls = rand_int(1, 10)
  var expected = init_val + (inc * calls)

  var body = "var counter = " + text(init_val) + NL
  body = body + "    var inc = function() { counter = counter + " + text(inc) + " }" + NL
  body = body + "    var i = 0" + NL
  body = body + "    while (i < " + text(calls) + ") {" + NL
  body = body + "      inc()" + NL
  body = body + "      i = i + 1" + NL
  body = body + "    }" + NL
  body = body + "    if (counter != " + text(expected) + ") return \"closure: expected " + text(expected) + " got \" + text(counter)"

  return body
}

// Generate a record property test
function gen_record_test() {
  var a = gen_int_literal()
  var b = gen_int_literal()
  var sum = a.val + b.val

  var body = "var r = {a: " + a.src + ", b: " + b.src + "}" + NL
  body = body + "    var result = r.a + r.b" + NL
  body = body + "    if (result != " + text(sum) + ") return \"record: expected " + text(sum) + " got \" + text(result)"

  return body
}

// Generate an array test
function gen_array_test() {
  var n = rand_int(2, 10)
  var vals = []
  var i = 0
  var sum = 0
  var v = 0
  while (i < n) {
    v = rand_int(-100, 100)
    push(vals, v)
    sum = sum + v
    i = i + 1
  }

  var val_strs = []
  i = 0
  while (i < n) {
    push(val_strs, text(vals[i]))
    i = i + 1
  }

  var body = "var a = [" + text(val_strs, ", ") + "]" + NL
  body = body + "    var _sum = 0" + NL
  body = body + "    var i = 0" + NL
  body = body + "    while (i < length(a)) {" + NL
  body = body + "      _sum = _sum + a[i]" + NL
  body = body + "      i = i + 1" + NL
  body = body + "    }" + NL
  body = body + "    if (_sum != " + text(sum) + ") return \"array_sum: expected " + text(sum) + " got \" + text(_sum)"

  return body
}

// Generate a nested function / higher-order test
function gen_higher_order_test() {
  var mul = rand_int(2, 10)
  var input = rand_int(1, 100)
  var expected = input * mul

  var body = "var make_mul = function(m) {" + NL
  body = body + "      return function(x) { return x * m }" + NL
  body = body + "    }" + NL
  body = body + "    var fn = make_mul(" + text(mul) + ")" + NL
  body = body + "    var result = fn(" + text(input) + ")" + NL
  body = body + "    if (result != " + text(expected) + ") return \"higher_order: expected " + text(expected) + " got \" + text(result)"

  return body
}

// Generate a disruption handling test
function gen_disrupt_test() {
  var body = "var caught = false" + NL
  body = body + "    var _fn = function() { disrupt } disruption { caught = true }" + NL
  body = body + "    _fn()" + NL
  body = body + "    if (!caught) return \"disrupt: expected to catch disruption\""

  return body
}

// Generate a text operation test
function gen_text_op_test() {
  var words = ["hello", "world", "foo", "bar", "baz"]
  var w1 = pick(words)
  var w2 = pick(words)
  var expected = w1 + w2

  var body = "var a = \"" + w1 + "\"" + NL
  body = body + "    var b = \"" + w2 + "\"" + NL
  body = body + "    var c = a + b" + NL
  body = body + "    if (c != \"" + expected + "\") return \"text_op: expected " + expected + " got \" + c"

  return body
}

// All generators
var generators = [
  gen_if_else_test,
  gen_loop_test,
  gen_closure_test,
  gen_record_test,
  gen_array_test,
  gen_higher_order_test,
  gen_disrupt_test,
  gen_text_op_test
]

// Generate a complete self-checking .cm program
function generate(s) {
  seed(s)

  var num_tests = rand_int(5, 15)
  var src = "// Auto-generated fuzz test (seed=" + text(s) + ")\nreturn {\n"
  var i = 0
  var gen = null
  var body = null

  while (i < num_tests) {
    gen = pick(generators)
    body = gen()
    if (i > 0) src = src + ",\n"
    src = src + "  fuzz_" + text(i) + ": function() {\n"
    src = src + "    " + body + "\n"
    src = src + "  }"
    i = i + 1
  }

  src = src + "\n}\n"
  return src
}

return {
  generate: generate,
  seed: seed
}
