///|
/// A bounded graph-category result, separate from an isolation-model judgment.
pub struct RelationPredicateClassificationV4 {
  priv status : String
  priv record : Json
}

///|
pub fn RelationPredicateClassificationV4::status(
  self : RelationPredicateClassificationV4,
) -> String {
  self.status
}

///|
fn relation_predicate_scoped_graph_v4(
  source : RelationPredicateGraphV4,
) -> Result[ScopedDependencyGraphV4, String] {
  let nodes = source.index.parsed.programs.filter_map(program => {
    if program.status == Committed {
      Some(program.id)
    } else {
      None
    }
  })
  let edges : Array[ScopedDataEdgeInputV4] = []
  for index, scoped in source.scoped_edges {
    let edge = scoped.dependency
    let kind = match (edge.kind, scoped.scope) {
      (WW, "item") => ScopedWWItem
      (WW, "predicate") => ScopedWWPredicate
      (WR, "item") => ScopedWRItem
      (WR, "predicate") => ScopedWRPredicate
      (RW, "item") => ScopedRWItem
      (RW, "predicate") => ScopedRWPredicate
      _ => return Err("unsupported relation scoped dependency")
    }
    edges.push({
      id: "relation-edge-" + index.to_string(),
      from: edge.from,
      to: edge.to,
      kind,
      sources: source.sources.get_or_default(scoped, []).map(v4_json_copy),
    })
  }
  scoped_dependency_graph_v4(
    nodes,
    edges,
    data_complete=source.complete,
    process_complete=false,
    realtime_complete=false,
    gaps=source.gaps.map(v4_json_copy),
  )
}

///|
/// Rules are required: Adya mixed-item/predicate and fixed Elle scopes differ.
/// Process and real-time edges are never inferred from physical connections.
pub fn classify_relation_predicate_v4(
  history : HistoryV4,
  category : ScopedCategoryV4,
  rule_set~ : ScopedRuleSetV4,
  max_graph_checks~ : Int32 = 1000000,
  max_predicate_checks~ : Int32 = 1000000,
  max_candidate_visits~ : Int32 = 100000,
  max_states~ : Int32 = 100000,
  max_edge_checks~ : Int32 = 1000000,
) -> Result[RelationPredicateClassificationV4, String] {
  let predicate = match
    build_relation_predicate_graph_v4(
      history,
      max_graph_checks~,
      max_predicate_checks~,
      max_candidate_visits~,
    ) {
    Ok(value) => value
    Err(reason) => return Err(reason)
  }
  let scoped = match relation_predicate_scoped_graph_v4(predicate) {
    Ok(value) => value
    Err(reason) => return Err(reason)
  }
  let result = search_scoped_category_v4(
    scoped,
    category,
    rule_set~,
    order_context=ScopedDataOnly,
    max_states~,
    max_edge_checks~,
  )
  let status = result.status()
  Ok({
    status,
    record: {
      "format": "moontxnwitness-relation-predicate-category/v4",
      "schemaVersion": 4,
      "dataModel": "relation-set-v1",
      "profile": "relation-predicate-categories/v4-1",
      "execution": {
        "counterContract": "relation-predicate-category-composite-counters-1",
        "limits": {
          "maxGraphChecks": Json(max_graph_checks),
          "maxPredicateChecks": Json(max_predicate_checks),
          "maxCandidateVisits": Json(max_candidate_visits),
          "maxStates": Json(max_states),
          "maxEdgeChecks": Json(max_edge_checks),
        },
        "counts": "predicateGraph.execution and search.execution retain their independent counter contracts",
      },
      "semanticContract": "relation-predicate-category-on-keyslots-v1",
      "algorithm": "relation-predicate-scoped-category-2",
      "sourceSha256": Json(v3_dislock_sha256(history.source_text)),
      "sourceEncoding": "exact validated history source UTF-8 bytes",
      "category": Json(scoped_category_name_v4(category)),
      "ruleSet": Json(scoped_ruleset_name_v4(rule_set)),
      "orderContext": "data-only",
      "acceptsIsolationModels": false,
      "status": Json(status),
      "predicateGraph": relation_predicate_graph_v4_json(predicate),
      "scopedGraph": scoped_dependency_graph_v4_json(scoped),
      "search": scoped_search_v4_json(result),
      "sourceAssurance": "derived from supplied original relation observations and checked effect journals; no database authentication",
      "scope": "one explicitly selected dependency category; no complete isolation-model or global shortest-cycle claim",
    },
  })
}

///|
pub fn relation_predicate_classification_v4_json(
  result : RelationPredicateClassificationV4,
) -> Json {
  v4_json_copy(result.record)
}

///|
/// The caller independently supplies every category/ruleset/budget selector.
pub fn verify_relation_predicate_classification_v4(
  history : HistoryV4,
  supplied : Json,
  category : ScopedCategoryV4,
  rule_set~ : ScopedRuleSetV4,
  max_graph_checks~ : Int32 = 1000000,
  max_predicate_checks~ : Int32 = 1000000,
  max_candidate_visits~ : Int32 = 100000,
  max_states~ : Int32 = 100000,
  max_edge_checks~ : Int32 = 1000000,
) -> Bool {
  match
    classify_relation_predicate_v4(
      history,
      category,
      rule_set~,
      max_graph_checks~,
      max_predicate_checks~,
      max_candidate_visits~,
      max_states~,
      max_edge_checks~,
    ) {
    Ok(actual) => supplied == relation_predicate_classification_v4_json(actual)
    Err(_) => false
  }
}