//! SQL table definition and storage.

use super::row::{Row, RowId};
use super::types::{SqlError, SqlType, SqlValue};
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap, HashSet};

/// Column definition.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ColumnDef {
    /// Column name.
    pub name: String,
    /// Data type.
    pub data_type: SqlType,
    /// Whether null values are allowed.
    pub nullable: bool,
    /// Default value.
    pub default: Option<SqlValue>,
    /// Primary key flag.
    pub primary_key: bool,
    /// Unique constraint.
    pub unique: bool,
}

impl ColumnDef {
    /// Creates a new column definition.
    pub fn new(name: impl Into<String>, data_type: SqlType) -> Self {
        Self {
            name: name.into(),
            data_type,
            nullable: true,
            default: None,
            primary_key: false,
            unique: false,
        }
    }

    /// Sets as not null.
    pub fn not_null(mut self) -> Self {
        self.nullable = false;
        self
    }

    /// Sets default value.
    pub fn default(mut self, value: SqlValue) -> Self {
        self.default = Some(value);
        self
    }

    /// Sets as primary key.
    pub fn primary_key(mut self) -> Self {
        self.primary_key = true;
        self.nullable = false;
        self.unique = true;
        self
    }

    /// Sets as unique.
    pub fn unique(mut self) -> Self {
        self.unique = true;
        self
    }

    /// Validates a value against this column definition.
    pub fn validate(&self, value: &SqlValue) -> Result<(), SqlError> {
        // Check null constraint
        if value.is_null() && !self.nullable {
            return Err(SqlError::NotNullViolation(self.name.clone()));
        }

        // Skip type check for null values
        if value.is_null() {
            return Ok(());
        }

        // Check type compatibility
        let compatible = match (&self.data_type, value) {
            (SqlType::Integer, SqlValue::Integer(_)) => true,
            (SqlType::Real, SqlValue::Real(_)) => true,
            (SqlType::Real, SqlValue::Integer(_)) => true, // Allow int to real
            (SqlType::Text, SqlValue::Text(_)) => true,
            (SqlType::Blob, SqlValue::Blob(_)) => true,
            (SqlType::Boolean, SqlValue::Boolean(_)) => true,
            (SqlType::Timestamp, SqlValue::Timestamp(_)) => true,
            (SqlType::Timestamp, SqlValue::Integer(_)) => true, // Allow int to timestamp
            _ => false,
        };

        if !compatible {
            return Err(SqlError::TypeMismatch {
                expected: format!("{:?}", self.data_type),
                got: format!("{:?}", value.sql_type()),
            });
        }

        Ok(())
    }
}

/// Table definition.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TableDef {
    /// Table name.
    pub name: String,
    /// Column definitions.
    pub columns: Vec<ColumnDef>,
    /// Primary key column name (if any).
    pub primary_key: Option<String>,
}

impl TableDef {
    /// Creates a new table definition.
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            columns: Vec::new(),
            primary_key: None,
        }
    }

    /// Adds a column.
    pub fn column(mut self, col: ColumnDef) -> Self {
        if col.primary_key {
            self.primary_key = Some(col.name.clone());
        }
        self.columns.push(col);
        self
    }

    /// Gets column definition by name.
    pub fn get_column(&self, name: &str) -> Option<&ColumnDef> {
        self.columns.iter().find(|c| c.name == name)
    }

    /// Returns column names.
    pub fn column_names(&self) -> Vec<String> {
        self.columns.iter().map(|c| c.name.clone()).collect()
    }

    /// Validates a row against this table definition.
    pub fn validate_row(&self, row: &Row) -> Result<(), SqlError> {
        for col in &self.columns {
            let value = row.get_or_null(&col.name);
            col.validate(&value)?;
        }
        Ok(())
    }
}

/// Index on a table column.
#[derive(Debug)]
pub struct TableIndex {
    /// Index name.
    pub name: String,
    /// Column being indexed.
    pub column: String,
    /// Whether index enforces uniqueness.
    pub unique: bool,
    /// B-tree index data: value -> row IDs.
    data: BTreeMap<SqlValue, HashSet<RowId>>,
}

