//! Projection-level configuration for `qroissant-arrow`.
//!
//! This module is intentionally free of PyO3 so the arrow crate can remain
//! Python-agnostic.  The Python crate converts `DecodeOptions` into a
//! `ProjectionOptions` at decode time and stores it alongside the value.

/// How to project q symbol (byte-string) values into Arrow.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum SymbolProjection {
    /// Arrow `Utf8` / `StringArray`.  Lossily converts non-UTF-8 bytes.
    #[default]
    Utf8,
    /// Arrow `LargeUtf8` / `LargeStringArray`.
    LargeUtf8,
    /// Arrow `Utf8View` / `StringViewArray`.
    Utf8View,
    /// Arrow `Dictionary<Int32, Utf8>`.
    Dictionary,
    /// Arrow `Binary` / `BinaryArray` — raw bytes, no UTF-8 coercion.
    RawBytes,
}

/// How to project q char-vector (byte string) values into Arrow.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum StringProjection {
    /// Arrow `Utf8` / `StringArray` (best-effort UTF-8).
    #[default]
    Utf8,
    /// Arrow `Binary` / `BinaryArray` — raw bytes.
    Binary,
}

/// Wrapper Arrow type used for homogeneous q list projection.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ListProjection {
    /// Arrow `List<i32>` / `ListArray`.
    List,
    /// Arrow `LargeList<i64>` / `LargeListArray`.
    #[default]
    LargeList,
    /// Arrow `ListView<i32>` — not yet supported; falls back to `List`.
    ListView,
}

/// Union encoding for heterogeneous q list projection.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UnionMode {
    /// Arrow dense union (compact offsets).
    #[default]
    Dense,
    /// Arrow sparse union (one slot per item per type).
    Sparse,
}

/// How heterogeneous q general lists are projected when child Arrow types differ.
///
/// KDB returns mixed-typed columns (most commonly: temporal nulls of varying
/// precision interleaved in a single column) as q general lists. The
/// faithful representation is an Arrow Union, but consumers such as Polars
/// reject Union types outright.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum HeterogeneousListMode {
    /// Faithful: emit Arrow Union (Dense or Sparse per `union_mode`).
    /// Lossless. Default.
    #[default]
    Union,
    /// Lossy: when every arm of the union is a temporal type (Timestamp,
    /// Date32, Date64, Time32, Time64, Duration), promote all values to
    /// `Timestamp(Nanosecond, None)` and emit a flat array. Falls back to
    /// Union if any arm is non-temporal or the cast fails.
    ///
    /// Use this when the downstream consumer cannot handle Arrow Union
    /// (Polars) and the column is known to be temporal-only.
    CoalesceTemporals,
}

/// Combined projection options threaded through `project()` / `project_table()`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProjectionOptions {
    pub symbol: SymbolProjection,
    pub string: StringProjection,
    pub list: ListProjection,
    pub union_mode: UnionMode,
    /// How heterogeneous general lists are projected when child Arrow types
    /// differ. Default: `Union` (faithful, lossless, may be rejected by Polars).
    pub heterogeneous_list_mode: HeterogeneousListMode,
    /// When `true`, q infinity sentinels (e.g. `0Wi`, `0Wj`, `0w`) are mapped
    /// to Arrow nulls alongside the standard null sentinels. Default: `false`.
    pub treat_infinity_as_null: bool,
    /// When `true` and the table has at least 4 columns, column projection
    /// is performed in parallel using rayon. Default: `true`.
    pub parallel: bool,
    /// When `true`, symbol bytes are assumed to be valid UTF-8 and are
    /// reinterpreted without validation or allocation. Default: `true`.
    pub assume_symbol_utf8: bool,
}

impl Default for ProjectionOptions {
    fn default() -> Self {
        Self {
            symbol: SymbolProjection::default(),
            string: StringProjection::default(),
            list: ListProjection::default(),
            union_mode: UnionMode::default(),
            heterogeneous_list_mode: HeterogeneousListMode::default(),
            treat_infinity_as_null: false,
            parallel: true,
            assume_symbol_utf8: true,
        }
    }
}
