///|
fn push_error(
  diagnostics : Array[Diagnostic],
  code : String,
  message : String,
  detail : String,
) -> Unit {
  diagnostics.push(Diagnostic::new(Error, code, message, detail))
}

///|
fn push_warn(
  diagnostics : Array[Diagnostic],
  code : String,
  message : String,
  detail : String,
) -> Unit {
  diagnostics.push(Diagnostic::new(Warn, code, message, detail))
}

///|
fn push_info(
  diagnostics : Array[Diagnostic],
  code : String,
  message : String,
  detail : String,
) -> Unit {
  diagnostics.push(Diagnostic::new(Info, code, message, detail))
}

///|
fn has_non_empty_field(project : ProjectSnapshot, key : String) -> Bool {
  match project.moon_mod_field(key) {
    Some(value) => value.trim().length() > 0
    None => false
  }
}

///|
fn check_required_files(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  if !project.has_file("moon.mod") {
    push_error(
      diagnostics, "missing_moon_mod", "moon.mod is required", "Add moon.mod at the project root so moon can identify the module.",
    )
  }
  if !project.has_file("moon.pkg") {
    push_warn(
      diagnostics, "missing_root_moon_pkg", "root moon.pkg is recommended for a reusable module",
      "Add root moon.pkg so the module has an explicit public package boundary.",
    )
  }
  if !project.has_file("LICENSE") {
    push_error(
      diagnostics, "missing_license", "LICENSE is required", "Add an MIT LICENSE file before uploading the repository.",
    )
  }
  if !project.has_readme_file() {
    push_warn(
      diagnostics, "missing_readme", "README.md is recommended", "Add README.md so GitHub and mooncakes users can understand the package.",
    )
  } else if !project.has_file("README.md") && project.has_file("README.mbt.md") {
    push_info(
      diagnostics, "missing_github_readme", "README.mbt.md is present but README.md is missing",
      "Add README.md or a repository-supported link so GitHub shows the project overview.",
    )
  }
}

///|
fn check_recommended_files(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  if !project.has_file(".gitignore") {
    push_info(
      diagnostics, "missing_gitignore", ".gitignore is useful for MoonBit build artifacts",
      "Ignore _build/, target/, .mooncakes/, and local editor files.",
    )
  } else {
    if !project.gitignore_mentions("_build") {
      push_info(
        diagnostics, "gitignore_missing_build", ".gitignore should ignore _build/",
        "Add _build/ to avoid committing generated build artifacts.",
      )
    }
    if !project.gitignore_mentions(".mooncakes") {
      push_info(
        diagnostics, "gitignore_missing_mooncakes", ".gitignore should usually ignore .mooncakes/",
        "Add .mooncakes/ unless the repository intentionally vendors fetched dependencies.",
      )
    }
  }
  if !project.has_workflow() {
    push_info(
      diagnostics, "missing_ci", "CI workflow is recommended", "Add a GitHub Actions workflow that runs moon check and moon test.",
    )
  } else {
    if !project.workflow_mentions("moon check") {
      push_warn(
        diagnostics, "ci_missing_moon_check", "CI should run moon check", "Add moon check to the GitHub Actions workflow.",
      )
    }
    if !project.workflow_mentions("moon test") {
      push_warn(
        diagnostics, "ci_missing_moon_test", "CI should run moon test", "Add moon test to the GitHub Actions workflow.",
      )
    }
  }
  if !project.has_dir("examples") {
    push_info(
      diagnostics, "missing_examples", "examples directory is recommended", "Add small runnable examples for users who want to try the package quickly.",
    )
  } else if !project.has_example_content() {
    push_info(
      diagnostics, "examples_missing_content", "examples should contain runnable code or documentation",
      "Add an example package, .mbt file, or README under examples/.",
    )
  }
  if !project.has_any_test_file() {
    push_warn(
      diagnostics, "missing_tests", "MoonBit tests are recommended", "Add *_test.mbt or *_wbtest.mbt files that cover the core behavior.",
    )
  }
}

///|
fn check_moon_mod_fields(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  if project.has_file("moon.mod") {
    for key in ["name", "version", "readme", "license"] {
      if !has_non_empty_field(project, key) {
        let rule_code = if key == "readme" {
          "missing_readme_metadata"
        } else {
          "missing_" + key
        }
        push_warn(
          diagnostics,
          rule_code,
          "moon.mod should define " + key,
          "Fill the " + key + " field before publishing.",
        )
      }
    }
    for key in ["repository", "description", "keywords"] {
      if !has_non_empty_field(project, key) {
        push_info(
          diagnostics,
          "missing_" + key,
          "moon.mod can be more discoverable with " + key,
          "Fill the " + key + " field to improve package metadata.",
        )
      }
    }
    check_readme_target(project, diagnostics)
    check_repository_url(project, diagnostics)
    check_metadata_quality(project, diagnostics)
  }
}