impl TableIndex {
    /// Creates a new index.
    pub fn new(name: impl Into<String>, column: impl Into<String>, unique: bool) -> Self {
        Self {
            name: name.into(),
            column: column.into(),
            unique,
            data: BTreeMap::new(),
        }
    }

    /// Inserts a value-rowid mapping.
    pub fn insert(&mut self, value: SqlValue, row_id: RowId) -> Result<(), SqlError> {
        if self.unique {
            if let Some(existing) = self.data.get(&value) {
                if !existing.is_empty() {
                    return Err(SqlError::ConstraintViolation(format!(
                        "Unique constraint violation on index '{}'",
                        self.name
                    )));
                }
            }
        }
        self.data.entry(value).or_default().insert(row_id);
        Ok(())
    }

    /// Removes a value-rowid mapping.
    pub fn remove(&mut self, value: &SqlValue, row_id: &RowId) {
        if let Some(ids) = self.data.get_mut(value) {
            ids.remove(row_id);
            if ids.is_empty() {
                self.data.remove(value);
            }
        }
    }

    /// Looks up rows by exact value.
    pub fn lookup(&self, value: &SqlValue) -> Vec<RowId> {
        self.data
            .get(value)
            .map(|ids| ids.iter().cloned().collect())
            .unwrap_or_default()
    }

    /// Range query.
    pub fn range(&self, start: Option<&SqlValue>, end: Option<&SqlValue>) -> Vec<RowId> {
        let mut result = Vec::new();
        for (key, ids) in &self.data {
            let in_range = match (start, end) {
                (Some(s), Some(e)) => key >= s && key <= e,
                (Some(s), None) => key >= s,
                (None, Some(e)) => key <= e,
                (None, None) => true,
            };
            if in_range {
                result.extend(ids.iter().cloned());
            }
        }
        result
    }
}

/// A SQL table with data.
pub struct Table {
    /// Table definition.
    pub def: TableDef,
    /// Row storage: row ID -> row data.
    rows: RwLock<HashMap<RowId, Row>>,
    /// Next row ID.
    next_id: RwLock<u64>,
    /// Indexes.
    indexes: RwLock<HashMap<String, TableIndex>>,
    /// Primary key index (if any).
    pk_index: RwLock<Option<String>>,
}

impl Table {
    /// Creates a new table.
    pub fn new(def: TableDef) -> Self {
        let pk_col = def.primary_key.clone();
        let unique_cols: Vec<String> = def
            .columns
            .iter()
            .filter(|c| c.unique && !c.primary_key)
            .map(|c| c.name.clone())
            .collect();

        let table = Self {
            def,
            rows: RwLock::new(HashMap::new()),
            next_id: RwLock::new(1),
            indexes: RwLock::new(HashMap::new()),
            pk_index: RwLock::new(None),
        };

        {
            let mut indexes = table.indexes.write();

            // Create primary key index if defined
            if let Some(pk) = pk_col {
                let idx_name = format!("pk_{}", pk);
                indexes.insert(idx_name.clone(), TableIndex::new(&idx_name, &pk, true));
                *table.pk_index.write() = Some(idx_name);
            }

            // Create indexes for unique columns
            for col in unique_cols {
                let idx_name = format!("unique_{}", col);
                indexes.insert(idx_name.clone(), TableIndex::new(&idx_name, &col, true));
            }
        }

        table
    }

    /// Returns table name.
    pub fn name(&self) -> &str {
        &self.def.name
    }

    /// Returns row count.
    pub fn count(&self) -> usize {
        self.rows.read().len()
    }

