///|
fn usage() -> String {
  (
    #|MoonEditorConfig - EditorConfig 解析、验证与来源追踪工具
    #|
    #|用法:
    #|  moon run cmd/main -- check <.editorconfig>
    #|  moon run cmd/main -- resolve <目标文件> [--json]
    #|  moon run cmd/main -- explain <目标文件>
    #|  moon run cmd/main -- query <目标文件> <属性名>
    #|  moon run cmd/main -- match  <相对路径>
    #|  moon run cmd/main -- format <.editorconfig> [--write]
    #|  moon run cmd/main -- audit <.editorconfig>
    #|  moon run cmd/main -- diff <.editorconfig>
    #|  moon run cmd/main -- conflicts <.editorconfig>
    #|
    #|退出码:0 = 成功,1 = 发现错误或未匹配,2 = 参数/文件错误
  )
}

///|
fn fail_usage(message : String) -> Unit {
  println("错误:" + message)
  println("")
  println(usage())
  @sys.exit(2)
}

///|
fn read_source(path : String) -> String? {
  Some(@fs.read_file_to_string(path)) catch {
    _ => {
      println("错误:无法读取文件 " + path)
      None
    }
  }
}

///|
fn absolute_path(path : String) -> String {
  let normalized = @lib.normalize_path(path)
  let windows_absolute = normalized.length() >= 3 &&
    normalized[1] == ':' &&
    normalized[2] == '/'
  if normalized.has_prefix("/") || windows_absolute {
    normalized
  } else {
    match @env.current_dir() {
      Some(directory) => @lib.normalize_path(directory + "/" + normalized)
      None => normalized
    }
  }
}

///|
fn config_path(directory : String) -> String {
  if directory.has_suffix("/") {
    directory + ".editorconfig"
  } else {
    directory + "/.editorconfig"
  }
}

///|
fn target_directory(target : String) -> String {
  match @lib.parent_path(target) {
    Some(directory) => directory
    None => absolute_path(".")
  }
}

///|
fn discover_layers(
  target : String,
) -> (Array[@lib.ConfigLayer], Array[@lib.Diagnostic]) {
  let layers : Array[@lib.ConfigLayer] = []
  let diagnostics : Array[@lib.Diagnostic] = []
  let mut directory = target_directory(target)
  let mut finished = false
  while !finished {
    let candidate = config_path(directory)
    if @fs.path_exists(candidate) {
      match read_source(candidate) {
        Some(source) => {
          let parsed = @lib.parse_document(candidate, source)
          layers.push({ path: candidate, directory, config: parsed.config })
          for diagnostic in parsed.diagnostics {
            diagnostics.push(diagnostic)
          }
          if parsed.config.root {
            finished = true
          }
        }
        None => ()
      }
    }
    if !finished {
      match @lib.parent_path(directory) {
        Some(parent) if parent != directory => directory = parent
        _ => finished = true
      }
    }
  }
  (layers, diagnostics)
}

///|
fn resolve_target(path : String) -> @lib.Resolution {
  let target = absolute_path(path)
  let (layers, parse_diagnostics) = discover_layers(target)
  let resolution = @lib.resolve_layers(layers, target)
  for diagnostic in parse_diagnostics {
    resolution.diagnostics.push(diagnostic)
  }
  resolution
}

///|
fn run_check(path : String) -> Unit {
  guard read_source(path) is Some(source) else {
    @sys.exit(2)
    return
  }
  let result = @lib.parse_document(absolute_path(path), source)
  let diagnostics = result.diagnostics.copy()
  for diagnostic in @lib.validate(result.config) {
    diagnostics.push(diagnostic)
  }
  if diagnostics.length() == 0 {
    println("通过:" + @lib.summarize(result.config))
  } else {
    for diagnostic in diagnostics {
      println(diagnostic.render_zh())
    }
    if @lib.has_errors(diagnostics) {
      @sys.exit(1)
    }
  }
}

///|
fn render_properties(resolution : @lib.Resolution) -> Unit {
  if resolution.properties.length() == 0 {
    println("未找到适用于目标文件的配置。")
    return
  }
  println("目标文件:" + resolution.target)
  println("最终配置:")
  for property in resolution.properties {
    println("  " + property.key + " = " + property.value)
  }
}

