///|
/// Transaction deletion with a fixed, independently selected relation category.
pub struct RelationCategoryReductionV4 {
  priv history : HistoryV4
  priv record : Json
}

///|
pub fn RelationCategoryReductionV4::history(
  self : RelationCategoryReductionV4,
) -> HistoryV4 {
  self.history
}

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

///|
fn relation_category_rule_status_v4(
  report : RelationClassificationV4,
  category : String,
) -> String {
  match
    relation_classification_at_v4(report.record, [
      "judgment", "rules", category, "status",
    ]) {
    String(status) => status
    _ => report.status
  }
}

///|
/// Deletion renumbers retained records; it cannot heal pre-existing lost events.
fn relation_reduction_sequence_window_v4(history : HistoryV4) -> Bool {
  let events = v4_source_sequences(history)
  for i, seq in events {
    if seq != Json(i + 1) {
      return false
    }
  }
  true
}

///|
/// Missing sources and unfinished checks prevent a single-minimality claim.
/// Final evidence is recomputed even after the candidate budget is exhausted.
pub fn reduce_relation_category_v4(
  history : HistoryV4,
  category~ : String,
  max_checks~ : Int32 = 256,
  max_states~ : Int32 = 100000,
  max_edge_checks~ : Int32 = 1000000,
  max_graph_checks~ : Int32 = 1000000,
) -> RelationCategoryReductionV4 {
  let original_sequence_window_closed = relation_reduction_sequence_window_v4(
    history,
  )
  let original_source_closed = v4_reduction_source_closed(history)
  let mut current = history
  let removed : Array[String] = []
  let mut origins = v4_source_sequences(history)
  let mut checks = 0
  let mut passes = 0
  let mut one_minimal = false
  let mut unresolved : Array[Json] = []
  let audit : Array[Json] = []
  let mut budget_exhausted = false
  let valid = relation_classification_categories_v4().contains(category) &&
    max_checks >= 0 &&
    max_checks <= 256 &&
    max_states >= 0 &&
    max_states <= 100000 &&
    max_edge_checks >= 0 &&
    max_edge_checks <= 1000000 &&
    max_graph_checks >= 0 &&
    max_graph_checks <= 1000000
  let initial = classify_relation_point_v4(
    history,
    categories=[category],
    max_states~,
    max_edge_checks~,
    max_graph_checks~,
  )
  let initial_status = relation_category_rule_status_v4(initial, category)
  let mut status = if !valid {
    "invalid"
  } else if history.data_model != RelationSetV4 {
    "unsupported"
  } else if initial_status == "refuted" {
    "budget"
  } else if initial_status == "checked-compatible" {
    "not-refuted"
  } else {
    "undetermined"
  }
  let mut changed = valid &&
    original_source_closed &&
    original_sequence_window_closed &&
    history.data_model == RelationSetV4 &&
    initial_status == "refuted"
  while changed && checks < max_checks {
    passes += 1
    changed = false
    unresolved = []
    let mut complete_pass = true
    for id in v4_capture_transaction_ids(current) {
      if checks >= max_checks {
        complete_pass = false
        budget_exhausted = true
        break
      }
      checks += 1
      let candidate = try {
        let (source, relative_origins) = v3_without_transaction(
          current.source_document,
          id,
        )
        match parse_history_v4(source.stringify()) {
          Ok(next) => Some((next, relative_origins))
          Err(_) => None
        }
      } catch {
        _ => None
      }
      match candidate {
        None => {
          let fact : Json = {
            "transaction": Json(id),
            "reason": "candidate-invalid-or-source-unavailable",
            "sourceClosed": false,
          }
          unresolved.push(fact)
          audit.push({
            "check": Json(checks),
            "transaction": Json(id),
            "accepted": false,
            "result": fact,
          })
        }
        Some((next, relative_origins)) => {
          let outcome = classify_relation_point_v4(
            next,
            categories=[category],
            max_states~,
            max_edge_checks~,
            max_graph_checks~,
          )
          let result_status = relation_category_rule_status_v4(
            outcome, category,
          )
          let source_closed = original_source_closed &&
            original_sequence_window_closed &&
            v4_reduction_source_closed(next)
          audit.push({
            "check": Json(checks),
            "transaction": Json(id),
            "accepted": Json(result_status == "refuted" && source_closed),
            "classificationStatus": Json(result_status),
            "sourceClosed": Json(source_closed),
          })
          if !source_closed {
            unresolved.push({
              "transaction": Json(id),
              "reason": "candidate-source-open",
              "classificationStatus": Json(result_status),
              "sourceClosed": false,
            })
          } else if result_status == "refuted" {
            let old_sequences = v4_source_sequences(current)
            let old_origins : Map[String, Json] = Map([])
            for index, sequence in old_sequences {
              old_origins[sequence.stringify()] = origins[index]
            }
            origins = relative_origins.map(sequence => {
              old_origins.at(sequence.stringify())
            })
            current = next
            removed.push(id)
            changed = true
            complete_pass = false
            unresolved = []
            break
          } else if result_status != "checked-compatible" {
            unresolved.push({
              "transaction": Json(id),
              "reason": Json(result_status),
              "sourceClosed": Json(v4_reduction_source_closed(next)),
            })
          }
        }
      }
    }
    one_minimal = complete_pass && unresolved.is_empty()
  }
  if changed && checks >= max_checks {
    budget_exhausted = true
  }
  let final_source_closed = original_sequence_window_closed &&
    v4_reduction_source_closed(current)
  one_minimal = one_minimal && final_source_closed
  let final_report = classify_relation_point_v4(
    current,
    categories=[category],
    max_states~,
    max_edge_checks~,
    max_graph_checks~,
  )
  if valid && history.data_model == RelationSetV4 && initial_status == "refuted" {
    if relation_category_rule_status_v4(final_report, category) != "refuted" {
      status = "invalid"
      one_minimal = false
    } else if one_minimal {
      status = if removed.is_empty() { "unchanged" } else { "reduced" }
    } else if budget_exhausted {
      status = "budget"
    } else {
      status = "undetermined"
    }
  }
  let record : Json = {
    "format": "moontxnwitness-relation-category-reduction/v4",
    "schemaVersion": 4,
    "semanticContract": "relation-point-category-transaction-reduction-2",
    "algorithm": "relation-category-single-delete-2",
    "profile": "relation-point-category-reduction/v4-2",
    "dataModel": Json(v4_model_name(history.data_model)),
    "preserveCategory": Json(category),
    "status": Json(status),
    "sourceDocument": history.source_document,
    "sourceSha256": Json(v3_dislock_sha256(history.source_text)),
    "history": current.source_document,
    "originalEventSequence": Json(origins),
    "removedTransactions": Json(removed),
    "oneMinimal": Json(one_minimal),
    "oneMinimalScope": "All remaining transaction single deletions completed for the selected source-checked category; unknown, invalid or source-open candidates prevent this claim. No operation or global minimum is claimed.",
    "unresolvedCandidates": Json(unresolved),
    "sourceCoverage": {
      "originalSourceClosed": Json(original_source_closed),
      "originalSequenceWindowClosed": Json(original_sequence_window_closed),
      "reducedSourceClosed": Json(final_source_closed),
    },
    "classification": final_report.record,
    "execution": {
      "counterContract": "relation-category-candidate-checks-1",
      "candidateChecks": Json(checks),
      "passes": Json(passes),
      "candidateAudit": Json(audit),
      "candidateLimitReached": Json(checks == max_checks),
      "candidateBudgetExhausted": Json(budget_exhausted),
      "initialClassification": true,
      "finalRevalidation": true,
      "limits": {
        "candidateChecks": Json(max_checks),
        "statesPerCandidate": Json(max_states),
        "edgeChecksPerCandidate": Json(max_edge_checks),
        "graphChecksPerCandidate": Json(max_graph_checks),
      },
    },
  }
  { history: current, record, }
}

///|
pub fn verify_relation_category_reduction_v4(
  history : HistoryV4,
  supplied : Json,
  category~ : String,
  max_checks~ : Int32 = 256,
  max_states~ : Int32 = 100000,
  max_edge_checks~ : Int32 = 1000000,
  max_graph_checks~ : Int32 = 1000000,
) -> Bool {
  supplied ==
  reduce_relation_category_v4(
    history,
    category~,
    max_checks~,
    max_states~,
    max_edge_checks~,
    max_graph_checks~,
  ).record
}