///|
/// 独立校验:只要诊断,不要模型。
///
/// 与 `parse_gpx` 共用同一套检查逻辑(`parse_gpx_core`),差别在**视角**:
///
/// | | `parse_gpx` | `validate_gpx` |
/// |---|---|---|
/// | 关心 | 能不能用 | 完不完美 |
/// | 有 error 时 | 返回 `Err`,丢掉已解析的部分 | 照常报出,连同 warning 一起 |
/// | 有 warning 时 | 成功路径上**丢弃** | 报出 |
/// | 返回 | `Result[Gpx, ...]` | `Array[Diagnostic]`(空 = 干净) |
///
/// 为什么要单独一个入口:真实场景里 "这个文件有什么问题" 和
/// "把这个文件读进来" 是两个需求。用 `parse_gpx` 做前者会丢信息——
/// 文档合法但有可疑之处时,你什么也看不到。

///|
/// 校验 GPX 文档,返回**全部**诊断(error + warning)。
///
/// - 返回空数组:文档干净
/// - 非空:按发现顺序排列,每条带 1-based 行列号
///
/// **不会因为发现一个错误就停下**——一个文件里的多个问题会一次性全部报出。
/// 唯一的例外是 XML 语法本身坏了(拿不到事件流),此时只能报一条 `E1000`。
pub fn validate_gpx(src : String) -> Array[Diagnostic] {
  let (_, diags) = parse_gpx_core(src)
  diags
}

///|
/// 文档是否**完全合格**:既没有 error,也没有 warning。
///
/// 这是最严格的口径。如果只想判断"能不能解析",用 `parse_gpx(...).is_ok()`。
pub fn is_valid_gpx(src : String) -> Bool {
  validate_gpx(src).length() == 0
}

///|
/// 文档是否存在**违反 schema** 的问题(不看 warning)。
pub fn has_schema_errors(src : String) -> Bool {
  has_errors(validate_gpx(src))
}

///|
/// 按级别筛出诊断。
pub fn diagnostics_of_severity(
  diags : Array[Diagnostic],
  severity : Severity,
) -> Array[Diagnostic] {
  let out : Array[Diagnostic] = []
  for d in diags {
    if d.severity == severity {
      out.push(d)
    }
  }
  out
}

///|
/// 校验并格式化成人话,便于直接打印或写进报告。
///
/// 没有问题时返回一句明确的"没问题",而不是空字符串——
/// 空字符串会让调用方分不清"干净"和"忘了填"。
pub fn validate_gpx_report(src : String) -> String {
  let diags = validate_gpx(src)
  if diags.length() == 0 {
    return "没有问题:文档符合 GPX 1.1 schema,且未发现可疑之处。"
  }
  let errors = diagnostics_of_severity(diags, Error).length()
  let warnings = diagnostics_of_severity(diags, Warning).length()
  "共 \{diags.length()} 条(\{errors} 错误 / \{warnings} 警告):\n" +
  format_diagnostics(diags)
}