///|
pub(all) enum Override {
  Count(String, Int)
  Generator(String, String, Generator)
  Nullable(String, String, Int)
} derive(Eq, Debug)

///|
pub extend Override with Eq::{equal, not_equal}

///|
pub extend Override with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub(all) struct Scenario {
  name : String
  overrides : Array[Override]
}

///|
/// Apply explicit overrides to a copy. Dependencies are revalidated afterwards.
pub fn Model::with_scenario(
  self : Model,
  scenario : Scenario,
) -> Result[Model, Array[Issue]] {
  if !valid_name(scenario.name) {
    return Err([issue("invalid_name", "scenario", "Invalid scenario name")])
  }
  let model = self.snapshot()
  let seen : Map[String, Bool] = Map([])
  for change in scenario.overrides {
    let (entity_name, field_name, property) = match change {
      Count(entity, _) => (entity, None, "count")
      Generator(entity, field, _) => (entity, Some(field), "generator")
      Nullable(entity, field, _) => (entity, Some(field), "nullable")
    }
    let path = entity_name +
      (match field_name {
        Some(field) => "." + field
        None => ""
      })
    let identity = path + ":" + property
    if seen.contains(identity) {
      return Err([
        issue(
          "duplicate_override", path, "Scenario overrides the same property twice",
        ),
      ])
    }
    seen[identity] = true
    let mut found = false
    for ei in 0.. {
          model.entities[ei] = { ..entity, count, }
          found = true
        }
        Generator(_, name, generator) =>
          for fi in 0..
          for fi in 0.. Ok(model)
    Err(errors) => Err(errors)
  }
}

///|
pub(all) enum Mutation {
  SetCell(String, Int, String, Value)
  RemoveCell(String, Int, String)
  DeleteRow(String, Int)
  DuplicateRow(String, Int)
} derive(Eq, Debug)

///|
pub extend Mutation with Eq::{equal, not_equal}

///|
pub extend Mutation with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub(all) struct MutationResult {
  dataset : Dataset
  operations : Array[Mutation]
  report : ValidationReport
}

///|
/// Mutations are ordered. Row indices refer to the state after preceding edits.
/// The original remains unchanged even when a later operation fails.
pub fn Dataset::mutate(
  self : Dataset,
  model : Model,
  operations : Array[Mutation],
) -> Result[MutationResult, Issue] {
  let data = self.snapshot()
  for operation in operations {
    let (name, index) = match operation {
      SetCell(name, index, _, _)
      | RemoveCell(name, index, _)
      | DeleteRow(name, index)
      | DuplicateRow(name, index) => (name, index)
    }
    let table = match data.table(name) {
      Some(table) => table
      None =>
        return Err(
          issue("mutation_table", name, "Mutation table does not exist"),
        )
    }
    if index < 0 || index >= table.rows.length() {
      return Err(issue("mutation_row", name, "Mutation row is out of range"))
    }
    match operation {
      SetCell(_, _, field, _) | RemoveCell(_, _, field) => {
        let row = table.rows[index]
        if row.get(field) is None {
          return Err(
            issue(
              "mutation_field",
              name + "." + field,
              "Mutation field does not exist",
            ),
          )
        }
        let cells : Array[Cell] = []
        for cell in row.cells {
          if cell.name != field {
            cells.push(cell)
          } else {
            match operation {
              SetCell(_, _, _, value) => cells.push({ name: field, value, })
              _ => ()
            }
          }
        }
        table.rows[index] = { cells, }
      }
      DeleteRow(_, _) => {
        let rows : Array[Row] = []
        for i in 0..
        table.rows.push({ cells: table.rows[index].cells.copy(), })
    }
  }
  Ok({
    dataset: data,
    operations: operations.copy(),
    report: validate_dataset(model, data),
  })
}