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

///|
/// The part of a driver a query layer actually needs: run a statement, and ask what
/// the backend is.
///
/// `Driver` bundles three unrelated responsibilities — running SQL, owning a
/// transaction, and owning a connection. A repository method, a query builder, a
/// migration step all want the first and must not be able to `close` the connection
/// out from under the caller that lent it to them. That is what the narrow view is
/// for: `Driver::executor` hands out an [`Executor`], and a function that takes
/// `&QueryExecutor` can run SQL and cannot end the session.
///
/// It also carries the two questions a dialect-sensitive layer asks before it emits
/// SQL: [`dialect`](#dialect) for the placeholder spelling and
/// [`capabilities`](#capabilities) for everything the backend can do beyond the base
/// contract. A layer that must prepare statements takes the full `Driver` instead.
///
/// ```moonbit check
/// test "a repository only needs to run SQL" {
///   let db = @moondb.MockDriver::new()
///   fn count(db : &@moondb.QueryExecutor) -> Int32 raise @moondb.DbError {
///     db.query("SELECT 1", []).length()
///   }
///   assert_eq(count(@moondb.Executor::new(db)), 0)
///   assert_eq(@moondb.Executor::new(db).dialect(), @moondb.Dialect::Sqlite)
/// }
/// ```
pub(open) trait QueryExecutor {
  /// Run a non-row statement with bound `params`.
  fn execute(Self, String, Array[Value]) -> ExecResult raise DbError
  /// Run a row-returning statement with bound `params`; materialise every row.
  fn query(Self, String, Array[Value]) -> Array[Row] raise DbError
  /// Stream a row-returning statement's rows instead of materialising them.
  fn query_stream(Self, String, Array[Value]) -> &Cursor raise DbError
  /// The SQL dialect this backend speaks.
  fn dialect(Self) -> Dialect
  /// What the backend can do beyond the operations above.
  fn capabilities(Self) -> Capabilities
}

///|
/// The narrow view of a [`Driver`]: everything needed to run SQL, and nothing that
/// owns the connection. Obtain one with `Driver::executor`.
pub struct Executor[T] {
  inner : T
}

///|
/// The narrow view over `inner`.
pub fn[T] Executor::new(inner : T) -> Executor[T] {
  { inner, }
}

///|
/// The driver underneath, for the caller that does own the connection.
pub fn[T] Executor::driver(self : Executor[T]) -> T {
  self.inner
}

///|
/// Run a non-row statement. Present as a plain method as well as a trait method so
/// the view reads like the driver it wraps.
pub fn[T : Driver] Executor::execute(
  self : Executor[T],
  sql : String,
  params : Array[Value],
) -> ExecResult raise DbError {
  self.inner.execute(sql, params)
}

///|
/// Run a row-returning statement and materialise every row.
pub fn[T : Driver] Executor::query(
  self : Executor[T],
  sql : String,
  params : Array[Value],
) -> Array[Row] raise DbError {
  self.inner.query(sql, params)
}

///|
/// Stream a row-returning statement's rows.
pub fn[T : Driver] Executor::query_stream(
  self : Executor[T],
  sql : String,
  params : Array[Value],
) -> &Cursor raise DbError {
  self.inner.query_stream(sql, params)
}

///|
/// The SQL dialect of the driver underneath.
pub fn[T : Driver] Executor::dialect(self : Executor[T]) -> Dialect {
  self.inner.dialect()
}

///|
/// What the driver underneath can do beyond the base contract.
pub fn[T : Driver] Executor::capabilities(self : Executor[T]) -> Capabilities {
  Capabilities::for_dialect(self.inner.dialect())
}

///|
/// Every operation forwards to the driver, so the narrow view adds no behaviour of
/// its own — only a smaller set of permissions.
pub impl[T : Driver] QueryExecutor for Executor[T] with fn execute(
  self,
  sql,
  params,
) raise DbError {
  self.inner.execute(sql, params)
}

///|
pub impl[T : Driver] QueryExecutor for Executor[T] with fn query(
  self,
  sql,
  params,
) raise DbError {
  self.inner.query(sql, params)
}

