///|
/// Render the statements that take `before` to `after`.
///
/// Dispatching on the *target* snapshot's dialect, not on an argument: a
/// snapshot records which dialect it was written for, so a migration cannot be
/// generated against a snapshot of another database by accident.
pub fn statements(
  before : Snapshot,
  after : Snapshot,
) -> Array[String] raise DdlError {
  guard before.dialect == after.dialect else {
    raise UnsupportedDialect(
      dialect=dialect_name(after.dialect),
      reason="the previous snapshot is for \{dialect_name(before.dialect)}",
    )
  }
  match after.dialect {
    Sqlite => sqlite_statements(before, after)
    Postgres =>
      raise UnsupportedDialect(
        dialect="postgres",
        reason="the type mapping is in place but the DDL emitter is not written yet",
      )
  }
}

///|
/// Quote an identifier, doubling any embedded quote.
///
/// Both dialects aya targets accept the SQL standard's double quotes, which
/// is also what `sql/emit.mbt` writes for column references.
pub fn ident(name : String) -> String {
  let buf = StringBuilder()
  buf.write_string("\"")
  for c in name {
    if c == '"' {
      buf.write_string("\"\"")
    } else {
      buf.write_char(c)
    }
  }
  buf.write_string("\"")
  buf.to_string()
}

///|
/// What separates two statements inside a generated migration file.
///
/// A marker rather than a bare `;`: a default expression or a trigger body can
/// contain a semicolon, so a runner that splits on `;` eventually splits in
/// the wrong place.
pub const BREAKPOINT = "--> statement-breakpoint"

///|
/// Render statements as the body of a `.sql` migration file.
pub fn migration_sql(statements : Array[String]) -> String {
  let buf = StringBuilder()
  for i, s in statements {
    if i > 0 {
      buf.write_string("\{BREAKPOINT}\n")
    }
    buf.write_string(s)
    buf.write_string("\n")
  }
  buf.to_string()
}

///|
/// Split a migration file back into the statements that made it.
///
/// The mirror of `migration_sql`, so a runner does not have to reimplement the
/// split, and so the round trip can be tested.
pub fn split_migration(text : String) -> Array[String] {
  let out = []
  let current = StringBuilder()
  let mut empty = true
  for line in text.split("\n") {
    if line.trim() == BREAKPOINT {
      if !empty {
        out.push(current.to_string().trim().to_owned())
      }
      current.reset()
      empty = true
    } else {
      current.write_stringview(line)
      current.write_string("\n")
      if !line.trim().is_empty() {
        empty = false
      }
    }
  }
  if !empty {
    out.push(current.to_string().trim().to_owned())
  }
  out
}