//! Arrow field metadata helpers for preserving q type semantics.
//!
//! Every Arrow field produced by qroissant carries metadata that round-trips
//! the original q shape, primitive type, and attribute information so that
//! downstream consumers can reconstruct exact q semantics from an Arrow schema.

use std::collections::HashMap;
use std::sync::Arc;

use arrow_schema::Field;
use arrow_schema::FieldRef;
use qroissant_core::Attribute;
use qroissant_core::Primitive;

pub const SHAPE_KEY: &str = "qroissant.shape";
pub const PRIMITIVE_KEY: &str = "qroissant.primitive";
pub const ATTRIBUTE_KEY: &str = "qroissant.attribute";
pub const SORTED_KEY: &str = "qroissant.sorted";

fn shape_to_str(shape: &str) -> &str {
    match shape {
        "atom" => "atom",
        "vector" => "vector",
        "list" => "list",
        "dictionary" => "dictionary",
        "table" => "table",
        "unary_primitive" => "unary_primitive",
        other => other,
    }
}

fn primitive_str(p: Primitive) -> &'static str {
    match p {
        Primitive::Boolean => "boolean",
        Primitive::Guid => "guid",
        Primitive::Byte => "byte",
        Primitive::Short => "short",
        Primitive::Int => "int",
        Primitive::Long => "long",
        Primitive::Real => "real",
        Primitive::Float => "float",
        Primitive::Char => "char",
        Primitive::Symbol => "symbol",
        Primitive::Timestamp => "timestamp",
        Primitive::Month => "month",
        Primitive::Date => "date",
        Primitive::Datetime => "datetime",
        Primitive::Timespan => "timespan",
        Primitive::Minute => "minute",
        Primitive::Second => "second",
        Primitive::Time => "time",
        Primitive::Mixed => "mixed",
    }
}

fn attribute_str(a: Attribute) -> &'static str {
    match a {
        Attribute::None => "none",
        Attribute::Sorted => "sorted",
        Attribute::Unique => "unique",
        Attribute::Parted => "parted",
        Attribute::Grouped => "grouped",
    }
}

/// Build an Arrow field for a q atom or vector column.
///
/// The field name is left empty (`""`); callers that embed the field in a
/// schema or struct field should rename it via [`arrow_schema::Field::with_name`].
pub fn q_field(
    data_type: arrow_schema::DataType,
    nullable: bool,
    shape: &str,
    primitive: Option<Primitive>,
    attribute: Option<Attribute>,
    sorted: Option<bool>,
) -> FieldRef {
    let mut meta = HashMap::new();
    meta.insert(SHAPE_KEY.to_string(), shape_to_str(shape).to_string());
    if let Some(p) = primitive {
        meta.insert(PRIMITIVE_KEY.to_string(), primitive_str(p).to_string());
    }
    if let Some(a) = attribute {
        meta.insert(ATTRIBUTE_KEY.to_string(), attribute_str(a).to_string());
    }
    if let Some(s) = sorted {
        meta.insert(SORTED_KEY.to_string(), s.to_string());
    }
    Arc::new(Field::new("", data_type, nullable).with_metadata(meta))
}