///|
fn check_readme_target(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  match project.moon_mod_field("readme") {
    Some(path) =>
      if !project.has_exact_file(path) {
        push_warn(
          diagnostics,
          "readme_target_missing",
          "moon.mod readme should point to an existing file",
          "moon.mod readme is " + path + ", but that file was not found.",
        )
      }
    None => ()
  }
}

///|
fn check_repository_url(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  match project.moon_mod_field("repository") {
    Some(url) =>
      if url.contains("<") || url.contains("example.com") {
        push_warn(
          diagnostics, "repository_placeholder", "moon.mod repository should not be a placeholder",
          "Replace the repository field with the final GitHub or Gitlink URL.",
        )
      } else if !(url.has_prefix("https://github.com/") ||
        url.has_prefix("https://gitlink.org.cn/")) {
        push_info(
          diagnostics, "repository_unusual", "moon.mod repository should use a recognizable hosting URL",
          "Use a GitHub or Gitlink repository URL when publishing community projects.",
        )
      }
    None => ()
  }
}

///|
fn check_metadata_quality(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  if project.field_mentions("description", "TODO") ||
    project.field_mentions("description", "todo") ||
    project.field_mentions("description", "example") {
    push_info(
      diagnostics, "description_placeholder", "moon.mod description looks like a placeholder",
      "Replace it with a concrete one-sentence project summary.",
    )
  }
  if project.field_mentions("keywords", "TODO") ||
    project.field_mentions("keywords", "todo") ||
    project.field_mentions("keywords", "example") {
    push_info(
      diagnostics, "keywords_placeholder", "moon.mod keywords look like placeholders",
      "Use searchable ecosystem terms such as moonbit, release, quality, or tooling.",
    )
  }
}

///|
fn check_license_consistency(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  match project.moon_mod_field("license") {
    Some(value) =>
      if project.has_file("LICENSE") && !project.license_mentions(value) {
        push_warn(
          diagnostics,
          "license_mismatch",
          "moon.mod license should match LICENSE",
          "moon.mod declares " + value + ", but LICENSE does not mention it.",
        )
      }
    None => ()
  }
}

///|
fn check_readme_sections(
  project : ProjectSnapshot,
  diagnostics : Array[Diagnostic],
) -> Unit {
  if project.has_file("README.md") {
    if !project.readme_mentions("Usage") && !project.readme_mentions("usage") {
      push_info(
        diagnostics, "readme_missing_usage", "README should include usage", "Show the exact command a user can run first.",
      )
    }
    if !project.readme_mentions("Development") &&
      !project.readme_mentions("development") {
      push_info(
        diagnostics, "readme_missing_development", "README should include development commands",
        "List moon check and moon test so contributors know how to validate changes.",
      )
    }
    if !project.readme_mentions("License") &&
      !project.readme_mentions("license") {
      push_info(
        diagnostics, "readme_missing_license", "README should include license information",
        "Mention the repository license in README.md.",
      )
    }
  }
}

///|
fn apply_config(
  diagnostics : Array[Diagnostic],
  config : DoctorConfig,
) -> Array[Diagnostic] {
  let configured : Array[Diagnostic] = []
  for diagnostic in diagnostics {
    if !config.is_rule_disabled(diagnostic.code) {
      let severity = config.severity_for(diagnostic.code, diagnostic.severity)
      configured.push({
        severity,
        code: diagnostic.code,
        message: diagnostic.message,
        detail: diagnostic.detail,
        location: diagnostic.location,
        remediation: diagnostic.remediation,
      })
    }
  }
  configured
}

///|
/// Run health checks with a project-local release policy.
pub fn diagnose_with_config(
  project : ProjectSnapshot,
  config : DoctorConfig,
) -> DoctorReport {
  let diagnostics : Array[Diagnostic] = []
  check_required_files(project, diagnostics)
  check_recommended_files(project, diagnostics)
  check_moon_mod_fields(project, diagnostics)
  check_license_consistency(project, diagnostics)
  check_readme_sections(project, diagnostics)
  DoctorReport::with_context(
    project.root,
    apply_config(diagnostics, config),
    config.profile.label(),
    config.release_checklist(),
  )
}

///|
/// Run all default-profile health checks. This preserves the original API.
pub fn diagnose(project : ProjectSnapshot) -> DoctorReport {
  diagnose_with_config(project, DoctorConfig::default())
}