///|
fn run_resolve(path : String, json : Bool) -> Unit {
  let resolution = resolve_target(path)
  if json {
    println(resolution.to_json(indent=true))
  } else {
    render_properties(resolution)
  }
  if @lib.has_errors(resolution.diagnostics) {
    @sys.exit(1)
  }
}

///|
fn run_explain(path : String) -> Unit {
  let resolution = resolve_target(path)
  println(resolution.explain_zh())
  if @lib.has_errors(resolution.diagnostics) {
    @sys.exit(1)
  }
}

///|
fn run_query(path : String, key : String) -> Unit {
  let resolution = resolve_target(path)
  match resolution.get(key) {
    Some(property) => {
      println(property.value)
      println(
        "来源:" +
        property.config_path +
        ":" +
        property.source.line.to_string() +
        " [" +
        property.pattern +
        "]",
      )
    }
    None => {
      println("未解析到属性:" + key)
      @sys.exit(1)
    }
  }
}

///|
fn run_match(pattern : String, path : String) -> Unit {
  let program = @lib.compile_glob(pattern)
  if program.diagnostics.length() > 0 {
    println("Glob 无效:" + program.diagnostics.join(";"))
    @sys.exit(2)
    return
  }
  if @lib.glob_matches(pattern, path) {
    println("匹配:" + @lib.explain_glob(pattern, path))
  } else {
    println("不匹配:" + @lib.explain_glob(pattern, path))
    @sys.exit(1)
  }
}

///|
fn run_format(path : String, write : Bool) -> Unit {
  guard read_source(path) is Some(source) else {
    @sys.exit(2)
    return
  }
  let parsed = @lib.parse_document(absolute_path(path), source)
  if @lib.has_errors(parsed.diagnostics) {
    for diagnostic in parsed.diagnostics {
      println(diagnostic.render_zh())
    }
    @sys.exit(1)
    return
  }
  let formatted = @lib.format_config(parsed.config)
  if write {
    try @fs.write_string_to_file(path, formatted) catch {
      _ => {
        println("错误:无法写入文件 " + path)
        @sys.exit(2)
      }
    } noraise {
      _ => println("已格式化:" + path)
    }
  } else {
    println(formatted)
  }
}

///|
fn parsed_file(path : String) -> @lib.ParseResult? {
  match read_source(path) {
    Some(source) => Some(@lib.parse_document(absolute_path(path), source))
    None => None
  }
}

///|
fn run_audit(path : String) -> Unit {
  guard parsed_file(path) is Some(parsed) else {
    @sys.exit(2)
    return
  }
  let report = @lib.audit(parsed.config, initial=parsed.diagnostics)
  println(report.summary_zh())
  for finding in report.findings {
    println("  [" + finding.code + "] " + finding.title)
    println("    " + finding.detail)
    println("    建议:" + finding.suggestion)
  }
  if report.has_blockers() {
    @sys.exit(1)
  }
}

///|
fn run_diff(path : String) -> Unit {
  guard read_source(path) is Some(source) else {
    @sys.exit(2)
    return
  }
  let diff = @lib.format_diff(source)
  if diff == "" {
    println("配置已经符合规范格式。")
  } else {
    println(diff)
  }
}

///|
fn run_conflicts(path : String) -> Unit {
  guard parsed_file(path) is Some(parsed) else {
    @sys.exit(2)
    return
  }
  let conflicts = @lib.analyze_conflicts(parsed.config)
  if conflicts.length() == 0 {
    println("未发现可证明的配置覆盖冲突。")
  } else {
    println(
      "发现 " + conflicts.length().to_string() + " 项配置覆盖关系:",
    )
    for conflict in conflicts {
      println("  " + conflict.render_zh())
    }
  }
}

///|
fn main {
  match @env.args() {
    [_, "check", path] => run_check(path)
    [_, "resolve", path] => run_resolve(path, false)
    [_, "resolve", path, "--json"] => run_resolve(path, true)
    [_, "explain", path] => run_explain(path)
    [_, "query", path, key] => run_query(path, key)
    [_, "match", pattern, path] => run_match(pattern, path)
    [_, "format", path] => run_format(path, false)
    [_, "format", path, "--write"] => run_format(path, true)
    [_, "audit", path] => run_audit(path)
    [_, "diff", path] => run_diff(path)
    [_, "conflicts", path] => run_conflicts(path)
    [_, "--help"] | [_, "-h"] => println(usage())
    _ => fail_usage("参数不完整或命令未知")
  }
}