//! Abstract Syntax Tree definitions for CQL statements
//!
//! This module defines the AST node types that represent parsed CQL statements.
//! The AST is designed to be parser-agnostic and provides a unified representation
//! that can be generated by different parser backends (nom, ANTLR, etc.).

use crate::schema::CqlType;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Top-level CQL statement
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlStatement {
    /// SELECT statement
    Select(CqlSelect),
    /// INSERT statement
    Insert(CqlInsert),
    /// UPDATE statement
    Update(CqlUpdate),
    /// DELETE statement
    Delete(CqlDelete),
    /// CREATE TABLE statement
    CreateTable(CqlCreateTable),
    /// DROP TABLE statement
    DropTable(CqlDropTable),
    /// CREATE INDEX statement
    CreateIndex(CqlCreateIndex),
    /// ALTER TABLE statement
    AlterTable(CqlAlterTable),
    /// CREATE TYPE (UDT) statement
    CreateType(CqlCreateType),
    /// DROP TYPE statement
    DropType(CqlDropType),
    /// USE statement (keyspace selection)
    Use(CqlUse),
    /// TRUNCATE statement
    Truncate(CqlTruncate),
    /// BATCH statement
    Batch(CqlBatch),
}

/// SELECT statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlSelect {
    /// DISTINCT modifier
    pub distinct: bool,
    /// Selected columns/expressions
    pub select_list: Vec<CqlSelectItem>,
    /// FROM clause
    pub from: CqlTable,
    /// WHERE clause (optional)
    pub where_clause: Option<CqlExpression>,
    /// ORDER BY clause (optional)
    pub order_by: Option<Vec<CqlOrderBy>>,
    /// LIMIT clause (optional)
    pub limit: Option<u64>,
    /// ALLOW FILTERING modifier
    pub allow_filtering: bool,
}

/// Item in the SELECT list
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlSelectItem {
    /// Wildcard (*)
    Wildcard,
    /// Expression with optional alias
    Expression {
        expression: CqlExpression,
        alias: Option<CqlIdentifier>,
    },
    /// Function call
    Function {
        name: CqlIdentifier,
        args: Vec<CqlExpression>,
        alias: Option<CqlIdentifier>,
    },
}

/// INSERT statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlInsert {
    /// Target table
    pub table: CqlTable,
    /// Column names
    pub columns: Vec<CqlIdentifier>,
    /// Values to insert
    pub values: CqlInsertValues,
    /// IF NOT EXISTS modifier
    pub if_not_exists: bool,
    /// USING clause (TTL, TIMESTAMP)
    pub using: Option<CqlUsing>,
}

/// INSERT values
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlInsertValues {
    /// VALUES clause with literal values
    Values(Vec<CqlExpression>),
    /// JSON values
    Json(String),
}

/// UPDATE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlUpdate {
    /// Target table
    pub table: CqlTable,
    /// USING clause (TTL, TIMESTAMP)
    pub using: Option<CqlUsing>,
    /// SET assignments
    pub assignments: Vec<CqlAssignment>,
    /// WHERE clause
    pub where_clause: CqlExpression,
    /// IF condition (optional)
    pub if_condition: Option<CqlExpression>,
}

/// Assignment in UPDATE statement
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlAssignment {
    /// Target column
    pub column: CqlIdentifier,
    /// Assignment operator
    pub operator: CqlAssignmentOperator,
    /// Value expression
    pub value: CqlExpression,
}

/// Assignment operators
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlAssignmentOperator {
    /// Simple assignment (=)
    Assign,
    /// Addition assignment (+=)
    AddAssign,
    /// Subtraction assignment (-=)
    SubAssign,
    /// List append (+=)
    ListAppend,
    /// List prepend (= value +)
    ListPrepend,
    /// Set add (+=)
    SetAdd,
    /// Set remove (-=)
    SetRemove,
    /// Map update ([key] = value)
    MapUpdate(CqlExpression),
}

/// DELETE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlDelete {
    /// Columns to delete (optional - if empty, delete entire row)
    pub columns: Vec<CqlIdentifier>,
    /// Target table
    pub table: CqlTable,
    /// USING clause (TIMESTAMP)
    pub using: Option<CqlUsing>,
    /// WHERE clause
    pub where_clause: CqlExpression,
    /// IF condition (optional)
    pub if_condition: Option<CqlExpression>,
}

/// CREATE TABLE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlCreateTable {
    /// IF NOT EXISTS modifier
    pub if_not_exists: bool,
    /// Table name
    pub table: CqlTable,
    /// Column definitions
    pub columns: Vec<CqlColumnDef>,
    /// Primary key definition
    pub primary_key: CqlPrimaryKey,
    /// Table options (WITH clause)
    pub options: CqlTableOptions,
}

