use crate::error::{Result, SqlError};
use crate::parser::{ComparisonOp, WhereClause};
use crate::types::{QueryResult, RowData, TableMetadata, Value};
use flaredb_client::RdbClient;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;

const DATA_PREFIX: &str = "__sql_data";

/// Row storage manager
pub struct StorageManager {
    client: Arc<Mutex<RdbClient>>,
}

struct ScannedRow {
    key: Vec<u8>,
    row: RowData,
    version: u64,
}

impl StorageManager {
    pub fn new(client: Arc<Mutex<RdbClient>>) -> Self {
        Self { client }
    }

    /// Insert a row into a table
    pub async fn insert_row(
        &self,
        table: &TableMetadata,
        columns: &[String],
        values: &[Value],
    ) -> Result<()> {
        if columns.len() != values.len() {
            return Err(SqlError::InvalidOperation(
                "Column count does not match value count".to_string(),
            ));
        }

        // Build row data
        let mut row_data = HashMap::new();
        for (col_name, value) in columns.iter().zip(values.iter()) {
            row_data.insert(col_name.clone(), value.clone());
        }

        // Validate all columns exist
        for col_name in columns {
            if table.get_column(col_name).is_none() {
                return Err(SqlError::ColumnNotFound(
                    col_name.clone(),
                    table.table_name.clone(),
                ));
            }
        }

        // Extract primary key values
        let pk_values: Result<Vec<&Value>> = table
            .primary_key
            .iter()
            .map(|pk_col| {
                row_data.get(pk_col).ok_or_else(|| {
                    SqlError::PrimaryKeyViolation(format!("Missing primary key column: {}", pk_col))
                })
            })
            .collect();
        let pk_values = pk_values?;

        // Encode row key
        let key = Self::encode_row_key(table.table_id, &pk_values)?;

        // Create RowData
        let row = RowData::new(row_data);

        // Serialize row
        let value =
            bincode::serialize(&row).map_err(|e| SqlError::SerializationError(e.to_string()))?;

        // Store in KVS using CAS (version 0 for new row)
        let mut client = self.client.lock().await;
        let (success, _current_version, _new_version) = client
            .cas(key, value, 0)
            .await
            .map_err(|e| SqlError::KvsError(e.to_string()))?;

        if !success {
            return Err(SqlError::PrimaryKeyViolation(
                "Row with this primary key already exists".to_string(),
            ));
        }

        Ok(())
    }

    /// Query rows from a table
    pub async fn query_rows(
        &self,
        table: &TableMetadata,
        columns: &[String],
        where_clause: Option<&WhereClause>,
    ) -> Result<QueryResult> {
        // Determine which columns to return
        let result_columns = if columns.is_empty() {
            // SELECT * - return all columns
            table.columns.iter().map(|c| c.name.clone()).collect()
        } else {
            // Validate requested columns exist
            for col_name in columns {
                if table.get_column(col_name).is_none() {
                    return Err(SqlError::ColumnNotFound(
                        col_name.clone(),
                        table.table_name.clone(),
                    ));
                }
            }
            columns.to_vec()
        };

        let mut result = QueryResult::new(result_columns.clone());

        // Process each row
        for scanned in self.scan_table_rows(table).await? {
            let row = scanned.row;
            // Apply WHERE filter
            if let Some(where_clause) = where_clause {
                if !Self::evaluate_where(&row, where_clause)? {
                    continue;
                }
            }

            // Extract requested columns
            let mut row_values = Vec::new();
            for col_name in &result_columns {
                let value = row.get(col_name).cloned().unwrap_or(Value::Null);
                row_values.push(value);
            }

            result.add_row(row_values);
        }

        Ok(result)
    }

