///|
fn unquote(value : StringView) -> String {
  let trimmed = value.trim()
  match trimmed.strip_prefix("\"") {
    Some(rest) =>
      match rest.rev_find("\"") {
        Some(index) => rest[:index].to_owned()
        None => rest.to_owned()
      }
    None => trimmed.to_owned()
  }
}

///|
fn assignment_value(line : StringView, key : StringView) -> String? {
  let line = line.trim()
  guard line.has_prefix(key) else { return None }
  guard line.split_once("=") is Some((lhs, rhs)) else { return None }
  guard lhs.trim() == key else { return None }
  Some(unquote(rhs))
}

///|
fn parse_dependency(spec : StringView) -> Dependency {
  match spec.rev_split_once("@") {
    Some((name, version)) if !name.is_empty() && !version.is_empty() =>
      { name: name.to_owned(), version: version.to_owned() }
    _ => { name: spec.to_owned(), version: "" }
  }
}

///|
/// Parse the current TOML-like MoonBit module format.
pub fn parse_moon_mod(text : StringView) -> Project {
  let mut name = ""
  let mut version = ""
  let mut license = ""
  let mut repository = ""
  let dependencies = []
  let mut in_import = false
  for raw_line in text.split("\n") {
    let line = raw_line.trim()
    if line.has_prefix("import") && line.contains("{") {
      in_import = true
      continue
    }
    if in_import && line.has_prefix("}") {
      in_import = false
      continue
    }
    if in_import && line.has_prefix("\"") {
      let spec = unquote(line)
      if !spec.is_empty() {
        dependencies.push(parse_dependency(spec))
      }
      continue
    }
    match assignment_value(line, "name") {
      Some(value) => name = value
      None => ()
    }
    match assignment_value(line, "version") {
      Some(value) => version = value
      None => ()
    }
    match assignment_value(line, "license") {
      Some(value) => license = value
      None => ()
    }
    match assignment_value(line, "repository") {
      Some(value) => repository = value
      None => ()
    }
  }
  { name, version, license, repository, dependencies }
}

///|
fn json_string(value : Json?) -> String {
  match value {
    Some(String(value)) => value
    _ => ""
  }
}

///|
fn dependencies_from_json(value : Json?) -> Array[Dependency] {
  let dependencies = []
  match value {
    Some(Array(items)) =>
      for item in items {
        match item {
          String(spec) => dependencies.push(parse_dependency(spec))
          _ => ()
        }
      }
    Some(Object(items)) =>
      for name, value in items {
        match value {
          String(version) => dependencies.push({ name, version })
          _ => ()
        }
      }
    _ => ()
  }
  dependencies
}

///|
/// Parse the legacy JSON MoonBit module format.
pub fn parse_moon_mod_json(text : StringView) -> Project raise {
  let value = @json.parse(text)
  guard value is Object(fields) else {
    fail("module manifest must be an object")
  }
  {
    name: json_string(fields.get("name")),
    version: json_string(fields.get("version")),
    license: json_string(fields.get("license")),
    repository: json_string(fields.get("repository")),
    dependencies: dependencies_from_json(
      match fields.get("import") {
        Some(value) => Some(value)
        None => fields.get("deps")
      },
    ),
  }
}

///|
/// Parse either supported module manifest based on its file name.
pub fn parse_manifest(path : StringView, text : StringView) -> Project raise {
  if path.has_suffix(".json") {
    parse_moon_mod_json(text)
  } else {
    parse_moon_mod(text)
  }
}