//! Type-safe row inference for query builders.
//!
//! Provides type-level machinery to infer the Rust return type from a query's
//! selected columns, table, and joins — so `.all()` and `.get()` return the
//! correct type without turbofish annotations.
//!
//! # Architecture
//!
//! ```rust
//! # let _ = r####"
//! .select(cols)    → Marker  (SelectStar | SelectCols<C> | SelectExpr)
//! .from(table)     → R       (Marker + Table → row type via ResolveRow)
//! .join(t2)        → R'      (Marker + R + JoinedTable → new R via AfterJoin)
//! .all()           → Vec<R>  (R: FromDrizzleRow)
//! # "####;
//! ```

// Driver-specific leaf FromDrizzleRow implementations
#[cfg(feature = "libsql")]
mod libsql;
#[cfg(any(feature = "tokio-postgres", feature = "postgres-sync"))]
mod postgres;
#[cfg(feature = "rusqlite")]
mod rusqlite;
// Shared blanket impls for SQLite-flavored drivers whose cells are tagged
// unions (rusqlite, libsql, turso). The driver-specific files above just
// impl `SqliteValueRow` for their row type; everything else lives here.
#[cfg(any(feature = "rusqlite", feature = "libsql", feature = "turso"))]
pub(crate) mod sqlite_value;
#[cfg(feature = "turso")]
mod turso;

use core::marker::PhantomData;

use crate::error::DrizzleError;
use crate::{Cons, Nil};

// =============================================================================
// Select Target Markers
// =============================================================================

/// Marker: `SELECT *` — R inferred from the table, grows with joins.
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectStar;

/// Marker: explicit columns — R inferred from column value types, stable across joins.
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectCols<Cols>(PhantomData<Cols>);

/// Marker: raw SQL or untyped expression — R must be user-specified.
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectExpr;

/// Marker: explicit row model target chosen by user.
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectAs<R>(PhantomData<R>);

/// Marker wrapper that carries in-scope tables.
#[derive(Debug, Clone, Copy, Default)]
pub struct Scoped<Marker, Scope>(PhantomData<(Marker, Scope)>);

/// Declares the set of tables a custom row model requires.
pub trait SelectRequiredTables {
    type RequiredTables;
}

/// Type-level witness that a table exists at the head of a scope list.
#[derive(Debug, Clone, Copy, Default)]
pub struct ScopeHere;

/// Type-level witness that a table exists deeper in a scope list.
#[derive(Debug, Clone, Copy, Default)]
pub struct ScopeThere<Prev>(PhantomData<Prev>);

/// Type-level table membership in a scope list.
pub trait ScopeContains<Table, Witness> {}

impl<Head, Tail> ScopeContains<Head, ScopeHere> for Cons<Head, Tail> {}

impl<Head, Tail, Table, Witness> ScopeContains<Table, ScopeThere<Witness>> for Cons<Head, Tail> where
    Tail: ScopeContains<Table, Witness>
{
}

/// Required-table list satisfaction.
pub trait ScopeSatisfies<Required, Proof> {}

impl<Scope> ScopeSatisfies<Nil, ()> for Scope {}

impl<Scope, Head, Tail, HeadProof, TailProof>
    ScopeSatisfies<Cons<Head, Tail>, (HeadProof, TailProof)> for Scope
where
    Scope: ScopeContains<Head, HeadProof> + ScopeSatisfies<Tail, TailProof>,
{
}

/// Marker-level required-table extraction.
pub trait MarkerRequiredTables {
    type RequiredTables;
}

impl MarkerRequiredTables for SelectStar {
    type RequiredTables = Nil;
}

impl<Cols> MarkerRequiredTables for SelectCols<Cols> {
    type RequiredTables = Nil;
}

impl MarkerRequiredTables for SelectExpr {
    type RequiredTables = Nil;
}

impl<R> MarkerRequiredTables for SelectAs<R>
where
    R: SelectRequiredTables,
{
    type RequiredTables = R::RequiredTables;
}

/// Marker validation for a specific scope-satisfaction proof.
#[diagnostic::on_unimplemented(
    message = "selected row requires tables not present in the current query scope",
    label = "add .join(...) entries for every table referenced by this selector",
    note = "for aliased selectors, use the same alias type in #[from(...)] and .from(...)"
)]
///
/// ```
/// use drizzle_core::{Cons, Nil, Scoped, SelectAs, SelectRequiredTables};
/// use drizzle_core::row::{MarkerScopeValidFor, ScopeHere};
///
/// struct Users;
/// struct Model;
///
/// impl SelectRequiredTables for Model {
///     type RequiredTables = Cons<Users, Nil>;
/// }
///
/// type Good = Scoped<SelectAs<Model>, Cons<Users, Nil>>;
///
/// fn needs_valid<M: MarkerScopeValidFor<(ScopeHere, ())>>() {}
///
/// fn main() {
///     needs_valid::<Good>();
/// }
/// ```
///
/// ```compile_fail
/// use drizzle_core::{Cons, Nil, Scoped, SelectAs, SelectRequiredTables};
/// use drizzle_core::row::{MarkerScopeValidFor, ScopeHere};
///
/// struct Users;
/// struct Posts;
/// struct Model;
///
/// impl SelectRequiredTables for Model {
///     type RequiredTables = Cons<Users, Nil>;
/// }
///
/// type Bad = Scoped<SelectAs<Model>, Cons<Posts, Nil>>;
///
/// fn needs_valid<M: MarkerScopeValidFor<(ScopeHere, ())>>() {}
///
/// fn main() {
///     needs_valid::<Bad>();
/// }
/// ```
pub trait MarkerScopeValidFor<Proof> {}

impl<M, Scope, Proof> MarkerScopeValidFor<Proof> for Scoped<M, Scope>
where
    M: MarkerRequiredTables,
    Scope: ScopeSatisfies<<M as MarkerRequiredTables>::RequiredTables, Proof>,
{
}

// =============================================================================
// Aggregate status validation for SELECT lists
// =============================================================================

/// Fold the aggregate statuses of a tuple of expressions.
///
/// For a 1-tuple, returns the single element's status.
/// For N-tuples, folds pairwise using `CombineAggStatus`.
pub trait AggStatus {
    type Status;
}

// 1-tuple base case
impl<E: crate::expr::HasAggStatus> AggStatus for (E,) {
    type Status = E::Status;
}