/// Column definition
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlColumnDef {
    /// Column name
    pub name: CqlIdentifier,
    /// Data type
    pub data_type: CqlDataType,
    /// STATIC modifier
    pub is_static: bool,
}

/// Primary key definition
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlPrimaryKey {
    /// Partition key columns
    pub partition_key: Vec<CqlIdentifier>,
    /// Clustering key columns (optional)
    pub clustering_key: Vec<CqlIdentifier>,
}

/// DROP TABLE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlDropTable {
    /// IF EXISTS modifier
    pub if_exists: bool,
    /// Table name
    pub table: CqlTable,
}

/// CREATE INDEX statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlCreateIndex {
    /// IF NOT EXISTS modifier
    pub if_not_exists: bool,
    /// Index name (optional)
    pub name: Option<CqlIdentifier>,
    /// Target table
    pub table: CqlTable,
    /// Indexed columns/expressions
    pub columns: Vec<CqlIndexColumn>,
    /// USING clause (index type)
    pub using: Option<String>,
    /// Index options
    pub options: HashMap<String, String>,
}

/// Index column specification
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlIndexColumn {
    /// Simple column reference
    Column(CqlIdentifier),
    /// KEYS() function for map columns
    Keys(CqlIdentifier),
    /// VALUES() function for map columns
    Values(CqlIdentifier),
    /// ENTRIES() function for map columns
    Entries(CqlIdentifier),
    /// FULL() function for collection columns
    Full(CqlIdentifier),
}

/// ALTER TABLE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlAlterTable {
    /// Target table
    pub table: CqlTable,
    /// Alteration operation
    pub operation: CqlAlterTableOp,
}

/// ALTER TABLE operations
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlAlterTableOp {
    /// ADD column
    AddColumn(CqlColumnDef),
    /// DROP column
    DropColumn(CqlIdentifier),
    /// ALTER column type
    AlterColumn {
        column: CqlIdentifier,
        new_type: CqlDataType,
    },
    /// RENAME column
    RenameColumn {
        old_name: CqlIdentifier,
        new_name: CqlIdentifier,
    },
    /// WITH options
    WithOptions(CqlTableOptions),
}

/// CREATE TYPE (UDT) statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlCreateType {
    /// IF NOT EXISTS modifier
    pub if_not_exists: bool,
    /// Type name
    pub name: CqlIdentifier,
    /// Field definitions
    pub fields: Vec<CqlUdtField>,
}

/// UDT field definition
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlUdtField {
    /// Field name
    pub name: CqlIdentifier,
    /// Field data type
    pub data_type: CqlDataType,
}

/// DROP TYPE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlDropType {
    /// IF EXISTS modifier
    pub if_exists: bool,
    /// Type name
    pub name: CqlIdentifier,
}

/// USE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlUse {
    /// Keyspace name
    pub keyspace: CqlIdentifier,
}

/// TRUNCATE statement AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlTruncate {
    /// Target table
    pub table: CqlTable,
}

/// BATCH statement AST.
///
/// The parser accepts multi-table batches (each statement may reference a different table),
/// but the write engine processes each statement independently against the provided schema.
/// Cross-table atomicity is not guaranteed by the local write engine.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlBatch {
    /// Batch type
    pub batch_type: CqlBatchType,
    /// USING clause (TIMESTAMP)
    pub using: Option<CqlUsing>,
    /// Statements in the batch (max 65535 entries, enforced by parser)
    pub statements: Vec<CqlBatchStatement>,
}

/// Batch types
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlBatchType {
    /// LOGGED batch (default)
    Logged,
    /// UNLOGGED batch
    Unlogged,
    /// COUNTER batch
    Counter,
}

/// Statement allowed in a batch
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlBatchStatement {
    /// INSERT statement
    Insert(CqlInsert),
    /// UPDATE statement
    Update(CqlUpdate),
    /// DELETE statement
    Delete(CqlDelete),
}

