///|
test "basic expression printing" {
  let expr = "let x = 1 + 2"
  let result = parse_impl(expr)
  let printed = to_string_impl(result)
  inspect(printed, content="let x = 1 + 2")

  // Test with simple expression 
  let expr2 = "1 + 2"
  let result2 = parse_impl(expr2)
  let printed2 = to_string_impl(result2)
  inspect(printed2, content="_")
}

///|
test "constant expressions" {
  let result1 = parse_impl("let x = 42")
  inspect(to_string_impl(result1), content="let x = 42")
  let result2 = parse_impl("let y = true")
  inspect(to_string_impl(result2), content="let y = true")
  let result3 = parse_impl("let z = \"hello\"")
  inspect(
    to_string_impl(result3),
    content=(
      #|let z = "hello"
    ),
  )
}

///|
test "function definitions" {
  let func_def = "fn add(x : Int, y : Int) -> Int { x + y }"
  let result = parse_impl(func_def)
  let printed = to_string_impl(result)
  inspect(printed, content="fn add(x : Int, y : Int) -> Int { x + y }")
}

///|
test "comprehensive printer functionality demo" {
  // Test various language constructs
  let examples = [
    "let x = 42", "pub const PI = 3.14", "let result = x + y * z", "fn factorial(n : Int) -> Int { if n <= 1 { 1 } else { n * factorial(n - 1) } }",
    "struct Point { x : Int, y : Int }", "test \"addition\" { assert_eq(1 + 1, 2) }",
  ]
  for example in examples {
    let result = parse_impl(example)
    let printed = to_string_impl(result)
    // Just verify they don't crash - actual content verified in other tests
    ignore(printed)
  }
}

///|
test "type definitions" {
  let type_def = "struct Point { x : Int, y : Int }"
  let result = parse_impl(type_def)
  let printed = to_string_impl(result)
  inspect(printed, content="struct Point { x : Int; y : Int; }")
}

///|
test "visibility modifiers" {
  let pub_let = "pub let x = 42"
  let result = parse_impl(pub_let)
  let printed = to_string_impl(result)
  inspect(printed, content="pub let x = 42")
}

///|
test "test definitions" {
  let test_def = "test \"sample test\" { assert_eq(1 + 1, 2) }"
  let result = parse_impl(test_def)
  let printed = to_string_impl(result)
  inspect(printed, content="test \"sample test\" { assert_eq(1 + 1, 2) }")
}

///|
test {
  let s = "let expr : Int = { let x = 1; let y = x + 3; y * 2 }"
  let result = parse_impl(s)
  let printed = to_string_impl(result)
  inspect(
    printed,
    content="let expr : Int = { let x = 1; let y = x + 3; y * 2 }",
  )
}

///|

///|
/// Get all MoonBit packages from a directory
/// Filters out files and hidden directories (starting with ".")
fn get_moonbit_package(dir_path : String) -> Array[String] raise {
  let packages = try! @fs.read_dir(dir_path)
  let packages = packages.filter(fn(p) { not(@fs.is_file(dir_path + "/" + p)) })
  packages.filter(fn(p) { not(p.has_prefix(".")) })
}

///|
/// Get all MoonBit source files from a package directory
/// Returns files with .mbt extension
fn get_moonbit_source_file(package_path : String) -> Array[String] {
  let files = try! @fs.read_dir(package_path)
  files.filter(fn(f) { f.has_suffix(".mbt") })
}

///|
fn prop_print_identity(path : String, progress~ : String) -> Unit raise {
  let s = try! @fs.read_file_to_string(path)
  let impls = @parser.parse_string(s).0
  for impl_ in impls {
    let printed = to_string_impl(impl_)
    let reparsed = parse_impl(printed)
    let reprinted = to_string_impl(reparsed)
    let loc = impl_.loc()
    assert_eq(
      printed,
      reprinted,
      msg=(
        $|progress: \{progress}
        $|
        $|printed:   `\{printed} `
        $|
        $|reprinted: `\{reprinted} `
        $|
        $| \{path}:\{loc.start.lnum}:\{loc.start.column()}-\{loc.end.lnum}:\{loc.end.column()} 
      ),
    )
  }
}

///|
fn get_all_test_path() -> Array[String] {
  let path = []
  let module_path = "./examples/core"
  let packages = try! get_moonbit_package(module_path)
  for pkg in packages {
    let package_path = module_path + "/" + pkg
    let source_files = get_moonbit_source_file(package_path)
    for file in source_files {
      path.push(package_path + "/" + file)
    }
  }
  path
}

///|
test "package references printing" {
  // Test type references with package prefix
  let type_ref = "let x : @list.List[Int] = @list.empty()"
  let result1 = parse_impl(type_ref)
  let printed1 = to_string_impl(result1)
  inspect(printed1, content="let x : @list.List[Int] = @list.empty()")

  // Test method references with package prefix
  let method_ref = "@json.inspect(x, content=\"test\")"
  let result2 = parse_impl(method_ref)
  let printed2 = to_string_impl(result2)
  inspect(printed2, content="_")

  // Test trait references in type constraints
  let trait_ref = "fn[T : @json.ToJson] test(x : T) -> Unit { ... }"
  let result3 = parse_impl(trait_ref)
  let printed3 = to_string_impl(result3)
  inspect(printed3, content="fn[T : @json.ToJson] () { _ }")
}

///|
test {
  let s = "test { inspect(0N.bit_length(),content=\"0\") }"
  let result = parse_impl(s)
  inspect(
    to_string_impl(result),
    content=(
      #|test { inspect(0N.bit_length(), content="0") }
    ),
  )
}

///|
test {
  let all_path = get_all_test_path()
  let total = all_path.length()
  let mut idx = 0
  for path in all_path {
    prop_print_identity(path, progress="\{idx}/\{total}")
    idx = idx + 1
  }
}

///|
test {
  let s = "let odd_even : ((Int) -> Bool, (Int) -> Bool) = { letrec is_odd = fn(n : Int) -> Bool { if n == 0 { false } else { is_even(n - 1) } } and is_even = fn(n : Int) -> Bool { if n == 0 { true } else { is_odd(n - 1) } } ; (is_odd,is_even) }"
  let r = parse_impl(s)
  inspect(
    to_string_impl(r),
    content="let odd_even : ((Int) -> Bool, (Int) -> Bool) = { letrec is_odd = fn(n : Int) -> Bool { if n == 0 { false } else { is_even(n - 1) } } and is_even = fn(n : Int) -> Bool { if n == 0 { true } else { is_odd(n - 1) } }; (is_odd, is_even) }",
  )
}