// Generate AggStatus for 2..N tuples.
// The `with_col_sizes_*` macros call this incrementally as:
//   callback!(T0; 0)  callback!(T0, T1; 0, 1)  callback!(T0, T1, T2; 0, 1, 2)  ...
// We skip the 1-tuple (handled above) and implement 2+ tuples.
macro_rules! impl_tuple_agg_status {
    // 1-tuple: skip (already implemented above)
    ($E0:ident; $i0:tt) => {};
    // 2-tuple
    ($E0:ident, $E1:ident; $i0:tt, $i1:tt) => {
        impl<$E0, $E1> AggStatus for ($E0, $E1)
        where
            $E0: crate::expr::HasAggStatus,
            $E1: crate::expr::HasAggStatus,
            <$E0 as crate::expr::HasAggStatus>::Status:
                crate::expr::CombineAggStatus<<$E1 as crate::expr::HasAggStatus>::Status>,
        {
            type Status = <<$E0 as crate::expr::HasAggStatus>::Status as
                crate::expr::CombineAggStatus<<$E1 as crate::expr::HasAggStatus>::Status>>::Output;
        }
    };
    // 3+ tuples: fold head element's status with the rest-tuple's status
    ($E0:ident, $E1:ident, $($rest:ident),+; $i0:tt, $i1:tt, $($ri:tt),+) => {
        impl<$E0, $E1, $($rest),+> AggStatus for ($E0, $E1, $($rest),+)
        where
            $E0: crate::expr::HasAggStatus,
            ($E1, $($rest),+): AggStatus,
            <$E0 as crate::expr::HasAggStatus>::Status:
                crate::expr::CombineAggStatus<<($E1, $($rest),+) as AggStatus>::Status>,
        {
            type Status = <<$E0 as crate::expr::HasAggStatus>::Status as
                crate::expr::CombineAggStatus<<($E1, $($rest),+) as AggStatus>::Status>>::Output;
        }
    };
}

with_col_sizes_8!(impl_tuple_agg_status);

#[cfg(any(
    feature = "col16",
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_16!(impl_tuple_agg_status);

#[cfg(any(
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_32!(impl_tuple_agg_status);

#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
with_col_sizes_64!(impl_tuple_agg_status);

#[cfg(any(feature = "col128", feature = "col200"))]
with_col_sizes_128!(impl_tuple_agg_status);

#[cfg(feature = "col200")]
with_col_sizes_200!(impl_tuple_agg_status);

// =============================================================================
// GROUP BY column tracking
// =============================================================================

/// Trait for types that can be passed to `.group_by()`.
///
/// Single columns and tuples of columns implement this.
/// The `Columns` associated type is a `Cons<...>` list of column types
/// for compile-time validation.
pub trait IntoGroupBy<'a, V: crate::SQLParam + 'a>: crate::ToSQL<'a, V> {
    /// Type-level list of grouped columns (e.g., `Cons<Col1, Cons<Col2, Nil>>`).
    type Columns;
}

// Single column → Cons<Self, Nil>
// (Implemented by proc macros for each column ZST)

// Tuple impls: (Col1, Col2) → Cons<Col1, Cons<Col2, Nil>>
macro_rules! impl_into_group_by_tuple {
    // 1-tuple: skip (single column uses direct impl)
    ($T0:ident; $i0:tt) => {};
    // 2-tuple
    ($T0:ident, $T1:ident; $i0:tt, $i1:tt) => {
        impl<'a, V: crate::SQLParam + 'a, $T0, $T1> IntoGroupBy<'a, V> for ($T0, $T1)
        where
            $T0: crate::ToSQL<'a, V>,
            $T1: crate::ToSQL<'a, V>,
        {
            type Columns = Cons<$T0, Cons<$T1, Nil>>;
        }
    };
    // 3+ tuples
    ($T0:ident, $T1:ident, $($rest:ident),+; $i0:tt, $i1:tt, $($ri:tt),+) => {
        impl<'a, V: crate::SQLParam + 'a, $T0, $T1, $($rest),+> IntoGroupBy<'a, V> for ($T0, $T1, $($rest),+)
        where
            $T0: crate::ToSQL<'a, V>,
            $T1: crate::ToSQL<'a, V>,
            $($rest: crate::ToSQL<'a, V>,)+
        {
            type Columns = impl_into_group_by_tuple!(@cons $T0, $T1, $($rest),+);
        }
    };
    // Helper: build nested Cons type
    (@cons $T:ident) => { Cons<$T, Nil> };
    (@cons $T:ident, $($rest:ident),+) => { Cons<$T, impl_into_group_by_tuple!(@cons $($rest),+)> };
}

with_col_sizes_8!(impl_into_group_by_tuple);

#[cfg(any(
    feature = "col16",
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_16!(impl_into_group_by_tuple);

// =============================================================================
// Scalar column validation against grouped columns
// =============================================================================

/// Checks that every scalar column in a `SelectCols` tuple is present in
/// the Grouped column list. Aggregate columns are skipped.
///
/// `Proof` is a witness type inferred by the compiler (like `ScopeContains`).
#[diagnostic::on_unimplemented(
    message = "non-aggregate column in SELECT is not in GROUP BY",
    label = "this column must appear in .group_by(...) or be wrapped in an aggregate function",
    note = "when using GROUP BY, every non-aggregate column in SELECT must be listed in GROUP BY"
)]
pub trait ScalarColumnsIn<Grouped, Proof> {}

/// Aggregate expressions always pass (they don't need to be in GROUP BY).
pub struct AggSkip;

/// Scalar expressions need a `ScopeContains` witness.
pub struct ScalarCheck<W>(core::marker::PhantomData<W>);

// 1-tuple
impl<E, Grouped, Proof> ScalarColumnsIn<Grouped, (Proof,)> for (E,) where
    E: SingleColGroupCheck<Grouped, Proof>
{
}

/// Per-element check: either skip (Agg) or verify (Scalar).
pub trait SingleColGroupCheck<Grouped, Proof> {}

/// Extracts the "base column" identity from an expression for GROUP BY matching.
///
/// `AliasedExpr<Col>` → `Col`, bare column → `Self`.
pub trait GroupByIdentity {
    type Identity;
}

// Default: identity is self (bare column ZSTs)
// (implemented by proc macros for each column ZST)

// AliasedExpr unwraps to inner
impl<E: GroupByIdentity> GroupByIdentity for crate::expr::AliasedExpr<E> {
    type Identity = E::Identity;
}

// SQLExpr: identity is self (for aggregate expressions, this won't be checked anyway)
impl<V: crate::SQLParam, T, N, A> GroupByIdentity for crate::expr::SQLExpr<'_, V, T, N, A>
where
    T: crate::types::DataType,
    N: crate::expr::Nullability,
    A: crate::expr::AggregateKind,
{
    type Identity = Self;
}