///|
pub impl[T : Driver] QueryExecutor for Executor[T] with fn query_stream(
  self,
  sql,
  params,
) raise DbError {
  self.inner.query_stream(sql, params)
}

///|
pub impl[T : Driver] QueryExecutor for Executor[T] with fn dialect(self) {
  self.inner.dialect()
}

///|
pub impl[T : Driver] QueryExecutor for Executor[T] with fn capabilities(self) {
  Capabilities::for_dialect(self.inner.dialect())
}

///|
/// The asynchronous counterpart of [`QueryExecutor`], for a backend reached over a
/// socket.
pub(open) trait AsyncQueryExecutor {
  /// Run a non-row statement with bound `params`.
  async fn execute(Self, String, Array[Value]) -> ExecResult raise DbError
  /// Run a row-returning statement with bound `params`; materialise every row.
  async fn query(Self, String, Array[Value]) -> Array[Row] raise DbError
  /// Stream a row-returning statement's rows instead of materialising them.
  async fn query_stream(Self, String, Array[Value]) -> &AsyncCursor raise DbError
  /// The SQL dialect this backend speaks.
  fn dialect(Self) -> Dialect
  /// What the backend can do beyond the operations above.
  fn capabilities(Self) -> Capabilities
}

///|
/// The narrow view of an [`AsyncDriver`]. Obtain one with `AsyncExecutor::new`.
pub struct AsyncExecutor[T] {
  inner : T
}

///|
/// The narrow view over `inner`.
pub fn[T] AsyncExecutor::new(inner : T) -> AsyncExecutor[T] {
  { inner, }
}

///|
/// The driver underneath.
pub fn[T] AsyncExecutor::driver(self : AsyncExecutor[T]) -> T {
  self.inner
}

///|
/// Run a non-row statement.
pub async fn[T : AsyncDriver] AsyncExecutor::execute(
  self : AsyncExecutor[T],
  sql : String,
  params : Array[Value],
) -> ExecResult raise DbError {
  self.inner.execute(sql, params)
}

///|
/// Run a row-returning statement and materialise every row.
pub async fn[T : AsyncDriver] AsyncExecutor::query(
  self : AsyncExecutor[T],
  sql : String,
  params : Array[Value],
) -> Array[Row] raise DbError {
  self.inner.query(sql, params)
}

///|
/// Stream a row-returning statement's rows.
pub async fn[T : AsyncDriver] AsyncExecutor::query_stream(
  self : AsyncExecutor[T],
  sql : String,
  params : Array[Value],
) -> &AsyncCursor raise DbError {
  self.inner.query_stream(sql, params)
}

///|
/// The SQL dialect of the driver underneath.
pub fn[T : AsyncDriver] AsyncExecutor::dialect(
  self : AsyncExecutor[T],
) -> Dialect {
  self.inner.dialect()
}

///|
/// What the driver underneath can do beyond the base contract.
pub fn[T : AsyncDriver] AsyncExecutor::capabilities(
  self : AsyncExecutor[T],
) -> Capabilities {
  Capabilities::for_dialect(self.inner.dialect())
}

///|
/// Every operation forwards to the driver.
pub impl[T : AsyncDriver] AsyncQueryExecutor for AsyncExecutor[T] with fn execute(
  self,
  sql,
  params,
) raise DbError {
  self.inner.execute(sql, params)
}

///|
pub impl[T : AsyncDriver] AsyncQueryExecutor for AsyncExecutor[T] with fn query(
  self,
  sql,
  params,
) raise DbError {
  self.inner.query(sql, params)
}

///|
pub impl[T : AsyncDriver] AsyncQueryExecutor for AsyncExecutor[T] with fn query_stream(
  self,
  sql,
  params,
) raise DbError {
  self.inner.query_stream(sql, params)
}

///|
pub impl[T : AsyncDriver] AsyncQueryExecutor for AsyncExecutor[T] with fn dialect(
  self,
) {
  self.inner.dialect()
}

///|
pub impl[T : AsyncDriver] AsyncQueryExecutor for AsyncExecutor[T] with fn capabilities(
  self,
) {
  Capabilities::for_dialect(self.inner.dialect())
}