/// CQL expression AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlExpression {
    /// Literal value
    Literal(CqlLiteral),
    /// Column reference
    Column(CqlIdentifier),
    /// Parameter placeholder (?)
    Parameter(u32),
    /// Named parameter (:name)
    NamedParameter(String),
    /// Binary operation (AND, OR, =, !=, <, >, etc.)
    Binary {
        left: Box<CqlExpression>,
        operator: CqlBinaryOperator,
        right: Box<CqlExpression>,
    },
    /// Unary operation (NOT, -)
    Unary {
        operator: CqlUnaryOperator,
        operand: Box<CqlExpression>,
    },
    /// Function call
    Function {
        name: CqlIdentifier,
        args: Vec<CqlExpression>,
    },
    /// IN clause
    In {
        expression: Box<CqlExpression>,
        values: Vec<CqlExpression>,
    },
    /// CONTAINS clause
    Contains {
        column: CqlIdentifier,
        value: Box<CqlExpression>,
    },
    /// CONTAINS KEY clause
    ContainsKey {
        column: CqlIdentifier,
        key: Box<CqlExpression>,
    },
    /// Collection access [index] or [key]
    CollectionAccess {
        collection: Box<CqlExpression>,
        index: Box<CqlExpression>,
    },
    /// UDT field access (udt.field)
    FieldAccess {
        object: Box<CqlExpression>,
        field: CqlIdentifier,
    },
    /// CASE expression
    Case {
        when_clauses: Vec<CqlWhenClause>,
        else_clause: Option<Box<CqlExpression>>,
    },
    /// Type cast (CAST)
    Cast {
        expression: Box<CqlExpression>,
        target_type: CqlDataType,
    },
}

/// WHEN clause in CASE expression
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlWhenClause {
    /// Condition
    pub condition: CqlExpression,
    /// Result if condition is true
    pub result: CqlExpression,
}

/// Binary operators
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlBinaryOperator {
    // Logical
    And,
    Or,

    // Comparison
    Eq,
    Ne,
    Lt,
    Le,
    Gt,
    Ge,

    // Arithmetic
    Add,
    Sub,
    Mul,
    Div,
    Mod,

    // String
    Like,
}

/// Unary operators
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlUnaryOperator {
    /// Logical NOT
    Not,
    /// Arithmetic negation
    Minus,
    /// Arithmetic positive (unary +)
    Plus,
}

/// CQL literal values
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlLiteral {
    /// NULL value
    Null,
    /// Boolean value
    Boolean(bool),
    /// Integer value
    Integer(i64),
    /// Float value
    Float(f64),
    /// String value
    String(String),
    /// UUID value
    Uuid(String),
    /// Blob value (hex string)
    Blob(String),
    /// Collection literal
    Collection(CqlCollectionLiteral),
    /// UDT literal
    Udt(CqlUdtLiteral),
    /// Tuple literal
    Tuple(Vec<CqlLiteral>),
}

/// Collection literal
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlCollectionLiteral {
    /// List literal [item1, item2, ...]
    List(Vec<CqlLiteral>),
    /// Set literal {item1, item2, ...}
    Set(Vec<CqlLiteral>),
    /// Map literal {key1: value1, key2: value2, ...}
    Map(Vec<(CqlLiteral, CqlLiteral)>),
}

/// UDT literal
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlUdtLiteral {
    /// UDT type name (optional)
    pub type_name: Option<CqlIdentifier>,
    /// Field values
    pub fields: Vec<(CqlIdentifier, CqlLiteral)>,
}

/// CQL data type AST
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlDataType {
    /// Primitive types
    Boolean,
    TinyInt,
    SmallInt,
    Int,
    BigInt,
    Varint,
    Decimal,
    Float,
    Double,
    Text,
    Ascii,
    Varchar,
    Blob,
    Timestamp,
    Date,
    Time,
    Uuid,
    TimeUuid,
    Inet,
    Duration,
    Counter,

    /// Collection types
    List(Box<CqlDataType>),
    Set(Box<CqlDataType>),
    Map(Box<CqlDataType>, Box<CqlDataType>),

    /// Complex types
    Tuple(Vec<CqlDataType>),
    Udt(CqlIdentifier),
    Frozen(Box<CqlDataType>),

    /// Custom type
    Custom(String),
}

/// CQL identifier
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CqlIdentifier {
    /// The identifier name
    pub name: String,
    /// Whether the identifier is quoted
    pub quoted: bool,
}

/// Table reference
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlTable {
    /// Keyspace name (optional)
    pub keyspace: Option<CqlIdentifier>,
    /// Table name
    pub name: CqlIdentifier,
}

/// ORDER BY clause
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlOrderBy {
    /// Column to order by
    pub column: CqlIdentifier,
    /// Sort direction
    pub direction: CqlSortDirection,
}

/// ORDER BY ordering (alias for CqlOrderBy for compatibility)
pub type CqlOrdering = CqlOrderBy;

/// LIMIT clause
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlLimit {
    /// Maximum number of rows to return
    pub count: u64,
}

/// TTL specification
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlTtl {
    /// TTL value in seconds
    pub seconds: Option<CqlExpression>,
}