    /// Update rows in a table
    pub async fn update_rows(
        &self,
        table: &TableMetadata,
        assignments: &[(String, Value)],
        where_clause: Option<&WhereClause>,
    ) -> Result<u64> {
        for (column, _) in assignments {
            if table.get_column(column).is_none() {
                return Err(SqlError::ColumnNotFound(
                    column.clone(),
                    table.table_name.clone(),
                ));
            }
        }

        let mut updated = 0u64;
        for scanned in self.scan_table_rows(table).await? {
            if let Some(where_clause) = where_clause {
                if !Self::evaluate_where(&scanned.row, where_clause)? {
                    continue;
                }
            }

            let mut row = scanned.row;
            for (column, value) in assignments {
                row.set(column.clone(), value.clone());
            }

            let pk_values = Self::primary_key_values(table, &row)?;
            let new_key = Self::encode_row_key(table.table_id, &pk_values)?;
            let value = Self::serialize_row(&row)?;

            if new_key == scanned.key {
                let mut client = self.client.lock().await;
                let (success, _current_version, _new_version) = client
                    .cas(scanned.key.clone(), value, scanned.version)
                    .await
                    .map_err(|e| SqlError::KvsError(e.to_string()))?;

                if !success {
                    return Err(SqlError::InvalidOperation(
                        "Concurrent row update detected".to_string(),
                    ));
                }
            } else {
                let mut client = self.client.lock().await;
                let (created, _current_version, new_version) = client
                    .cas(new_key.clone(), value, 0)
                    .await
                    .map_err(|e| SqlError::KvsError(e.to_string()))?;
                if !created {
                    return Err(SqlError::PrimaryKeyViolation(
                        "Row with this primary key already exists".to_string(),
                    ));
                }

                let (deleted, _current_version, _existed) = client
                    .cas_delete(scanned.key.clone(), scanned.version)
                    .await
                    .map_err(|e| SqlError::KvsError(e.to_string()))?;
                if !deleted {
                    let _ = client.cas_delete(new_key, new_version).await;
                    return Err(SqlError::InvalidOperation(
                        "Concurrent row update detected".to_string(),
                    ));
                }
            }

            updated += 1;
        }

        Ok(updated)
    }

    /// Delete rows from a table
    pub async fn delete_rows(
        &self,
        table: &TableMetadata,
        where_clause: Option<&WhereClause>,
    ) -> Result<u64> {
        let mut deleted = 0u64;
        for scanned in self.scan_table_rows(table).await? {
            if let Some(where_clause) = where_clause {
                if !Self::evaluate_where(&scanned.row, where_clause)? {
                    continue;
                }
            }

            let mut client = self.client.lock().await;
            let (success, _current_version, _existed) = client
                .cas_delete(scanned.key, scanned.version)
                .await
                .map_err(|e| SqlError::KvsError(e.to_string()))?;
            if !success {
                return Err(SqlError::InvalidOperation(
                    "Concurrent row delete detected".to_string(),
                ));
            }
            deleted += 1;
        }

        Ok(deleted)
    }

    async fn scan_table_rows(&self, table: &TableMetadata) -> Result<Vec<ScannedRow>> {
        let (mut next_key, end_key) = Self::encode_table_scan_range(table.table_id);
        let mut rows = Vec::new();
        let mut client = self.client.lock().await;

        loop {
            let (entries, continuation) = client
                .cas_scan(next_key.clone(), end_key.clone(), 1000)
                .await
                .map_err(|e| SqlError::KvsError(e.to_string()))?;

            for (key, value_bytes, version) in entries {
                let row: RowData = bincode::deserialize(&value_bytes)
                    .map_err(|e| SqlError::SerializationError(e.to_string()))?;
                rows.push(ScannedRow { key, row, version });
            }

            if let Some(continuation) = continuation {
                next_key = continuation;
            } else {
                break;
            }
        }

        Ok(rows)
    }