// ColumnBinOp: identity is self (complex expressions won't match GROUP BY)
impl<Lhs, Rhs, Op> GroupByIdentity for crate::expr::ColumnBinOp<Lhs, Rhs, Op> {
    type Identity = Self;
}

// ColumnNeg: identity is self
impl<T> GroupByIdentity for crate::expr::ColumnNeg<T> {
    type Identity = Self;
}

// Aggregate expressions → always OK
impl<E, Grouped> SingleColGroupCheck<Grouped, AggSkip> for E where
    E: crate::expr::HasAggStatus<Status = crate::expr::AllAgg>
{
}

// Scalar expressions → base column identity must be in Grouped list
impl<E, Grouped, W> SingleColGroupCheck<Grouped, ScalarCheck<W>> for E
where
    E: crate::expr::HasAggStatus<Status = crate::expr::AllScalar> + GroupByIdentity,
    Grouped: ScopeContains<E::Identity, W>,
{
}

// N-tuple: check head element, recurse on tail
// Uses (HeadProof, TailProof) witness structure, like ScopeSatisfies.

// 2-tuple
impl<T0, T1, Grouped, P0, P1> ScalarColumnsIn<Grouped, (P0, P1)> for (T0, T1)
where
    T0: SingleColGroupCheck<Grouped, P0>,
    T1: SingleColGroupCheck<Grouped, P1>,
{
}

// 3+ tuples: check head, recurse on (T1, T2, ...)
macro_rules! impl_scalar_columns_in {
    // 1-tuple: skip (handled above directly)
    ($T0:ident; $i0:tt) => {};
    // 2-tuple: skip (handled above directly)
    ($T0:ident, $T1:ident; $i0:tt, $i1:tt) => {};
    // 3+ tuples: head + tail recursion
    ($T0:ident, $($rest:ident),+; $i0:tt, $($ri:tt),+) => {
        impl<$T0, $($rest),+, Grouped, HeadProof, TailProof>
            ScalarColumnsIn<Grouped, (HeadProof, TailProof)>
            for ($T0, $($rest),+)
        where
            $T0: SingleColGroupCheck<Grouped, HeadProof>,
            ($($rest,)+): ScalarColumnsIn<Grouped, TailProof>,
        {}
    };
}

with_col_sizes_8!(impl_scalar_columns_in);

#[cfg(any(
    feature = "col16",
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_16!(impl_scalar_columns_in);

// =============================================================================
// MarkerAggValidFor — top-level bound on terminal methods
// =============================================================================

/// Validates that the SELECT list is legal given the grouped column set.
///
/// - `Grouped = ()` (no GROUP BY): everything is valid
/// - `Grouped = Cons<...>` (has GROUP BY): scalar columns must be in the list
#[diagnostic::on_unimplemented(
    message = "non-aggregate column in SELECT is not in GROUP BY",
    label = "add this column to .group_by(...) or wrap it in an aggregate function"
)]
pub trait MarkerAggValidFor<Grouped, Proof = ()> {}

// No GROUP BY (Grouped = ()) → always valid, any mix is fine
impl<Mk> MarkerAggValidFor<()> for Mk {}

// SelectStar with GROUP BY: can't check at compile time, always passes
impl<Scope, Head, Tail> MarkerAggValidFor<Cons<Head, Tail>> for Scoped<SelectStar, Scope> {}

// SelectExpr with GROUP BY: can't check, always passes
impl<Scope, Head, Tail> MarkerAggValidFor<Cons<Head, Tail>> for Scoped<SelectExpr, Scope> {}

// SelectAs with GROUP BY: user-specified type, always passes
impl<Scope, R, Head, Tail> MarkerAggValidFor<Cons<Head, Tail>> for Scoped<SelectAs<R>, Scope> {}

// SelectCols with GROUP BY: check each scalar column is in the Grouped list
impl<Scope, Cols, Head, Tail, Proof> MarkerAggValidFor<Cons<Head, Tail>, Proof>
    for Scoped<SelectCols<Cols>, Scope>
where
    Cols: ScalarColumnsIn<Cons<Head, Tail>, Proof>,
{
}

// =============================================================================
// Marker column-count validation for strict decode paths
// =============================================================================

/// Type-level column-list representation for a row decode target.
///
/// Each consumed column is represented by a `Cons<T, ...>` node where `T`
/// is the decoded Rust type for that column.
pub trait RowColumnList<Row: ?Sized> {
    type Columns: crate::TypeSet;
}

/// Type-level column-list representation for selected column tuples.
pub trait SelectedColumnList {
    type Columns: crate::TypeSet;
}

trait SameType<T> {}
impl<T> SameType<T> for T {}

trait ColumnTypeCompatible<Row: ?Sized, Expected, Actual> {}

impl<Row: ?Sized, T> ColumnTypeCompatible<Row, T, T> for () {}

trait TypeListCompatible<Row: ?Sized, ActualList> {}

impl<Row: ?Sized> TypeListCompatible<Row, Self> for crate::Nil {}

impl<Row: ?Sized, EH, ET, AH, AT> TypeListCompatible<Row, crate::Cons<AH, AT>>
    for crate::Cons<EH, ET>
where
    (): ColumnTypeCompatible<Row, EH, AH>,
    ET: TypeListCompatible<Row, AT>,
{
}

trait SqliteDecodeRow {}

#[cfg(feature = "rusqlite")]
impl SqliteDecodeRow for ::rusqlite::Row<'_> {}

#[cfg(feature = "libsql")]
impl SqliteDecodeRow for ::libsql::Row {}

#[cfg(feature = "turso")]
impl SqliteDecodeRow for ::turso::Row {}

macro_rules! impl_sqlite_integer_decode_compat {
    ($expected:ty => $($actual:ty),+ $(,)?) => {
        $(
            impl<Row> ColumnTypeCompatible<Row, $expected, $actual> for ()
            where
                Row: SqliteDecodeRow,
            {
            }
        )+
    };
}

impl_sqlite_integer_decode_compat!(
    i64 => i8, i16, i32, isize, u8, u16, u32, u64, usize, bool
);

impl_sqlite_integer_decode_compat!(
    Option<i64> =>
        Option<i8>,
        Option<i16>,
        Option<i32>,
        Option<isize>,
        Option<u8>,
        Option<u16>,
        Option<u32>,
        Option<u64>,
        Option<usize>,
        Option<bool>
);

macro_rules! impl_row_column_list_one {
    ($($ty:ty),+ $(,)?) => {
        $(
            impl<Row: ?Sized> RowColumnList<Row> for $ty {
                type Columns = crate::Cons<$ty, crate::Nil>;
            }
        )+
    };
}