/// TIMESTAMP specification
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlTimestamp {
    /// Timestamp value (microseconds since epoch)
    pub microseconds: Option<CqlExpression>,
}

/// Sort direction
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum CqlSortDirection {
    /// Ascending order
    Asc,
    /// Descending order
    Desc,
}

/// USING clause for timestamps and TTL
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CqlUsing {
    /// TTL specification
    pub ttl: Option<CqlExpression>,
    /// TIMESTAMP specification
    pub timestamp: Option<CqlExpression>,
}

/// Table options (WITH clause)
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct CqlTableOptions {
    /// Option values
    pub options: HashMap<String, CqlLiteral>,
}

impl CqlIdentifier {
    /// Create a new unquoted identifier
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            quoted: false,
        }
    }

    /// Create a new quoted identifier
    pub fn quoted(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            quoted: true,
        }
    }

    /// Get the identifier name as a string
    pub fn as_str(&self) -> &str {
        &self.name
    }

    /// Get the identifier name (alias for as_str)
    pub fn name(&self) -> &str {
        &self.name
    }

    /// Check if the identifier is quoted
    pub fn is_quoted(&self) -> bool {
        self.quoted
    }

    /// Check if this identifier needs quoting
    pub fn needs_quoting(&self) -> bool {
        self.quoted || !self.is_valid_unquoted()
    }

    /// Check if the name is valid as an unquoted identifier
    fn is_valid_unquoted(&self) -> bool {
        let mut chars = self.name.chars();
        let Some(first) = chars.next() else {
            return false;
        };
        (first.is_ascii_alphabetic() || first == '_')
            && chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
    }
}

impl CqlTable {
    /// Create a new table reference without keyspace
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            keyspace: None,
            name: CqlIdentifier::new(name),
        }
    }

    /// Create a new table reference with keyspace
    pub fn with_keyspace(keyspace: impl Into<String>, name: impl Into<String>) -> Self {
        Self {
            keyspace: Some(CqlIdentifier::new(keyspace)),
            name: CqlIdentifier::new(name),
        }
    }

    /// Get the full table name (keyspace.table or just table)
    pub fn full_name(&self) -> String {
        match &self.keyspace {
            Some(ks) => format!("{}.{}", ks.as_str(), self.name.as_str()),
            None => self.name.as_str().to_string(),
        }
    }

    /// Get the table name
    pub fn name(&self) -> &CqlIdentifier {
        &self.name
    }

    /// Get keyspace (returns Option<&CqlIdentifier>)
    pub fn keyspace(&self) -> Option<&CqlIdentifier> {
        self.keyspace.as_ref()
    }
}

impl From<CqlDataType> for CqlType {
    fn from(data_type: CqlDataType) -> Self {
        match data_type {
            CqlDataType::Boolean => CqlType::Boolean,
            CqlDataType::TinyInt => CqlType::TinyInt,
            CqlDataType::SmallInt => CqlType::SmallInt,
            CqlDataType::Int => CqlType::Int,
            CqlDataType::BigInt => CqlType::BigInt,
            CqlDataType::Float => CqlType::Float,
            CqlDataType::Double => CqlType::Double,
            CqlDataType::Decimal => CqlType::Decimal,
            CqlDataType::Text | CqlDataType::Varchar => CqlType::Text,
            CqlDataType::Ascii => CqlType::Ascii,
            CqlDataType::Blob => CqlType::Blob,
            CqlDataType::Timestamp => CqlType::Timestamp,
            CqlDataType::Date => CqlType::Date,
            CqlDataType::Time => CqlType::Time,
            CqlDataType::Uuid => CqlType::Uuid,
            CqlDataType::TimeUuid => CqlType::TimeUuid,
            CqlDataType::Inet => CqlType::Inet,
            CqlDataType::Duration => CqlType::Duration,
            CqlDataType::List(inner) => CqlType::List(Box::new((*inner).into())),
            CqlDataType::Set(inner) => CqlType::Set(Box::new((*inner).into())),
            CqlDataType::Map(key, value) => {
                CqlType::Map(Box::new((*key).into()), Box::new((*value).into()))
            }
            CqlDataType::Tuple(types) => {
                CqlType::Tuple(types.into_iter().map(|t| t.into()).collect())
            }
            CqlDataType::Udt(name) => CqlType::Udt(name.as_str().to_string(), vec![]),
            CqlDataType::Frozen(inner) => CqlType::Frozen(Box::new((*inner).into())),
            CqlDataType::Custom(name) => CqlType::Custom(name),
            CqlDataType::Varint => CqlType::BigInt, // Map varint to bigint
            CqlDataType::Counter => CqlType::Counter,
        }
    }
}

