///|
fn emit_invalid_config(
  diagnostics : Array[Report],
  config_name : String,
  key : String,
  value : Ast,
) -> Unit {
  diagnostics.push({
    loc: value.loc(),
    msg: "Invalid \{config_name} config: unexpected key `\{key}`.",
  })
}

///|
fn check_unique_toplevel(
  seen : Map[String, Bool],
  diagnostics : Array[Report],
  config_name : String,
  allowed_duplicate : (String) -> Bool,
  key : String,
  value : Ast,
) -> Unit {
  if seen.contains(key) && !allowed_duplicate(key) {
    diagnostics.push({
      loc: value.loc(),
      msg: "Duplicate key `\{key}` found in \{config_name}.",
    })
  }
  seen[key] = true
}

///|
fn _validate_moon_pkg(ast : Ast, diagnostics : Array[Report]) -> Unit {
  match ast {
    Obj(fields, ..) => {
      let allowed_duplicate = fn(key : String) -> Bool {
        key == "dev_build" || key == "rule"
      }
      let seen : Map[String, Bool] = Map([])
      for field in fields {
        let (key, value) = field
        check_unique_toplevel(
          seen, diagnostics, "moon.pkg", allowed_duplicate, key, value,
        )
        match key {
          "warnings" | "supported_targets" | "options" =>
            emit_invalid_config(diagnostics, "moon.pkg", key, value)
          _ => ()
        }
      }
    }
    _ => ()
  }
}

///|
fn _validate_moon_mod(ast : Ast, diagnostics : Array[Report]) -> Unit {
  match ast {
    Obj(fields, ..) => {
      let seen : Map[String, Bool] = Map([])
      let allowed_duplicate = fn(key : String) -> Bool { key == "rule" }
      for field in fields {
        let (key, value) = field
        check_unique_toplevel(
          seen, diagnostics, "moon.mod", allowed_duplicate, key, value,
        )
        match key {
          "warnings"
          | "supported_targets"
          | "preferred_target"
          | "import"
          | "options" =>
            emit_invalid_config(diagnostics, "moon.mod", key, value)
          _ => ()
        }
      }
    }
    _ => ()
  }
}

///|
fn _validate_moon_work(ast : Ast, diagnostics : Array[Report]) -> Unit {
  match ast {
    Obj(fields, ..) => {
      let seen : Map[String, Bool] = Map([])
      let allowed_duplicate = fn(_ : String) -> Bool { false }
      for field in fields {
        let (key, value) = field
        check_unique_toplevel(
          seen, diagnostics, "moon.work", allowed_duplicate, key, value,
        )
        match key {
          "members" | "preferred_target" => ()
          _ => emit_invalid_config(diagnostics, "moon.work", key, value)
        }
      }
    }
    _ => ()
  }
}