impl_row_column_list_one!(
    i8,
    i16,
    i32,
    i64,
    isize,
    u8,
    u16,
    u32,
    u64,
    usize,
    f32,
    f64,
    bool,
    crate::prelude::String,
    crate::prelude::Vec<u8>
);

impl<Row: ?Sized> RowColumnList<Row> for () {
    type Columns = crate::Cons<(), crate::Nil>;
}

#[cfg(feature = "uuid")]
impl<Row: ?Sized> RowColumnList<Row> for uuid::Uuid {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "chrono")]
impl<Row: ?Sized> RowColumnList<Row> for chrono::NaiveDate {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "chrono")]
impl<Row: ?Sized> RowColumnList<Row> for chrono::NaiveTime {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "chrono")]
impl<Row: ?Sized> RowColumnList<Row> for chrono::NaiveDateTime {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "chrono")]
impl<Row: ?Sized> RowColumnList<Row> for chrono::DateTime<chrono::Utc> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "serde")]
impl<Row: ?Sized> RowColumnList<Row> for serde_json::Value {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "rust-decimal")]
impl<Row: ?Sized> RowColumnList<Row> for rust_decimal::Decimal {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "chrono")]
impl<Row: ?Sized> RowColumnList<Row> for chrono::Duration {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "cidr")]
impl<Row: ?Sized> RowColumnList<Row> for cidr::IpInet {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "cidr")]
impl<Row: ?Sized> RowColumnList<Row> for cidr::IpCidr {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "geo-types")]
impl<Row: ?Sized> RowColumnList<Row> for geo_types::Point<f64> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "geo-types")]
impl<Row: ?Sized> RowColumnList<Row> for geo_types::LineString<f64> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "geo-types")]
impl<Row: ?Sized> RowColumnList<Row> for geo_types::Rect<f64> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "bit-vec")]
impl<Row: ?Sized> RowColumnList<Row> for bit_vec::BitVec {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "arrayvec")]
impl<Row: ?Sized, const N: usize> RowColumnList<Row> for arrayvec::ArrayString<N> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "arrayvec")]
impl<Row: ?Sized, T, const N: usize> RowColumnList<Row> for arrayvec::ArrayVec<T, N> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

impl<Row: ?Sized> RowColumnList<Row> for compact_str::CompactString {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "bytes")]
impl<Row: ?Sized> RowColumnList<Row> for bytes::Bytes {
    type Columns = crate::Cons<Self, crate::Nil>;
}

#[cfg(feature = "bytes")]
impl<Row: ?Sized> RowColumnList<Row> for bytes::BytesMut {
    type Columns = crate::Cons<Self, crate::Nil>;
}

impl<Row: ?Sized, A: smallvec::Array> RowColumnList<Row> for smallvec::SmallVec<A> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

impl<Row: ?Sized, T> RowColumnList<Row> for Option<T> {
    type Columns = crate::Cons<Self, crate::Nil>;
}

impl<Row: ?Sized, A> RowColumnList<Row> for (A,)
where
    A: RowColumnList<Row>,
{
    type Columns = <A as RowColumnList<Row>>::Columns;
}

impl<Row: ?Sized, A, B> RowColumnList<Row> for (A, B)
where
    A: RowColumnList<Row>,
    B: RowColumnList<Row>,
    <A as RowColumnList<Row>>::Columns: crate::Concat<<B as RowColumnList<Row>>::Columns>,
{
    type Columns = <<A as RowColumnList<Row>>::Columns as crate::Concat<
        <B as RowColumnList<Row>>::Columns,
    >>::Output;
}

impl<Row: ?Sized, A, B, C> RowColumnList<Row> for (A, B, C)
where
    A: RowColumnList<Row>,
    B: RowColumnList<Row>,
    C: RowColumnList<Row>,
    <A as RowColumnList<Row>>::Columns: crate::Concat<<B as RowColumnList<Row>>::Columns>,
    <<A as RowColumnList<Row>>::Columns as crate::Concat<<B as RowColumnList<Row>>::Columns>>::Output:
        crate::Concat<<C as RowColumnList<Row>>::Columns>,
{
    type Columns = <<<A as RowColumnList<Row>>::Columns as crate::Concat<
        <B as RowColumnList<Row>>::Columns,
    >>::Output as crate::Concat<<C as RowColumnList<Row>>::Columns>>::Output;
}

impl<Row: ?Sized, A, B, C, D> RowColumnList<Row> for (A, B, C, D)
where
    A: RowColumnList<Row>,
    B: RowColumnList<Row>,
    C: RowColumnList<Row>,
    D: RowColumnList<Row>,
    (A, B, C): RowColumnList<Row>,
    <(A, B, C) as RowColumnList<Row>>::Columns: crate::Concat<<D as RowColumnList<Row>>::Columns>,
{
    type Columns = <<(A, B, C) as RowColumnList<Row>>::Columns as crate::Concat<
        <D as RowColumnList<Row>>::Columns,
    >>::Output;
}

impl<Row: ?Sized, A, B, C, D, E> RowColumnList<Row> for (A, B, C, D, E)
where
    E: RowColumnList<Row>,
    (A, B, C, D): RowColumnList<Row>,
    <(A, B, C, D) as RowColumnList<Row>>::Columns:
        crate::Concat<<E as RowColumnList<Row>>::Columns>,
{
    type Columns = <<(A, B, C, D) as RowColumnList<Row>>::Columns as crate::Concat<
        <E as RowColumnList<Row>>::Columns,
    >>::Output;
}

impl<Row: ?Sized, A, B, C, D, E, F> RowColumnList<Row> for (A, B, C, D, E, F)
where
    F: RowColumnList<Row>,
    (A, B, C, D, E): RowColumnList<Row>,
    <(A, B, C, D, E) as RowColumnList<Row>>::Columns:
        crate::Concat<<F as RowColumnList<Row>>::Columns>,
{
    type Columns = <<(A, B, C, D, E) as RowColumnList<Row>>::Columns as crate::Concat<
        <F as RowColumnList<Row>>::Columns,
    >>::Output;
}

impl<Row: ?Sized, A, B, C, D, E, F, G> RowColumnList<Row> for (A, B, C, D, E, F, G)
where
    G: RowColumnList<Row>,
    (A, B, C, D, E, F): RowColumnList<Row>,
    <(A, B, C, D, E, F) as RowColumnList<Row>>::Columns:
        crate::Concat<<G as RowColumnList<Row>>::Columns>,
{
    type Columns = <<(A, B, C, D, E, F) as RowColumnList<Row>>::Columns as crate::Concat<
        <G as RowColumnList<Row>>::Columns,
    >>::Output;
}