    /// Creates an index on a column.
    pub fn create_index(
        &self,
        name: impl Into<String>,
        column: impl Into<String>,
        unique: bool,
    ) -> Result<(), SqlError> {
        let name = name.into();
        let column = column.into();

        let mut indexes = self.indexes.write();
        if indexes.contains_key(&name) {
            return Err(SqlError::InvalidOperation(format!("Index '{}' already exists", name)));
        }

        let mut index = TableIndex::new(&name, &column, unique);

        // Index existing rows
        let rows = self.rows.read();
        for (row_id, row) in rows.iter() {
            let value = row.get_or_null(&column);
            index.insert(value, *row_id)?;
        }

        indexes.insert(name, index);
        Ok(())
    }

    /// Drops an index.
    pub fn drop_index(&self, name: &str) -> Result<(), SqlError> {
        let mut indexes = self.indexes.write();
        if indexes.remove(name).is_none() {
            return Err(SqlError::InvalidOperation(format!("Index '{}' not found", name)));
        }
        Ok(())
    }

    /// Inserts a row.
    pub fn insert(&self, values: HashMap<String, SqlValue>) -> Result<RowId, SqlError> {
        let mut next_id = self.next_id.write();
        let row_id = RowId(*next_id);
        *next_id += 1;

        let row = Row::from_map(row_id, values);

        // Validate row
        self.def.validate_row(&row)?;

        // Check uniqueness constraints via indexes
        {
            let mut indexes = self.indexes.write();
            for (_, index) in indexes.iter_mut() {
                if index.unique {
                    let value = row.get_or_null(&index.column);
                    if !value.is_null() && !index.lookup(&value).is_empty() {
                        return Err(SqlError::ConstraintViolation(format!(
                            "Unique constraint violation on column '{}'",
                            index.column
                        )));
                    }
                }
            }

            // Update indexes
            for (_, index) in indexes.iter_mut() {
                let value = row.get_or_null(&index.column);
                index.insert(value, row_id)?;
            }
        }

        // Insert row
        self.rows.write().insert(row_id, row);

        Ok(row_id)
    }

    /// Gets a row by ID.
    pub fn get(&self, id: RowId) -> Option<Row> {
        self.rows.read().get(&id).cloned()
    }

    /// Updates a row.
    pub fn update(&self, id: RowId, updates: HashMap<String, SqlValue>) -> Result<(), SqlError> {
        let mut rows = self.rows.write();
        let row = rows.get_mut(&id).ok_or_else(|| {
            SqlError::InvalidOperation(format!("Row {} not found", id))
        })?;

        let old_values: HashMap<String, SqlValue> = updates
            .keys()
            .map(|k| (k.clone(), row.get_or_null(k)))
            .collect();

        // Validate updates
        for (col, value) in &updates {
            if let Some(col_def) = self.def.get_column(col) {
                col_def.validate(value)?;
            }
        }

        // Update indexes
        {
            let mut indexes = self.indexes.write();
            for (_, index) in indexes.iter_mut() {
                if let Some(new_value) = updates.get(&index.column) {
                    let old_value = old_values.get(&index.column).cloned().unwrap_or(SqlValue::Null);
                    index.remove(&old_value, &id);
                    index.insert(new_value.clone(), id)?;
                }
            }
        }

        // Apply updates
        for (col, value) in updates {
            row.set(&col, value);
        }

        Ok(())
    }

    /// Deletes a row.
    pub fn delete(&self, id: RowId) -> Result<bool, SqlError> {
        let mut rows = self.rows.write();
        if let Some(row) = rows.remove(&id) {
            // Update indexes
            let mut indexes = self.indexes.write();
            for (_, index) in indexes.iter_mut() {
                let value = row.get_or_null(&index.column);
                index.remove(&value, &id);
            }
            Ok(true)
        } else {
            Ok(false)
        }
    }

    /// Scans all rows.
    pub fn scan(&self) -> Vec<Row> {
        self.rows.read().values().cloned().collect()
    }

    /// Scans with filter function.
    pub fn scan_filter<F>(&self, predicate: F) -> Vec<Row>
    where
        F: Fn(&Row) -> bool,
    {
        self.rows
            .read()
            .values()
            .filter(|row| predicate(row))
            .cloned()
            .collect()
    }

