// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// A driver that remembers what it was asked and answers from a list, so the
/// statements and their bound values can be read off and checked.
struct Log {
  asked : Array[(String, Array[@moondb.Value])]
  rows : Array[@moondb.Row]
}

///|
impl @moondb.Driver for Log with fn execute(self, sql, params) {
  self.asked.push((sql, params))
  { rows_affected: 1L, last_insert_id: 41L, }
}

///|
impl @moondb.Driver for Log with fn query(self, sql, params) {
  self.asked.push((sql, params))
  self.rows
}

///|
impl @moondb.Driver for Log with fn begin(_) {

}

///|
impl @moondb.Driver for Log with fn commit(_) {

}

///|
impl @moondb.Driver for Log with fn rollback(_) {

}

///|
impl @moondb.Driver for Log with fn close(_) {

}

///|
fn expect(got : String, want : String) -> Unit {
  if got != want {
    abort("got \{got}, wanted \{want}")
  }
}

///|
/// SQL statements written as annotations on declared functions. `mapper.g.mbt` is
/// generated from `models.mbt` by `cmd/sql`; a project wires the generator as a
/// dev_build rule (see the README).
///
///     moon run src/examples/23-sql-mapper
fn main {
  run() catch {
    e => abort("\{e}")
  }
}

///|
fn run() -> Unit raise {
  let ada : @moondb.Row = {
    columns: ["id", "name", "age"],
    values: [Int(1), Text("ada"), Int(36)],
  }
  let log = Log::{ asked: [], rows: [ada], }
  let db : &@moondb.Driver = log
  let people = adults(db, 18)
  expect("\{people.length()} \{people[0].name}", "1 ada")
  expect(log.asked[0].0, "SELECT id, name, age FROM user WHERE age > ?")
  println(log.asked[0].0)
  let maybe = find(db, 1)
  expect(maybe.map(u => u.name).unwrap_or("nobody"), "ada")
  let renamed_row : @moondb.Row = {
    columns: ["id", "user_name", "age"],
    values: [Int(2), Text("grace"), Null],
  }
  let other = Log::{ asked: [], rows: [renamed_row], }
  let read = renamed(other)
  expect(read[0].name, "grace")
  expect(read[0].age.map(a => a.to_string()).unwrap_or("none"), "none")
  let count = Log::{
    asked: [],
    rows: [{ columns: ["count"], values: [Int(5)], }],
  }
  expect("\{total(count)}", "5")
  let user = { id: 0L, name: "linus", age: None, }
  expect("\{add(db, user)}", "41")
  println(log.asked.last().unwrap().0)
  let returned = Log::{
    asked: [],
    rows: [{ columns: ["id"], values: [Int(9)], }],
  }
  expect("\{add_returning(returned, user)}", "9")
  expect(
    returned.asked[0].0,
    "INSERT INTO user(name, age) VALUES($1, $2) RETURNING id",
  )
  println(returned.asked[0].0)
  expect("\{set_age(db, 1, 37)}", "1")
  remove(db, 1)
  println("\{log.asked.length()} statements on the log")
}