///|
/// Validate the subset of SARIF conventions expected by GitHub Code Scanning.
///
/// This complements `validate` with checks that are useful immediately before
/// uploading a SARIF artifact. It intentionally remains conservative: missing
/// locations are warnings because some analyzers emit project-level findings.
pub fn validate_github_compatibility(log : SarifLog) -> ValidationReport {
  let report = validate(log)
  let issues = report.issues.copy()
  if log.version != "2.1.0" {
    add_issue(
      issues,
      IssueError,
      "github.version",
      "$.version",
      "GitHub Code Scanning expects SARIF version 2.1.0",
    )
  }
  for run_index, run in log.runs {
    let run_path = "$.runs[\{run_index}]"
    if run.tool.driver.name.trim().is_empty() {
      add_issue(
        issues,
        IssueError,
        "github.tool.name",
        run_path + ".tool.driver.name",
        "tool driver name is required by GitHub Code Scanning",
      )
    }
    match run.results {
      Some(results) =>
        for result_index, result in results {
          let result_path = run_path + ".results[\{result_index}]"
          match result.ruleId {
            Some(rule_id) if !rule_id.trim().is_empty() => ()
            _ =>
              add_issue(
                issues,
                IssueError,
                "github.result.ruleId",
                result_path + ".ruleId",
                "result ruleId is required by GitHub Code Scanning",
              )
          }
          match result.locations {
            Some(locations) if !locations.is_empty() =>
              validate_github_locations(locations, result_path, issues)
            Some(_) | None =>
              add_issue(
                issues,
                IssueWarning,
                "github.result.location",
                result_path + ".locations",
                "result has no location; GitHub may display it as a project-level finding",
              )
          }
          match result.baselineState {
            Some("new" | "unchanged" | "absent") | None => ()
            Some(_) =>
              add_issue(
                issues,
                IssueError,
                "github.result.baselineState",
                result_path + ".baselineState",
                "baselineState must be new, unchanged, or absent",
              )
          }
        }
      None => ()
    }
  }
  { issues, }
}

///|
fn validate_github_locations(
  locations : Array[Location],
  path : String,
  issues : Array[ValidationIssue],
) -> Unit {
  for index, location in locations {
    match location.physicalLocation {
      Some(physical) =>
        match physical.artifactLocation {
          Some(artifact) =>
            match artifact.uri {
              Some(uri) if !uri.trim().is_empty() =>
                if uri.contains("\\") {
                  add_issue(
                    issues,
                    IssueWarning,
                    "github.location.path",
                    path +
                    ".locations[\{index}].physicalLocation.artifactLocation.uri",
                    "artifact URI uses backslashes; normalize it before upload",
                  )
                }
              _ =>
                add_issue(
                  issues,
                  IssueWarning,
                  "github.location.path",
                  path +
                  ".locations[\{index}].physicalLocation.artifactLocation.uri",
                  "physical location has no artifact URI",
                )
            }
          None =>
            add_issue(
              issues,
              IssueWarning,
              "github.location.path",
              path + ".locations[\{index}].physicalLocation",
              "physical location has no artifact location",
            )
        }
      None =>
        add_issue(
          issues,
          IssueWarning,
          "github.location.physical",
          path + ".locations[\{index}]",
          "location has no physicalLocation",
        )
    }
  }
}