    /// Looks up rows by index.
    pub fn lookup_index(&self, index_name: &str, value: &SqlValue) -> Vec<Row> {
        let indexes = self.indexes.read();
        let rows = self.rows.read();

        if let Some(index) = indexes.get(index_name) {
            index
                .lookup(value)
                .into_iter()
                .filter_map(|id| rows.get(&id).cloned())
                .collect()
        } else {
            Vec::new()
        }
    }
}

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

    fn create_test_table() -> Table {
        let def = TableDef::new("users")
            .column(ColumnDef::new("id", SqlType::Integer).primary_key())
            .column(ColumnDef::new("name", SqlType::Text).not_null())
            .column(ColumnDef::new("age", SqlType::Integer))
            .column(ColumnDef::new("email", SqlType::Text).unique());

        Table::new(def)
    }

    #[test]
    fn test_table_insert() {
        let table = create_test_table();

        let mut values = HashMap::new();
        values.insert("id".to_string(), SqlValue::Integer(1));
        values.insert("name".to_string(), SqlValue::Text("Alice".to_string()));
        values.insert("age".to_string(), SqlValue::Integer(30));
        values.insert("email".to_string(), SqlValue::Text("alice@example.com".to_string()));

        let row_id = table.insert(values).unwrap();
        assert_eq!(table.count(), 1);

        let row = table.get(row_id).unwrap();
        assert_eq!(row.get("name"), Some(&SqlValue::Text("Alice".to_string())));
    }

    #[test]
    fn test_table_not_null() {
        let table = create_test_table();

        let mut values = HashMap::new();
        values.insert("id".to_string(), SqlValue::Integer(1));
        // Missing required "name" field

        let result = table.insert(values);
        assert!(result.is_err());
    }

    #[test]
    fn test_table_unique() {
        let table = create_test_table();

        let mut values1 = HashMap::new();
        values1.insert("id".to_string(), SqlValue::Integer(1));
        values1.insert("name".to_string(), SqlValue::Text("Alice".to_string()));
        values1.insert("email".to_string(), SqlValue::Text("test@example.com".to_string()));
        table.insert(values1).unwrap();

        let mut values2 = HashMap::new();
        values2.insert("id".to_string(), SqlValue::Integer(2));
        values2.insert("name".to_string(), SqlValue::Text("Bob".to_string()));
        values2.insert("email".to_string(), SqlValue::Text("test@example.com".to_string()));

        let result = table.insert(values2);
        assert!(result.is_err()); // Duplicate email
    }

    #[test]
    fn test_table_update() {
        let table = create_test_table();

        let mut values = HashMap::new();
        values.insert("id".to_string(), SqlValue::Integer(1));
        values.insert("name".to_string(), SqlValue::Text("Alice".to_string()));
        values.insert("age".to_string(), SqlValue::Integer(30));

        let row_id = table.insert(values).unwrap();

        let mut updates = HashMap::new();
        updates.insert("age".to_string(), SqlValue::Integer(31));
        table.update(row_id, updates).unwrap();

        let row = table.get(row_id).unwrap();
        assert_eq!(row.get("age"), Some(&SqlValue::Integer(31)));
    }

    #[test]
    fn test_table_delete() {
        let table = create_test_table();

        let mut values = HashMap::new();
        values.insert("id".to_string(), SqlValue::Integer(1));
        values.insert("name".to_string(), SqlValue::Text("Alice".to_string()));

        let row_id = table.insert(values).unwrap();
        assert_eq!(table.count(), 1);

        table.delete(row_id).unwrap();
        assert_eq!(table.count(), 0);
    }

    #[test]
    fn test_table_index() {
        let table = create_test_table();
        table.create_index("idx_name", "name", false).unwrap();

        let mut values = HashMap::new();
        values.insert("id".to_string(), SqlValue::Integer(1));
        values.insert("name".to_string(), SqlValue::Text("Alice".to_string()));
        table.insert(values).unwrap();

        let rows = table.lookup_index("idx_name", &SqlValue::Text("Alice".to_string()));
        assert_eq!(rows.len(), 1);
    }
}