    /// Evaluate WHERE clause against a row
    fn evaluate_where(row: &RowData, where_clause: &WhereClause) -> Result<bool> {
        match where_clause {
            WhereClause::Comparison { column, op, value } => {
                let row_value = row.get(column).unwrap_or(&Value::Null);
                Ok(Self::compare_values(row_value, op, value))
            }
            WhereClause::And(left, right) => {
                let left_result = Self::evaluate_where(row, left)?;
                let right_result = Self::evaluate_where(row, right)?;
                Ok(left_result && right_result)
            }
            WhereClause::Or(left, right) => {
                let left_result = Self::evaluate_where(row, left)?;
                let right_result = Self::evaluate_where(row, right)?;
                Ok(left_result || right_result)
            }
        }
    }

    fn compare_values(left: &Value, op: &ComparisonOp, right: &Value) -> bool {
        match op {
            ComparisonOp::Eq => left == right,
            ComparisonOp::Ne => left != right,
            ComparisonOp::Lt => match (left, right) {
                (Value::Integer(a), Value::Integer(b)) => a < b,
                _ => false,
            },
            ComparisonOp::Le => match (left, right) {
                (Value::Integer(a), Value::Integer(b)) => a <= b,
                _ => false,
            },
            ComparisonOp::Gt => match (left, right) {
                (Value::Integer(a), Value::Integer(b)) => a > b,
                _ => false,
            },
            ComparisonOp::Ge => match (left, right) {
                (Value::Integer(a), Value::Integer(b)) => a >= b,
                _ => false,
            },
        }
    }

    /// Encode row key: __sql_data:{table_id}:{pk1}:{pk2}:...
    fn encode_row_key(table_id: u32, pk_values: &[&Value]) -> Result<Vec<u8>> {
        let mut key = format!("{}:{}", DATA_PREFIX, table_id);

        for value in pk_values {
            match value {
                Value::Integer(i) => key.push_str(&format!(":{}", i)),
                Value::Text(s) => key.push_str(&format!(":{}", s)),
                _ => {
                    return Err(SqlError::InvalidOperation(
                        "Primary key must be Integer or Text".to_string(),
                    ))
                }
            }
        }

        Ok(key.into_bytes())
    }

    /// Encode table prefix for scanning: __sql_data:{table_id}:
    fn encode_table_prefix(table_id: u32) -> Vec<u8> {
        format!("{}:{}:", DATA_PREFIX, table_id).into_bytes()
    }

    fn encode_table_scan_range(table_id: u32) -> (Vec<u8>, Vec<u8>) {
        let start = Self::encode_table_prefix(table_id);
        let end = Self::prefix_end(&start);
        (start, end)
    }

    fn prefix_end(prefix: &[u8]) -> Vec<u8> {
        let mut end_key = prefix.to_vec();
        if let Some(last) = end_key.last_mut() {
            if *last == 0xff {
                end_key.push(0x00);
            } else {
                *last += 1;
            }
        } else {
            end_key.push(0xff);
        }
        end_key
    }

    fn primary_key_values<'a>(
        table: &'a TableMetadata,
        row: &'a RowData,
    ) -> Result<Vec<&'a Value>> {
        table
            .primary_key
            .iter()
            .map(|pk_col| {
                row.get(pk_col).ok_or_else(|| {
                    SqlError::PrimaryKeyViolation(format!("Missing primary key column: {}", pk_col))
                })
            })
            .collect()
    }

    fn serialize_row(row: &RowData) -> Result<Vec<u8>> {
        bincode::serialize(row).map_err(|e| SqlError::SerializationError(e.to_string()))
    }
}

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

    #[test]
    fn table_scan_range_uses_prefix_successor() {
        let (start, end) = StorageManager::encode_table_scan_range(9);
        assert_eq!(start, b"__sql_data:9:".to_vec());
        assert_eq!(end, b"__sql_data:9;".to_vec());
        assert!(b"__sql_data:9:1".to_vec() < end);
        assert!(b"__sql_data:10:1".to_vec() < start);
    }
}
