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

///|
/// A single dialect-neutral database value — the unit of everything that crosses
/// the driver boundary in either direction. Bound parameters travel *in* as
/// `Value`s (never spliced into the SQL string, so binding is injection-safe by
/// construction) and result columns come *back* as `Value`s.
///
/// This is the moondb analogue of Go's `driver.Value` and Python DB-API's input
/// type objects: a small, closed sum every backend can map onto its own wire
/// types. Drivers are expected to normalise their column types down to these
/// seven cases; a query layer built on moondb reads them back through [`Row`]'s
/// typed accessors.
///
/// * `Null`   — SQL `NULL` / a missing value.
/// * `Bool`   — a boolean; backends without a native boolean map `0`/`1`.
/// * `Int`    — a 32-bit signed integer.
/// * `Int64`  — a 64-bit signed integer (rowids, `BIGINT`, counters).
/// * `Double` — an IEEE-754 double (`REAL` / `FLOAT8`).
/// * `Text`   — a UTF-8 string (`TEXT` / `VARCHAR`); dates and times ride here as
///   ISO-8601 text until a dedicated temporal case lands (see the README roadmap).
/// * `Blob`   — an opaque byte string (`BLOB` / `BYTEA`).
pub(all) enum Value {
  Null
  Bool(Bool)
  Int(Int)
  Int64(Int64)
  Double(Double)
  Text(String)
  Blob(Bytes)
} derive(Eq)

///|
/// The name of a value's variant (`"Null"`, `"Int"`, `"Blob"`, …). Used to build
/// legible [`DbError::TypeError`] messages when a typed accessor is asked for the
/// wrong shape.
pub fn Value::kind(self : Value) -> String {
  match self {
    Null => "Null"
    Bool(_) => "Bool"
    Int(_) => "Int"
    Int64(_) => "Int64"
    Double(_) => "Double"
    Text(_) => "Text"
    Blob(_) => "Blob"
  }
}

///|
/// Whether this value is SQL `NULL`.
pub fn Value::is_null(self : Value) -> Bool {
  self is Null
}

///|
/// Render a `Value` for logs and test output. Scalars print their payload; `Text`
/// is quoted; `Blob` shows its length rather than raw bytes.
pub impl Show for Value with fn output(self : Value, logger : &Logger) -> Unit {
  match self {
    Null => logger.write_string("Null")
    Bool(b) => logger.write_string("Bool(" + b.to_string() + ")")
    Int(v) => logger.write_string("Int(" + v.to_string() + ")")
    Int64(v) => logger.write_string("Int64(" + v.to_string() + ")")
    Double(v) => logger.write_string("Double(" + v.to_string() + ")")
    Text(v) => {
      logger.write_string("Text(\"")
      logger.write_string(v)
      logger.write_string("\")")
    }
    Blob(v) => logger.write_string("Blob(" + v.length().to_string() + " bytes)")
  }
}