impl<Row: ?Sized, A, B, C, D, E, F, G, H> RowColumnList<Row> for (A, B, C, D, E, F, G, H)
where
    H: RowColumnList<Row>,
    (A, B, C, D, E, F, G): RowColumnList<Row>,
    <(A, B, C, D, E, F, G) as RowColumnList<Row>>::Columns:
        crate::Concat<<H as RowColumnList<Row>>::Columns>,
{
    type Columns = <<(A, B, C, D, E, F, G) as RowColumnList<Row>>::Columns as crate::Concat<
        <H as RowColumnList<Row>>::Columns,
    >>::Output;
}

/// Marker-level column-count compatibility check used by strict `.all()` / `.get()`.
///
/// Currently enforced for `SelectCols<_>` where selected shape is explicit.
#[diagnostic::on_unimplemented(
    message = "selected shape does not match decode target `{Actual}`",
    label = "this decode target is not type-compatible with .select(...) output",
    note = "use typed expressions or derive FromRow for explicit remapping when selecting custom expressions"
)]
pub trait MarkerColumnCountValid<Row: ?Sized, Inferred, Actual> {}

/// Marker-level guard for strict decode entry points.
///
/// Raw `SelectExpr` (`select(sql!(...))`) is intentionally excluded so strict
/// decode requires either typed expressions (`raw_non_null`, `sql!(.., Type)`) or
/// explicit remapping via typed expressions or `FromRow` derive.
#[diagnostic::on_unimplemented(
    message = "raw select expressions require explicit typing in strict decode",
    label = "`select(sql!(...)).all()/get()` is not allowed in strict mode",
    note = "use typed wrappers like `raw_non_null`/`raw_nullable` or derive FromRow"
)]
pub trait StrictDecodeMarker {}

impl StrictDecodeMarker for SelectStar {}
impl<Cols> StrictDecodeMarker for SelectCols<Cols> {}
impl<R> StrictDecodeMarker for SelectAs<R> {}
impl<M, Scope> StrictDecodeMarker for Scoped<M, Scope> where M: StrictDecodeMarker {}

impl<Row: ?Sized, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual> for SelectStar {}

impl<Row: ?Sized, Cols, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual>
    for SelectCols<Cols>
where
    Cols: SelectedColumnList,
    Actual: RowColumnList<Row>,
    <Cols as SelectedColumnList>::Columns:
        TypeListCompatible<Row, <Actual as RowColumnList<Row>>::Columns>,
{
}

impl<Row: ?Sized, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual> for SelectExpr where
    Inferred: SameType<Actual>
{
}

impl<Row: ?Sized, R, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual>
    for SelectAs<R>
{
}

impl<M, Scope, Row: ?Sized, Inferred, Actual> MarkerColumnCountValid<Row, Inferred, Actual>
    for Scoped<M, Scope>
where
    M: MarkerColumnCountValid<Row, Inferred, Actual>,
{
}

/// Pushes a joined table into the marker scope.
pub trait ScopePush<Joined> {
    type Out;
}

impl<M, Scope, Joined> ScopePush<Joined> for Scoped<M, Scope> {
    type Out = Scoped<M, Cons<Joined, Scope>>;
}

/// Marker-directed row decoding for `.all()`/`.get()`.
pub trait DecodeSelectedRef<RowRef, R> {
    /// Decode the row into `R` according to the marker.
    ///
    /// # Errors
    ///
    /// Returns an error if the row cannot be decoded into the expected type
    /// (missing columns, type mismatch, or downstream conversion failure).
    fn decode(row: RowRef) -> Result<R, DrizzleError>;
}

impl<RowRef, R> DecodeSelectedRef<RowRef, R> for SelectAs<R>
where
    R: TryFrom<RowRef>,
    <R as TryFrom<RowRef>>::Error: Into<DrizzleError>,
{
    fn decode(row: RowRef) -> Result<R, DrizzleError> {
        R::try_from(row).map_err(Into::into)
    }
}

impl<RowRef, R, M, Scope> DecodeSelectedRef<RowRef, R> for Scoped<M, Scope>
where
    M: DecodeSelectedRef<RowRef, R>,
{
    fn decode(row: RowRef) -> Result<R, DrizzleError> {
        M::decode(row)
    }
}

impl<RowRef, Row: ?Sized, R> DecodeSelectedRef<RowRef, R> for SelectStar
where
    RowRef: core::ops::Deref<Target = Row>,
    R: FromDrizzleRow<Row>,
{
    fn decode(row: RowRef) -> Result<R, DrizzleError> {
        R::from_row(&*row)
    }
}

impl<RowRef, Row: ?Sized, Cols, R> DecodeSelectedRef<RowRef, R> for SelectCols<Cols>
where
    RowRef: core::ops::Deref<Target = Row>,
    R: FromDrizzleRow<Row>,
{
    fn decode(row: RowRef) -> Result<R, DrizzleError> {
        R::from_row(&*row)
    }
}

impl<RowRef, Row: ?Sized, R> DecodeSelectedRef<RowRef, R> for SelectExpr
where
    RowRef: core::ops::Deref<Target = Row>,
    R: FromDrizzleRow<Row>,
{
    fn decode(row: RowRef) -> Result<R, DrizzleError> {
        R::from_row(&*row)
    }
}

// =============================================================================
// FromDrizzleRow — offset-based row extraction
// =============================================================================

/// Extracts a Rust value from a database row at a given column offset.
///
/// Unlike `TryFrom<Row>`, supports offset-based reading so joined results
/// can split a single row across multiple model types.
///
/// Tuple impls compose: `(A, B)` reads A at `offset`, then B at
/// `offset + A::COLUMN_COUNT`.
#[diagnostic::on_unimplemented(
    message = "cannot deserialize `{Self}` from a database row",
    label = "this type does not implement FromDrizzleRow",
    note = "derive #[SQLiteFromRow] or #[PostgresFromRow]"
)]
pub trait FromDrizzleRow<Row: ?Sized>: Sized {
    /// Number of columns this type reads from the row.
    const COLUMN_COUNT: usize;

    /// Read this type from `row` starting at column `offset`.
    ///
    /// # Errors
    ///
    /// Returns an error if any column from `offset` through
    /// `offset + COLUMN_COUNT - 1` cannot be read or converted.
    fn from_row_at(row: &Row, offset: usize) -> Result<Self, DrizzleError>;

    /// Read from offset 0.
    ///
    /// # Errors
    ///
    /// Returns an error if the row cannot be decoded — see [`Self::from_row_at`].
    fn from_row(row: &Row) -> Result<Self, DrizzleError> {
        Self::from_row_at(row, 0)
    }
}

