// Compile-time diagnostics tests — verify the type checker catches errors
var fd = use('fd')
var shop = use('internal/shop')
var streamline = use('streamline')

var tmpfile = "/tmp/_cell_compile_test.cm"

function write_source(src) {
  fd.slurpwrite(tmpfile, stone(blob(src)))
}

function get_diagnostics(src) {
  write_source(src)
  var compiled = shop.mcode_file(tmpfile)
  compiled._warn = true
  var optimized = streamline(compiled)
  if (optimized._diagnostics == null) return []
  return optimized._diagnostics
}

function has_diagnostic(diags, severity, pattern) {
  var i = 0
  while (i < length(diags)) {
    if (diags[i].severity == severity && search(diags[i].message, pattern) != null) {
      return true
    }
    i = i + 1
  }
  return false
}

function expect(diags, severity, pattern) {
  if (!has_diagnostic(diags, severity, pattern)) {
    return `expected ${severity} matching '${pattern}', got ${text(length(diags))} diagnostic(s)`
  }
  return null
}

function expect_clean(diags) {
  if (length(diags) > 0) {
    return `expected no diagnostics, got ${text(length(diags))}`
  }
  return null
}

return {
  // === Store errors ===

  test_store_field_on_array: function() {
    var d = get_diagnostics("var a = []\na[\"x\"] = 1")
    return expect(d, "error", "storing named property on array")
  },

  test_store_index_on_record: function() {
    var d = get_diagnostics("var a = {}\na[1] = 1")
    return expect(d, "error", "storing numeric index on record")
  },

  test_store_field_on_text: function() {
    var d = get_diagnostics("var s = \"hello\"\ns.x = 1")
    return expect(d, "error", "storing property on text")
  },

  test_store_index_on_text: function() {
    var d = get_diagnostics("var s = \"hello\"\ns[0] = 1")
    return expect(d, "error", "storing index on text")
  },

  test_push_on_text: function() {
    var d = get_diagnostics("var s = \"hello\"\ns[] = 1")
    return expect(d, "error", "push on text")
  },

  test_push_on_record: function() {
    var d = get_diagnostics("var r = {}\nr[] = 1")
    return expect(d, "error", "push on record")
  },

  // === Invoke errors ===

  test_invoke_null: function() {
    var d = get_diagnostics("var x = null\nx()")
    return expect(d, "error", "invoking null")
  },

  test_invoke_number: function() {
    var d = get_diagnostics("var x = 42\nx()")
    return expect(d, "error", "invoking")
  },

  test_invoke_text: function() {
    var d = get_diagnostics("var x = \"hello\"\nx()")
    return expect(d, "error", "invoking")
  },

  // === Warnings ===

  test_field_on_array_warns: function() {
    var d = get_diagnostics("var a = [1, 2]\nvar x = a.name")
    return expect(d, "warning", "named property access on array")
  },

  test_field_on_text_warns: function() {
    var d = get_diagnostics("var s = \"hello\"\nvar x = s.name")
    return expect(d, "warning", "named property access on text")
  },

  test_record_key_on_record_warns: function() {
    var d = get_diagnostics("var r = {a: 1}\nvar k = {}\nvar x = r[k]")
    return expect(d, "warning", "record key on record")
  },

  // === Clean code produces no diagnostics ===

  test_clean_array_ops: function() {
    var d = get_diagnostics("var a = [1, 2, 3]\nvar x = a[0]\na[1] = 5\na[] = 4")
    return expect_clean(d)
  },

  test_clean_record_ops: function() {
    var d = get_diagnostics("var r = {a: 1, b: 2}\nvar x = r.a\nr.c = 3")
    return expect_clean(d)
  },

  test_clean_function_call: function() {
    var d = get_diagnostics("function f(a, b) { return a + b }\nvar x = f(1, 2)")
    return expect_clean(d)
  }
}
