///|
/// Parse Aseprite sprite sheet JSON exported with `--format json-array` or
/// the default object form. This is the primary high-level entry point.
pub fn parse_sprite_sheet_json(input : String) -> PixelAnimParseResult {
  let json = @json.parse(input) catch {
    err =>
      return PixelAnimParseResult::failure(
        PixelAnimError::new(
          ErrorJsonParse,
          "cannot parse sprite sheet JSON: \{err}",
        ),
      )
  }
  parse_sprite_sheet_value(json, source_length=input.length())
}

///|
pub fn parse_sprite_sheet_value(
  json : Json,
  source_length? : Int = 0,
) -> PixelAnimParseResult {
  match json {
    Object(_) =>
      PixelAnimParseResult::success(
        parse_sprite_sheet_root(json, source_length~),
      )
    _ =>
      PixelAnimParseResult::failure(
        PixelAnimError::new(
          ErrorExpectedObject,
          "sprite sheet root must be a JSON object, got \{json_type_name(json)}",
        ),
      )
  }
}

///|
/// Validate without forcing callers to keep the parsed value.
pub fn validate_sprite_sheet_json(input : String) -> ValidationReport {
  let parsed = parse_sprite_sheet_json(input)
  if !parsed.ok {
    ValidationReport::new([
      ValidationIssue::error(parsed.error.message, path=parsed.error.path),
    ])
  } else {
    validate_sprite_sheet(parsed.sheet)
  }
}

///|
/// Parse an `.ase` or `.aseprite` byte array and return structural metadata.
/// Pixel decompression is intentionally not exposed in v1.
pub fn parse_aseprite_binary(bytes : Array[Int]) -> AseParseResult {
  parse_aseprite_file(bytes)
}

///|
pub fn inspect_fixture_report(format? : ReportFormat = ReportText) -> String {
  asset_report_from_json(fixture_aseprite_json(), format~)
}

///|
pub fn library_smoke_passed() -> Bool {
  let parsed = parse_sprite_sheet_json(fixture_aseprite_json())
  let binary = parse_aseprite_binary(fixture_aseprite_binary())
  if !parsed.ok || !binary.ok {
    false
  } else {
    let validation = validate_sprite_sheet(parsed.sheet)
    let library = build_animation_library(parsed.sheet)
    let manifest = export_runtime_manifest(parsed.sheet)
    validation.ok &&
    library.clips.length() >= 2 &&
    manifest.find("\"clips\"") != None &&
    binary.file.chunk_count() >= 2
  }
}