/// Trait for composite (multi-column) row types that support NULL probing.
///
/// Implementing this trait enables `Option<T>` to work as a `FromDrizzleRow`
/// target, used for LEFT JOIN results where the joined table may be absent
/// (all columns NULL).
///
/// Proc macros generate this for each `SelectModel`. Leaf types (i32, String,
/// etc.) use concrete `Option<T>` impls instead.
pub trait NullProbeRow<Row: ?Sized>: FromDrizzleRow<Row> {
    /// Returns `true` if the first column at `offset` is NULL.
    ///
    /// # Errors
    ///
    /// Returns an error if the row cannot be inspected at `offset` (e.g. the
    /// driver reports an out-of-range index or conversion failure).
    fn is_null_at(row: &Row, offset: usize) -> Result<bool, DrizzleError>;
}

// -- Tuple impls: generic over Row, composing inner impls --

macro_rules! impl_from_drizzle_row_tuple {
    ($($T:ident),+; $($idx:tt),+) => {
        impl<__Row: ?Sized, $($T: FromDrizzleRow<__Row>),+> FromDrizzleRow<__Row> for ($($T,)+) {
            const COLUMN_COUNT: usize = 0 $(+ <$T as FromDrizzleRow<__Row>>::COLUMN_COUNT)+;

            #[allow(non_snake_case)]
            fn from_row_at(
                row: &__Row,
                offset: usize,
            ) -> Result<Self, DrizzleError> {
                let mut __off = offset;
                $(
                    let $T = <$T as FromDrizzleRow<__Row>>::from_row_at(row, __off)?;
                    __off += <$T as FromDrizzleRow<__Row>>::COLUMN_COUNT;
                )+
                Ok(($($T,)+))
            }
        }
    };
}

with_col_sizes_8!(impl_from_drizzle_row_tuple);

#[cfg(any(
    feature = "col16",
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_16!(impl_from_drizzle_row_tuple);

#[cfg(any(
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_32!(impl_from_drizzle_row_tuple);

#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
with_col_sizes_64!(impl_from_drizzle_row_tuple);

#[cfg(any(feature = "col128", feature = "col200"))]
with_col_sizes_128!(impl_from_drizzle_row_tuple);

#[cfg(feature = "col200")]
with_col_sizes_200!(impl_from_drizzle_row_tuple);

// =============================================================================
// SQLTypeToRust — SQL type marker × dialect → canonical Rust type
// =============================================================================

/// Maps a dialect-native SQL type marker to its canonical Rust type.
///
/// Parameterized by `D` (a dialect marker such as
/// [`SQLiteDialect`](crate::dialect::SQLiteDialect) or
/// [`PostgresDialect`](crate::dialect::PostgresDialect)) so that
/// type mappings can differ per database.
///
/// Each dialect's native type markers (e.g., `sqlite::types::Integer`,
/// `postgres::types::Int4`) implement this trait for their respective dialect.
///
/// Feature-gated types (`chrono`, `uuid`, `serde`) provide mappings when
/// the feature is enabled. Without the feature there is **no impl**,
/// producing a compile error that guides the user.
#[diagnostic::on_unimplemented(
    message = "SQL type `{Self}` has no default Rust mapping for dialect `{D}`",
    label = "this SQL type has no default Rust mapping for this dialect",
    note = "enable `chrono` for Date/Time/Timestamp/TimestampTz, `uuid` for Uuid, or `serde` for Json/Jsonb"
)]
pub trait SQLTypeToRust<D> {
    type RustType;
}

// -- Dialect-native mappings ---------------------------------------------------

use crate::dialect::{PostgresDialect, SQLiteDialect};

impl<D, T> SQLTypeToRust<D> for crate::types::Array<T>
where
    T: crate::types::DataType + SQLTypeToRust<D>,
{
    type RustType = crate::prelude::Vec<<T as SQLTypeToRust<D>>::RustType>;
}

impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Integer {
    type RustType = i64;
}

impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Text {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Real {
    type RustType = f64;
}

impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Blob {
    type RustType = crate::prelude::Vec<u8>;
}

impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Numeric {
    type RustType = f64;
}

impl SQLTypeToRust<SQLiteDialect> for drizzle_types::sqlite::types::Any {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Int2 {
    type RustType = i16;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Int4 {
    type RustType = i32;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Int8 {
    type RustType = i64;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Float4 {
    type RustType = f32;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Float8 {
    type RustType = f64;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Varchar {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Text {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Char {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Bytea {
    type RustType = crate::prelude::Vec<u8>;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Boolean {
    type RustType = bool;
}

#[cfg(feature = "rust-decimal")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Numeric {
    type RustType = rust_decimal::Decimal;
}

#[cfg(not(feature = "rust-decimal"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Numeric {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Any {
    type RustType = crate::prelude::String;
}

#[cfg(feature = "chrono")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamptz {
    type RustType = chrono::DateTime<chrono::Utc>;
}

#[cfg(feature = "chrono")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timestamp {
    type RustType = chrono::NaiveDateTime;
}

#[cfg(feature = "chrono")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Date {
    type RustType = chrono::NaiveDate;
}

#[cfg(feature = "chrono")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Time {
    type RustType = chrono::NaiveTime;
}

#[cfg(feature = "chrono")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Timetz {
    type RustType = chrono::NaiveTime;
}

#[cfg(feature = "uuid")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Uuid {
    type RustType = uuid::Uuid;
}

#[cfg(feature = "serde")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Json {
    type RustType = serde_json::Value;
}

#[cfg(feature = "serde")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Jsonb {
    type RustType = serde_json::Value;
}

// -- Feature-gated type marker mappings --

#[cfg(feature = "chrono")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Interval {
    type RustType = chrono::Duration;
}

#[cfg(not(feature = "chrono"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Interval {
    type RustType = crate::prelude::String;
}

#[cfg(feature = "cidr")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Inet {
    type RustType = cidr::IpInet;
}

#[cfg(not(feature = "cidr"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Inet {
    type RustType = crate::prelude::String;
}

#[cfg(feature = "cidr")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Cidr {
    type RustType = cidr::IpCidr;
}

#[cfg(not(feature = "cidr"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Cidr {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::MacAddr {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::MacAddr8 {
    type RustType = crate::prelude::String;
}

#[cfg(feature = "geo-types")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Point {
    type RustType = geo_types::Point<f64>;
}

#[cfg(not(feature = "geo-types"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Point {
    type RustType = crate::prelude::String;
}

#[cfg(feature = "geo-types")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::LineString {
    type RustType = geo_types::LineString<f64>;
}

#[cfg(not(feature = "geo-types"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::LineString {
    type RustType = crate::prelude::String;
}

#[cfg(feature = "geo-types")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Rect {
    type RustType = geo_types::Rect<f64>;
}

