use crate::error::Result;
use crate::metadata::MetadataManager;
use crate::parser::{parse_sql, SqlStatement};
use crate::storage::StorageManager;
use crate::types::QueryResult;
use flaredb_client::RdbClient;
use std::sync::Arc;
use tokio::sync::Mutex;

/// SQL execution result
#[derive(Debug)]
pub enum ExecutionResult {
    DdlSuccess(String),
    DmlSuccess(u64),
    Query(QueryResult),
}

/// SQL executor
pub struct SqlExecutor {
    metadata_manager: Arc<MetadataManager>,
    storage_manager: Arc<StorageManager>,
}

impl SqlExecutor {
    pub fn new(client: Arc<Mutex<RdbClient>>) -> Self {
        let metadata_manager = Arc::new(MetadataManager::new(client.clone()));
        let storage_manager = Arc::new(StorageManager::new(client));

        Self {
            metadata_manager,
            storage_manager,
        }
    }

    /// Execute a SQL statement
    pub async fn execute(&self, sql: &str) -> Result<ExecutionResult> {
        let statement = parse_sql(sql)?;
        self.execute_statement(&statement).await
    }

    async fn execute_statement(&self, statement: &SqlStatement) -> Result<ExecutionResult> {
        match statement {
            SqlStatement::CreateTable {
                table_name,
                columns,
                primary_key,
            } => {
                self.metadata_manager
                    .create_table(table_name.clone(), columns.clone(), primary_key.clone())
                    .await?;

                Ok(ExecutionResult::DdlSuccess(format!(
                    "Table '{}' created",
                    table_name
                )))
            }

            SqlStatement::DropTable { table_name } => {
                self.metadata_manager.drop_table(table_name).await?;

                Ok(ExecutionResult::DdlSuccess(format!(
                    "Table '{}' dropped",
                    table_name
                )))
            }

            SqlStatement::Insert {
                table_name,
                columns,
                values,
            } => {
                let table = self.metadata_manager.get_table_metadata(table_name).await?;

                self.storage_manager
                    .insert_row(&table, columns, values)
                    .await?;

                Ok(ExecutionResult::DmlSuccess(1))
            }

            SqlStatement::Select {
                table_name,
                columns,
                where_clause,
            } => {
                let table = self.metadata_manager.get_table_metadata(table_name).await?;

                let result = self
                    .storage_manager
                    .query_rows(&table, columns, where_clause.as_ref())
                    .await?;

                Ok(ExecutionResult::Query(result))
            }

            SqlStatement::Update {
                table_name,
                assignments,
                where_clause,
            } => {
                let table = self.metadata_manager.get_table_metadata(table_name).await?;
                let updated = self
                    .storage_manager
                    .update_rows(&table, assignments, where_clause.as_ref())
                    .await?;

                Ok(ExecutionResult::DmlSuccess(updated))
            }

            SqlStatement::Delete {
                table_name,
                where_clause,
            } => {
                let table = self.metadata_manager.get_table_metadata(table_name).await?;
                let deleted = self
                    .storage_manager
                    .delete_rows(&table, where_clause.as_ref())
                    .await?;

                Ok(ExecutionResult::DmlSuccess(deleted))
            }
        }
    }
}

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

    #[tokio::test]
    #[ignore] // Requires FlareDB server
    async fn test_create_and_query_table() {
        let client = RdbClient::connect_direct("127.0.0.1:8001".to_string(), "sqltest".to_string())
            .await
            .unwrap();
        let executor = SqlExecutor::new(Arc::new(Mutex::new(client)));

        // Create table
        let result = executor
            .execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)")
            .await
            .unwrap();

        match result {
            ExecutionResult::DdlSuccess(msg) => {
                assert!(msg.contains("created"));
            }
            _ => panic!("Expected DdlSuccess"),
        }

        // Insert data
        let result = executor
            .execute("INSERT INTO users (id, name) VALUES (1, 'Alice')")
            .await
            .unwrap();

        match result {
            ExecutionResult::DmlSuccess(count) => {
                assert_eq!(count, 1);
            }
            _ => panic!("Expected DmlSuccess"),
        }

        // Query data
        let result = executor
            .execute("SELECT * FROM users WHERE id = 1")
            .await
            .unwrap();

        match result {
            ExecutionResult::Query(query_result) => {
                assert_eq!(query_result.row_count(), 1);
            }
            _ => panic!("Expected Query result"),
        }
    }
}
