///|
using @datalog {type RelationData, type FunctionalRelationData, type RuleData}

///|
/// Shared teardown for datalog cells: they hold no upstream subscriber links
/// or free-list slots, so disposal only clears metadata and marks the slot
/// `Disposed`.
fn dispose_datalog_cell(
  meta : CellMeta,
  rt : Runtime,
  cell_id : CellId,
) -> Unit {
  meta.subscribers.clear()
  meta.label = None
  rt.core.cell_index[cell_id.id] = Disposed
}

///|
fn RuleDeclarationRole::label(self : RuleDeclarationRole) -> String {
  match self {
    InputOnly => "input"
    OutputOnly => "output"
    InputAndOutput => "both"
  }
}

///|
fn relation_dispose_error(rt : Runtime, cell_id : CellId) -> DisposeError? {
  match find_live_rule_declaration(rt.core, rt.datalog.rules, cell_id) {
    Some(declaration) =>
      Some(
        DisposeError::Rejected(
          "dispose: relation " +
          cell_id.id.to_string() +
          " is pinned by live rule " +
          declaration.rule_id.id.to_string() +
          " as " +
          declaration.role.label() +
          "; dispose the rule first",
        ),
      )
    None => None
  }
}

///|
impl CellLifecycle for RelationData with fn preflight_dispose_cell(
  _self,
  rt,
  cell_id,
) -> DisposeError? {
  relation_dispose_error(rt, cell_id)
}

///|
impl CellLifecycle for RelationData with fn dispose_cell(self, rt, cell_id) -> Unit {
  dispose_datalog_cell(self.meta, rt, cell_id)
}

///|
impl CellLifecycle for FunctionalRelationData with fn preflight_dispose_cell(
  _self,
  rt,
  cell_id,
) -> DisposeError? {
  relation_dispose_error(rt, cell_id)
}

///|
impl CellLifecycle for FunctionalRelationData with fn dispose_cell(
  self,
  rt,
  cell_id,
) -> Unit {
  dispose_datalog_cell(self.meta, rt, cell_id)
}

///|
impl CellLifecycle for RuleData with fn dispose_cell(self, rt, cell_id) -> Unit {
  dispose_datalog_cell(self.meta, rt, cell_id)
}