#[cfg(not(feature = "geo-types"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Rect {
    type RustType = crate::prelude::String;
}

#[cfg(feature = "bit-vec")]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::BitString {
    type RustType = bit_vec::BitVec;
}

#[cfg(not(feature = "bit-vec"))]
impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::BitString {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Line {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::LineSegment {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Polygon {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Circle {
    type RustType = crate::prelude::String;
}

impl SQLTypeToRust<PostgresDialect> for drizzle_types::postgres::types::Enum {
    type RustType = crate::prelude::String;
}

// =============================================================================
// WrapNullable — Option<T> wrapping based on nullability
// =============================================================================

/// Wraps a Rust type in `Option<T>` when nullable.
pub trait WrapNullable<T> {
    type Output;
}

impl<T> WrapNullable<T> for crate::expr::NonNull {
    type Output = T;
}

impl<T> WrapNullable<T> for crate::expr::Null {
    type Output = Option<T>;
}

// =============================================================================
// ExprValueType — "what Rust type does this expression produce?"
// =============================================================================

/// Resolves the Rust value type for a column or typed expression.
///
/// Implemented for:
/// - Column ZSTs (proc macro generates alongside `ColumnValueType`)
/// - `SQLExpr<T, N, A>` where `T: SQLTypeToRust<D>` and `N: WrapNullable`
///
/// For `SQL<'a, V>` (raw SQL), `ValueType = ()` — the user must specify
/// the concrete row type via turbofish (`.all::<T>()`).
#[diagnostic::on_unimplemented(
    message = "cannot infer Rust type for expression `{Self}`",
    label = "use typed expressions or derive FromRow to specify the Rust type",
    note = "raw SQL and JSON expressions require explicit type annotation"
)]
pub trait ExprValueType {
    type ValueType;
}

impl<V: crate::SQLParam, T, N, A> ExprValueType for crate::expr::SQLExpr<'_, V, T, N, A>
where
    T: crate::types::DataType + SQLTypeToRust<V::DialectMarker>,
    N: crate::expr::Nullability + WrapNullable<<T as SQLTypeToRust<V::DialectMarker>>::RustType>,
    A: crate::expr::AggregateKind,
{
    type ValueType = <N as WrapNullable<<T as SQLTypeToRust<V::DialectMarker>>::RustType>>::Output;
}

/// Raw SQL fallback — value type is `()`, user must specify the concrete type.
impl<V: crate::SQLParam> ExprValueType for crate::sql::SQL<'_, V> {
    type ValueType = ();
}

// =============================================================================
// HasSelectModel — table → Select model (lifetime-free)
// =============================================================================

/// Associates a table with its Select model type and column count.
///
/// Generated by `#[SQLiteTable]` / `#[PostgresTable]` alongside `SQLTable`.
#[diagnostic::on_unimplemented(
    message = "`{Self}` is not a drizzle table",
    label = "ensure this type was derived with #[SQLiteTable] or #[PostgresTable]"
)]
pub trait HasSelectModel {
    type SelectModel;
    const COLUMN_COUNT: usize;
}

// =============================================================================
// ResolveRow — Marker + Table → default row type R
// =============================================================================

/// Given a select marker and a table, determines the default row type R.
/// Evaluated at `.from(table)` time.
#[diagnostic::on_unimplemented(
    message = "cannot resolve return type for this query",
    label = "the selected columns and table do not produce a known row type"
)]
pub trait ResolveRow<Table> {
    type Row;
}

impl<T: HasSelectModel> ResolveRow<T> for SelectStar {
    type Row = T::SelectModel;
}

impl<T> ResolveRow<T> for SelectExpr {
    type Row = ();
}

impl<R, T> ResolveRow<T> for SelectAs<R>
where
    R: SelectAsFrom<T>,
{
    type Row = R;
}

impl<M, Scope, T> ResolveRow<T> for Scoped<M, Scope>
where
    M: ResolveRow<T>,
{
    type Row = M::Row;
}

/// Compile-time constraint for `.select(MyRow::Select).from(table)` base table matching.
///
/// `#[from(Table)]` on `*FromRow` structs emits `impl SelectAsFrom<Table> for MyRow`.
/// Structs without `#[from(...)]` may opt into any table.
#[diagnostic::on_unimplemented(
    message = "row selector `{Self}` cannot be used with table `{Table}`",
    label = "the #[from(...)] table does not match .from(...)",
    note = "set #[from(TheTable)] to the same table passed to .from(...)"
)]
pub trait SelectAsFrom<Table> {}

// -- SelectCols: column value types → row tuple --

macro_rules! impl_resolve_row_cols {
    ($($T:ident),+; $($idx:tt),+) => {
        impl<__Table, $($T: ExprValueType),+> ResolveRow<__Table> for SelectCols<($($T,)+)> {
            type Row = ($(<$T as ExprValueType>::ValueType,)+);
        }
    };
}

with_col_sizes_8!(impl_resolve_row_cols);

#[cfg(any(
    feature = "col16",
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_16!(impl_resolve_row_cols);

#[cfg(any(
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_32!(impl_resolve_row_cols);

#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
with_col_sizes_64!(impl_resolve_row_cols);

#[cfg(any(feature = "col128", feature = "col200"))]
with_col_sizes_128!(impl_resolve_row_cols);

#[cfg(feature = "col200")]
with_col_sizes_200!(impl_resolve_row_cols);

macro_rules! selected_columns_cons {
    () => {
        crate::Nil
    };
    ($head:ident $(, $tail:ident)*) => {
        crate::Cons<<$head as ExprValueType>::ValueType, selected_columns_cons!($($tail),*)>
    };
}

macro_rules! impl_selected_column_list_tuple {
    ($($T:ident),+; $($idx:tt),+) => {
        impl<$($T: ExprValueType),+> SelectedColumnList for ($($T,)+) {
            type Columns = selected_columns_cons!($($T),+);
        }
    };
}

with_col_sizes_8!(impl_selected_column_list_tuple);

#[cfg(any(
    feature = "col16",
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_16!(impl_selected_column_list_tuple);

#[cfg(any(
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_32!(impl_selected_column_list_tuple);

#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
with_col_sizes_64!(impl_selected_column_list_tuple);

#[cfg(any(feature = "col128", feature = "col200"))]
with_col_sizes_128!(impl_selected_column_list_tuple);

#[cfg(feature = "col200")]
with_col_sizes_200!(impl_selected_column_list_tuple);

// =============================================================================
// AfterJoin — how joins transform the row type
// =============================================================================

/// Determines the new row type after a JOIN.
pub trait AfterJoin<CurrentRow, JoinedTable> {
    type NewRow;
}

