///|
pub fn run_default_caliper(image : GrayImage) -> PipelineResult {
  let config = CaliperConfig::new(
    start=Point::Point(x=0.0, y=Double::from_int(image.height / 2)),
    finish=Point::Point(
      x=Double::from_int((image.width - 1).max(0)),
      y=Double::from_int(image.height / 2),
    ),
    threshold=0.05,
    mode=Both,
    expected_edges=2,
    unit="px",
  )
  let tolerance = Tolerance::Tolerance(
    nominal=0.0,
    lower=-1.0e9,
    upper=1.0e9,
    unit="px",
  )
  safe_measure_caliper(image, config, tolerance)
}

///|
pub fn run_default_inspection(image : GrayImage) -> String {
  inspection_summary(run_default_caliper(image))
}

///|
pub fn image_dimensions_text(image : GrayImage) -> String {
  image.width.to_string() + "x" + image.height.to_string()
}

///|
pub fn mask_dimensions_text(mask : BinaryMask) -> String {
  mask.width.to_string() + "x" + mask.height.to_string()
}

///|
pub fn profile_dimensions_text(profile : ProfileSummary) -> String {
  "samples=" +
  profile.samples.length().to_string() +
  "; edges=" +
  profile.edges.length().to_string()
}

///|
pub fn component_dimensions_text(component : Component) -> String {
  (component.bounds.max_x - component.bounds.min_x + 1).to_string() +
  "x" +
  (component.bounds.max_y - component.bounds.min_y + 1).to_string()
}

///|
pub fn summary_dimensions_text(summary : MeasurementSummary) -> String {
  "total=" +
  summary.total.to_string() +
  "; pass=" +
  summary.pass.to_string() +
  "; warn=" +
  summary.warn.to_string() +
  "; fail=" +
  summary.fail.to_string()
}

///|
pub fn make_report_for_image(image : GrayImage, part_id : String) -> String {
  let result = run_default_caliper(image)
  inspection_markdown(part_id, image, result)
}

///|
pub fn make_json_for_image(image : GrayImage, part_id : String) -> String {
  let result = run_default_caliper(image)
  let batch = BatchRun::new(part_id)
  for measurement in result.measurements {
    batch.add(measurement)
  }
  render_batch_json(batch)
}

///|
pub fn make_csv_for_image(image : GrayImage) -> String {
  render_measurements_csv(run_default_caliper(image).measurements)
}

///|
pub fn make_quality_for_image(image : GrayImage) -> String {
  quality_summary_text(frame_quality(image))
}

///|
pub fn make_benchmark_summary() -> String {
  "checksums:\n" + benchmark_checksum_text()
}

///|
pub fn package_health(image : GrayImage) -> Array[PipelineDiagnostic] {
  let result = contract_diagnostics_image(image)
  result.append(
    contract_diagnostics_mask(threshold_image(image, otsu_threshold(image))),
  )
  result
}

///|
pub fn package_health_text(image : GrayImage) -> String {
  contract_summary(package_health(image))
}

///|
pub fn supported_report_formats() -> Array[String] {
  ["markdown", "csv", "json", "diagnostic-csv", "profile-csv", "edge-csv"]
}

///|
pub fn supported_targets() -> Array[String] {
  ["wasm", "wasm-gc", "js", "native"]
}

///|
pub fn supported_measurements() -> Array[String] {
  [
    "distance", "angle", "line-fit", "circle-fit", "width", "height", "area", "perimeter",
    "roundness", "runout",
  ]
}

///|
pub fn public_project_summary() -> String {
  "A deterministic MoonBit-native visual measurement core for profiles, edges, geometry, calibration, tolerances, and reports."
}

///|
pub fn versioned_project_summary() -> String {
  inspection_version() + " - " + public_project_summary()
}

///|
pub fn algorithm_inventory() -> Array[String] {
  [
    "bilinear sampling", "Sobel gradient", "hysteresis edges", "median and Gaussian smoothing",
    "morphology", "connected components", "contours", "convex hull", "line and circle fit",
    "affine calibration", "tolerance analysis", "uncertainty propagation", "batch analytics",
  ]
}

///|
pub fn deterministic_fixture_inventory() -> Array[String] {
  [
    "bracket", "slot", "circle", "ring", "diagonal edge", "rotated rectangle", "checker",
    "ramp", "multi-part", "noisy frame",
  ]
}

///|
pub fn api_contract_inventory() -> Array[String] {
  [
    "safe image access", "safe fitting", "safe caliper", "mask shape validation",
    "tolerance validation", "calibration validation",
  ]
}

///|
pub fn default_edge_policy() -> EdgePolicy {
  EdgePolicy::new(
    low_threshold=0.05,
    high_threshold=0.12,
    mode=Both,
    min_strength=0.02,
  )
}

///|
pub fn default_tolerance(nominal : Double, unit : String) -> Tolerance {
  Tolerance::Tolerance(
    nominal~,
    lower=nominal * 0.99,
    upper=nominal * 1.01,
    unit~,
  )
}

///|
pub fn default_caliper(image : GrayImage) -> CaliperConfig {
  CaliperConfig::new(
    start=Point::Point(x=0.0, y=Double::from_int(image.height / 2)),
    finish=Point::Point(
      x=Double::from_int((image.width - 1).max(0)),
      y=Double::from_int(image.height / 2),
    ),
    threshold=default_edge_policy().low_threshold,
    mode=Both,
    expected_edges=2,
    unit="px",
  )
}

///|
pub fn default_roi(image : GrayImage) -> Roi {
  Roi::from_bounds(
    "full-frame",
    { min_x: 0, min_y: 0, max_x: image.width - 1, max_y: image.height - 1 },
    image,
  )
}

///|
pub fn nominal_tolerance(nominal : Double) -> Tolerance {
  default_tolerance(nominal, "mm")
}

///|
pub fn pixel_tolerance(nominal : Double) -> Tolerance {
  default_tolerance(nominal, "px")
}

///|
pub fn full_frame_roi(image : GrayImage) -> Roi {
  default_roi(image)
}

///|
pub fn frame_checksum(image : GrayImage) -> Double {
  stable_checksum_image(image)
}

///|
pub fn frame_focus(image : GrayImage) -> Double {
  focus_score(image)
}

///|
pub fn frame_sharpness(image : GrayImage) -> Double {
  sharpness_score(image)
}

///|
pub fn frame_entropy(image : GrayImage) -> Double {
  entropy_image(image, 32)
}

///|
pub fn frame_dynamic_range(image : GrayImage) -> Double {
  image_dynamic_range(image)
}

///|
pub fn frame_noise(image : GrayImage) -> Double {
  texture_noise(image)
}

///|
pub fn frame_edge_count(image : GrayImage) -> Int {
  edge_map(image, 0.05, 0.12).count
}

///|
pub fn frame_component_count(image : GrayImage) -> Int {
  label_components(adaptive_threshold(image, 2, 0.01)).length()
}

///|
pub fn frame_contour_count(image : GrayImage) -> Int {
  contour_loops(adaptive_threshold(image, 2, 0.01)).length()
}

///|
pub fn frame_quality_score(image : GrayImage) -> Double {
  image_quality_score(image)
}

///|
pub fn frame_health_text(image : GrayImage) -> String {
  package_health_text(image)
}

///|
pub fn frame_dimensions(image : GrayImage) -> Bounds {
  { min_x: 0, min_y: 0, max_x: image.width - 1, max_y: image.height - 1 }
}