impl From<CqlType> for CqlDataType {
    fn from(cql_type: CqlType) -> Self {
        match cql_type {
            CqlType::Boolean => CqlDataType::Boolean,
            CqlType::TinyInt => CqlDataType::TinyInt,
            CqlType::SmallInt => CqlDataType::SmallInt,
            CqlType::Int => CqlDataType::Int,
            CqlType::BigInt => CqlDataType::BigInt,
            CqlType::Counter => CqlDataType::Counter,
            CqlType::Float => CqlDataType::Float,
            CqlType::Double => CqlDataType::Double,
            CqlType::Decimal => CqlDataType::Decimal,
            CqlType::Text | CqlType::Varchar => CqlDataType::Text,
            CqlType::Ascii => CqlDataType::Ascii,
            CqlType::Blob => CqlDataType::Blob,
            CqlType::Timestamp => CqlDataType::Timestamp,
            CqlType::Date => CqlDataType::Date,
            CqlType::Time => CqlDataType::Time,
            CqlType::Uuid => CqlDataType::Uuid,
            CqlType::TimeUuid => CqlDataType::TimeUuid,
            CqlType::Inet => CqlDataType::Inet,
            CqlType::Duration => CqlDataType::Duration,
            CqlType::Varint => CqlDataType::Custom("varint".to_string()),
            CqlType::List(inner) => CqlDataType::List(Box::new((*inner).into())),
            CqlType::Set(inner) => CqlDataType::Set(Box::new((*inner).into())),
            CqlType::Map(key, value) => {
                CqlDataType::Map(Box::new((*key).into()), Box::new((*value).into()))
            }
            CqlType::Tuple(types) => {
                CqlDataType::Tuple(types.into_iter().map(|t| t.into()).collect())
            }
            CqlType::Udt(name, _) => CqlDataType::Udt(CqlIdentifier::new(name)),
            CqlType::Frozen(inner) => CqlDataType::Frozen(Box::new((*inner).into())),
            CqlType::Custom(name) => CqlDataType::Custom(name),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_identifier_creation() {
        let id1 = CqlIdentifier::new("test");
        assert_eq!(id1.name, "test");
        assert!(!id1.quoted);
        assert!(!id1.needs_quoting());

        let id2 = CqlIdentifier::quoted("test");
        assert_eq!(id2.name, "test");
        assert!(id2.quoted);
        assert!(id2.needs_quoting());
    }

    #[test]
    fn test_identifier_validation() {
        assert!(CqlIdentifier::new("valid_name").is_valid_unquoted());
        assert!(CqlIdentifier::new("_valid").is_valid_unquoted());
        assert!(CqlIdentifier::new("valid123").is_valid_unquoted());

        assert!(!CqlIdentifier::new("123invalid").is_valid_unquoted());
        assert!(!CqlIdentifier::new("invalid-name").is_valid_unquoted());
        assert!(!CqlIdentifier::new("").is_valid_unquoted());
    }

    #[test]
    fn test_table_creation() {
        let table1 = CqlTable::new("users");
        assert_eq!(table1.name.as_str(), "users");
        assert!(table1.keyspace.is_none());
        assert_eq!(table1.full_name(), "users");

        let table2 = CqlTable::with_keyspace("test", "users");
        assert_eq!(table2.keyspace.as_ref().unwrap().as_str(), "test");
        assert_eq!(table2.name.as_str(), "users");
        assert_eq!(table2.full_name(), "test.users");
    }

    #[test]
    fn test_data_type_conversion() {
        let cql_type = CqlType::List(Box::new(CqlType::Text));
        let data_type: CqlDataType = cql_type.clone().into();
        let back_to_cql: CqlType = data_type.into();
        assert_eq!(cql_type, back_to_cql);
    }

    #[test]
    fn test_identifier_needs_quoting_rules() {
        let numeric_start = CqlIdentifier::new("123abc");
        assert!(numeric_start.needs_quoting());

        let mixed_case = CqlIdentifier::new("CamelCase");
        assert!(!mixed_case.needs_quoting());

        let quoted = CqlIdentifier::quoted("any value");
        assert!(quoted.needs_quoting());
    }

    #[test]
    fn test_table_options_default_is_empty() {
        let options = CqlTableOptions::default();
        assert!(options.options.is_empty());
    }

    #[test]
    fn test_varint_and_counter_mapping() {
        let big_int = CqlType::from(CqlDataType::Varint);
        assert_eq!(big_int, CqlType::BigInt);

        let counter_type = CqlType::from(CqlDataType::Counter);
        assert_eq!(counter_type, CqlType::Counter);
    }
}