/// Determines the new row type after a LEFT JOIN.
pub trait AfterLeftJoin<CurrentRow, JoinedTable> {
    type NewRow;
}

/// Determines the new row type after a RIGHT JOIN.
pub trait AfterRightJoin<CurrentRow, JoinedTable> {
    type NewRow;
}

/// Determines the new row type after a FULL JOIN.
pub trait AfterFullJoin<CurrentRow, JoinedTable> {
    type NewRow;
}

/// `SELECT *` + JOIN → `(CurrentRow, JoinedTable::SelectModel)`.
impl<R, T: HasSelectModel> AfterJoin<R, T> for SelectStar {
    type NewRow = (R, T::SelectModel);
}

/// `SELECT *` + LEFT JOIN → `(CurrentRow, Option<JoinedTable::SelectModel>)`.
impl<R, T: HasSelectModel> AfterLeftJoin<R, T> for SelectStar {
    type NewRow = (R, Option<T::SelectModel>);
}

/// `SELECT *` + RIGHT JOIN → `(Option<CurrentRow>, JoinedTable::SelectModel)`.
impl<R, T: HasSelectModel> AfterRightJoin<R, T> for SelectStar {
    type NewRow = (Option<R>, T::SelectModel);
}

/// `SELECT *` + FULL JOIN → `(Option<CurrentRow>, Option<JoinedTable::SelectModel>)`.
impl<R, T: HasSelectModel> AfterFullJoin<R, T> for SelectStar {
    type NewRow = (Option<R>, Option<T::SelectModel>);
}

/// Explicit columns + JOIN → R unchanged.
impl<Cols, R, T> AfterJoin<R, T> for SelectCols<Cols> {
    type NewRow = R;
}

impl<Cols, R, T> AfterLeftJoin<R, T> for SelectCols<Cols> {
    type NewRow = R;
}

impl<Cols, R, T> AfterRightJoin<R, T> for SelectCols<Cols> {
    type NewRow = R;
}

impl<Cols, R, T> AfterFullJoin<R, T> for SelectCols<Cols> {
    type NewRow = R;
}

/// Raw/untyped + JOIN → R unchanged.
impl<R, T> AfterJoin<R, T> for SelectExpr {
    type NewRow = R;
}

impl<R, T> AfterLeftJoin<R, T> for SelectExpr {
    type NewRow = R;
}

impl<R, T> AfterRightJoin<R, T> for SelectExpr {
    type NewRow = R;
}

impl<R, T> AfterFullJoin<R, T> for SelectExpr {
    type NewRow = R;
}

/// Explicit model + JOIN → R unchanged.
impl<Row, R, T> AfterJoin<R, T> for SelectAs<Row> {
    type NewRow = R;
}

impl<Row, R, T> AfterLeftJoin<R, T> for SelectAs<Row> {
    type NewRow = R;
}

impl<Row, R, T> AfterRightJoin<R, T> for SelectAs<Row> {
    type NewRow = R;
}

impl<Row, R, T> AfterFullJoin<R, T> for SelectAs<Row> {
    type NewRow = R;
}

impl<M, Scope, R, T> AfterJoin<R, T> for Scoped<M, Scope>
where
    M: AfterJoin<R, T>,
{
    type NewRow = M::NewRow;
}

impl<M, Scope, R, T> AfterLeftJoin<R, T> for Scoped<M, Scope>
where
    M: AfterLeftJoin<R, T>,
{
    type NewRow = M::NewRow;
}

impl<M, Scope, R, T> AfterRightJoin<R, T> for Scoped<M, Scope>
where
    M: AfterRightJoin<R, T>,
{
    type NewRow = M::NewRow;
}

impl<M, Scope, R, T> AfterFullJoin<R, T> for Scoped<M, Scope>
where
    M: AfterFullJoin<R, T>,
{
    type NewRow = M::NewRow;
}

// =============================================================================
// IntoSelectTarget — select arguments → Marker type
// =============================================================================

/// Determines the select marker from what was passed to `.select()`.
///
/// The marker controls how row types are inferred:
/// - `SelectStar` — infer R from the table's Select model
/// - `SelectCols<C>` — infer R from the column value types
/// - `SelectExpr` — R must be specified by the user
///
/// Implemented automatically for:
/// - `()` → `SelectStar`
/// - `SQL<'a, V>` → `SelectExpr`
/// - `SQLExpr<'a, V, T, N, A>` → `SelectCols<(Self,)>`
/// - Tuples `(A, B, ...)` → `SelectCols<(A, B, ...)>`
/// - Column ZSTs (proc macro generated)
/// - Table structs (proc macro generated) → `SelectStar`
#[diagnostic::on_unimplemented(
    message = "`{Self}` cannot be used as a select target",
    label = "this type does not implement IntoSelectTarget",
    note = "implement IntoSelectTarget or use a column, table, or typed expression"
)]
pub trait IntoSelectTarget {
    type Marker;
}

/// `select(())` → `SelectStar` — infer row type from the table.
impl IntoSelectTarget for () {
    type Marker = SelectStar;
}

/// `select(sql!(...))` → `SelectExpr` — user must specify row type.
impl<V: crate::SQLParam> IntoSelectTarget for crate::sql::SQL<'_, V> {
    type Marker = SelectExpr;
}

/// `select(typed_expr)` → `SelectCols<(Expr,)>` — single typed expression.
impl<V: crate::SQLParam, T, N, A> IntoSelectTarget for crate::expr::SQLExpr<'_, V, T, N, A>
where
    T: crate::types::DataType,
    N: crate::expr::Nullability,
    A: crate::expr::AggregateKind,
{
    type Marker = SelectCols<(Self,)>;
}

/// Tuples of select targets → `SelectCols<(A, B, ...)>`.
macro_rules! impl_into_select_target_tuple {
    ($($T:ident),+; $($idx:tt),+) => {
        impl<$($T),+> IntoSelectTarget for ($($T,)+) {
            type Marker = SelectCols<($($T,)+)>;
        }
    };
}

with_col_sizes_8!(impl_into_select_target_tuple);

#[cfg(any(
    feature = "col16",
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_16!(impl_into_select_target_tuple);

#[cfg(any(
    feature = "col32",
    feature = "col64",
    feature = "col128",
    feature = "col200"
))]
with_col_sizes_32!(impl_into_select_target_tuple);

#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
with_col_sizes_64!(impl_into_select_target_tuple);

#[cfg(any(feature = "col128", feature = "col200"))]
with_col_sizes_128!(impl_into_select_target_tuple);

#[cfg(feature = "col200")]
with_col_sizes_200!(impl_into_select_target_tuple);
