use std::error::Error;
use std::io::ErrorKind;

use crate::lib::ast::ddl::AlterColumnAction;
use crate::lib::ast::predule::{AlterTableAction, AlterTableQuery, TableName};
use crate::lib::errors::predule::ExecuteError;
use crate::lib::executor::predule::{
    ExecuteColumn, ExecuteColumnType, ExecuteField, ExecuteResult, ExecuteRow, Executor,
    StorageEncoder, TableConfig,
};

impl Executor {
    pub async fn alter_table(
        &self,
        query: AlterTableQuery,
    ) -> Result<ExecuteResult, Box<dyn Error>> {
        let encoder = StorageEncoder::new();

        let base_path = self.get_base_path();

        let TableName {
            database_name,
            table_name,
        } = query.table.unwrap();

        let database_name = database_name.unwrap();

        let database_path = base_path.clone().join(&database_name);
        let table_path = database_path.clone().join(&table_name);

        match query.action {
            AlterTableAction::AlterTableRenameTo(action) => {
                let change_name = action.name;
                let change_path = database_path.clone().join(&change_name);

                // table 디렉터리명 변경
                if let Err(error) = tokio::fs::rename(&table_path, &change_path).await {
                    return Err(ExecuteError::boxed(format!(
                        "table rename failed: {}",
                        error.to_string()
                    )));
                }

                // config data 파일 내용 변경
                let config_path = change_path.clone().join("table.config");

                match tokio::fs::read(&config_path).await {
                    Ok(data) => {
                        let table_config: Option<TableConfig> = encoder.decode(data.as_slice());

                        match table_config {
                            Some(mut table_config) => {
                                table_config.table.table_name = change_name;
                                tokio::fs::write(config_path, encoder.encode(table_config)).await?;
                            }
                            None => {
                                return Err(ExecuteError::boxed("invalid config data"));
                            }
                        }
                    }
                    Err(error) => match error.kind() {
                        ErrorKind::NotFound => {
                            return Err(ExecuteError::boxed("table not found"));
                        }
                        _ => {
                            return Err(ExecuteError::boxed(format!("{:?}", error)));
                        }
                    },
                }
            }
            AlterTableAction::AddColumn(action) => {
                // TODO: 실 데이터 목록에도 반영하기

                // config data 파일 내용 변경
                let config_path = table_path.clone().join("table.config");

                let column_to_add = action.column;

                match tokio::fs::read(&config_path).await {
                    Ok(data) => {
                        let table_config: Option<TableConfig> = encoder.decode(data.as_slice());

                        match table_config {
                            Some(mut table_config) => {
                                if table_config.columns.contains(&column_to_add) {
                                    return Err(ExecuteError::boxed(format!(
                                        "column '{}' already exists ",
                                        column_to_add.name
                                    )));
                                }

                                table_config.columns.push(column_to_add);

                                tokio::fs::write(config_path, encoder.encode(table_config)).await?;
                            }
                            None => {
                                return Err(ExecuteError::boxed("invalid config data"));
                            }
                        }
                    }
                    Err(error) => match error.kind() {
                        ErrorKind::NotFound => {
                            return Err(ExecuteError::boxed("table not found"));
                        }
                        _ => {
                            return Err(ExecuteError::boxed(format!("{:?}", error)));
                        }
                    },
                }
            }
            AlterTableAction::AlterColumn(action) => {
                // TODO: 실 데이터 목록에도 반영하기

                let column_name = action.column_name;

                match action.action {
                    AlterColumnAction::AlterColumnSetDefault(action) => {
                        // config data 파일 내용 변경
                        let config_path = table_path.clone().join("table.config");

                        match tokio::fs::read(&config_path).await {
                            Ok(data) => {
                                let table_config: Option<TableConfig> =
                                    encoder.decode(data.as_slice());

                                match table_config {
                                    Some(mut table_config) => {
                                        let target = table_config
                                            .columns
                                            .iter_mut()
                                            .find(|e| &e.name == &column_name);

                                        match target {
                                            Some(target) => {
                                                target.default = Some(action.expression);
                                            }
                                            None => {
                                                return Err(ExecuteError::boxed(format!(
                                                    "column '{}' not exists ",
                                                    column_name
                                                )));
                                            }
                                        }

                                        tokio::fs::write(config_path, encoder.encode(table_config))
                                            .await?;
                                    }
                                    None => {
                                        return Err(ExecuteError::boxed("invalid config data"));
                                    }
                                }
                            }
                            Err(error) => match error.kind() {
                                ErrorKind::NotFound => {
                                    return Err(ExecuteError::boxed("table not found"));
                                }
                                _ => {
                                    return Err(ExecuteError::boxed(format!("{:?}", error)));
                                }
                            },
                        }
                    }
                    AlterColumnAction::AlterColumnDropDefault(_) => {
                        // config data 파일 내용 변경
                        let config_path = table_path.clone().join("table.config");

                        match tokio::fs::read(&config_path).await {
                            Ok(data) => {
                                let table_config: Option<TableConfig> =
                                    encoder.decode(data.as_slice());

                                match table_config {
                                    Some(mut table_config) => {
                                        let target = table_config
                                            .columns
                                            .iter_mut()
                                            .find(|e| &e.name == &column_name);

                                        match target {
                                            Some(target) => {
                                                target.default = None;
                                            }
                                            None => {
                                                return Err(ExecuteError::boxed(format!(
                                                    "column '{}' not exists ",
                                                    column_name
                                                )));
                                            }
                                        }

                                        tokio::fs::write(config_path, encoder.encode(table_config))
                                            .await?;
                                    }
                                    None => {
                                        return Err(ExecuteError::boxed("invalid config data"));
                                    }
                                }
                            }
                            Err(error) => match error.kind() {
                                ErrorKind::NotFound => {
                                    return Err(ExecuteError::boxed("table not found"));
                                }
                                _ => {
                                    return Err(ExecuteError::boxed(format!("{:?}", error)));
                                }
                            },
                        }
                    }
                    AlterColumnAction::AlterColumnSetNotNull => {
                        // config data 파일 내용 변경
                        let config_path = table_path.clone().join("table.config");

                        match tokio::fs::read(&config_path).await {
                            Ok(data) => {
                                let table_config: Option<TableConfig> =
                                    encoder.decode(data.as_slice());

                                match table_config {
                                    Some(mut table_config) => {
                                        let target = table_config
                                            .columns
                                            .iter_mut()
                                            .find(|e| &e.name == &column_name);

                                        match target {
                                            Some(target) => {
                                                target.not_null = true;
                                            }
                                            None => {
                                                return Err(ExecuteError::boxed(format!(
                                                    "column '{}' not exists ",
                                                    column_name
                                                )));
                                            }
                                        }

                                        tokio::fs::write(config_path, encoder.encode(table_config))
                                            .await?;
                                    }
                                    None => {
                                        return Err(ExecuteError::boxed("invalid config data"));
                                    }
                                }
                            }
                            Err(error) => match error.kind() {
                                ErrorKind::NotFound => {
                                    return Err(ExecuteError::boxed("table not found"));
                                }
                                _ => {
                                    return Err(ExecuteError::boxed(format!("{:?}", error)));
                                }
                            },
                        }
                    }
                    AlterColumnAction::AlterColumnDropNotNull => {
                        // config data 파일 내용 변경
                        let config_path = table_path.clone().join("table.config");

                        match tokio::fs::read(&config_path).await {
                            Ok(data) => {
                                let table_config: Option<TableConfig> =
                                    encoder.decode(data.as_slice());

                                match table_config {
                                    Some(mut table_config) => {
                                        let target = table_config
                                            .columns
                                            .iter_mut()
                                            .find(|e| &e.name == &column_name);

                                        match target {
                                            Some(target) => {
                                                target.not_null = false;
                                            }
                                            None => {
                                                return Err(ExecuteError::boxed(format!(
                                                    "column '{}' not exists ",
                                                    column_name
                                                )));
                                            }
                                        }

                                        tokio::fs::write(config_path, encoder.encode(table_config))
                                            .await?;
                                    }
                                    None => {
                                        return Err(ExecuteError::boxed("invalid config data"));
                                    }
                                }
                            }
                            Err(error) => match error.kind() {
                                ErrorKind::NotFound => {
                                    return Err(ExecuteError::boxed("table not found"));
                                }
                                _ => {
                                    return Err(ExecuteError::boxed(format!("{:?}", error)));
                                }
                            },
                        }
                    }
                    AlterColumnAction::AlterColumnSetType(action) => {
                        let config_path = table_path.clone().join("table.config");

                        match tokio::fs::read(&config_path).await {
                            Ok(data) => {
                                let table_config: Option<TableConfig> =
                                    encoder.decode(data.as_slice());

                                match table_config {
                                    Some(mut table_config) => {
                                        let target = table_config
                                            .columns
                                            .iter_mut()
                                            .find(|e| &e.name == &column_name);

                                        match target {
                                            Some(target) => {
                                                target.data_type = action.data_type;
                                            }
                                            None => {
                                                return Err(ExecuteError::boxed(format!(
                                                    "column '{}' not exists ",
                                                    column_name
                                                )));
                                            }
                                        }

                                        tokio::fs::write(config_path, encoder.encode(table_config))
                                            .await?;
                                    }
                                    None => {
                                        return Err(ExecuteError::boxed("invalid config data"));
                                    }
                                }
                            }
                            Err(error) => match error.kind() {
                                ErrorKind::NotFound => {
                                    return Err(ExecuteError::boxed("table not found"));
                                }
                                _ => {
                                    return Err(ExecuteError::boxed(format!("{:?}", error)));
                                }
                            },
                        }
                    }
                }
            }
            AlterTableAction::DropColumn(action) => {
                // TODO: 실 데이터 목록에도 반영하기

                // config data 파일 내용 변경
                let config_path = table_path.clone().join("table.config");

                match tokio::fs::read(&config_path).await {
                    Ok(data) => {
                        let table_config: Option<TableConfig> = encoder.decode(data.as_slice());

                        match table_config {
                            Some(mut table_config) => {
                                if let None = table_config
                                    .columns
                                    .iter()
                                    .find(|e| &e.name == &action.column_name)
                                {
                                    return Err(ExecuteError::boxed(format!(
                                        "column '{}' not exists ",
                                        action.column_name
                                    )));
                                }

                                table_config
                                    .columns
                                    .retain(|e| e.name != action.column_name);

                                tokio::fs::write(config_path, encoder.encode(table_config)).await?;
                            }
                            None => {
                                return Err(ExecuteError::boxed("invalid config data"));
                            }
                        }
                    }
                    Err(error) => match error.kind() {
                        ErrorKind::NotFound => {
                            return Err(ExecuteError::boxed("table not found"));
                        }
                        _ => {
                            return Err(ExecuteError::boxed(format!("{:?}", error)));
                        }
                    },
                }
            }
            AlterTableAction::RenameColumn(action) => {
                // TODO: 실 데이터 목록에도 반영하기

                // config data 파일 내용 변경
                let config_path = table_path.clone().join("table.config");

                match tokio::fs::read(&config_path).await {
                    Ok(data) => {
                        let table_config: Option<TableConfig> = encoder.decode(data.as_slice());

                        match table_config {
                            Some(mut table_config) => {
                                if table_config
                                    .columns
                                    .iter()
                                    .any(|e| &e.name == &action.to_name)
                                {
                                    return Err(ExecuteError::boxed(format!(
                                        "column '{}' already exists ",
                                        action.to_name
                                    )));
                                }

                                let target = table_config
                                    .columns
                                    .iter_mut()
                                    .find(|e| &e.name == &action.from_name);

                                match target {
                                    Some(target) => {
                                        target.name = action.to_name;
                                    }
                                    None => {
                                        return Err(ExecuteError::boxed(format!(
                                            "column '{}' not exists ",
                                            action.from_name
                                        )));
                                    }
                                }

                                tokio::fs::write(config_path, encoder.encode(table_config)).await?;
                            }
                            None => {
                                return Err(ExecuteError::boxed("invalid config data"));
                            }
                        }
                    }
                    Err(error) => match error.kind() {
                        ErrorKind::NotFound => {
                            return Err(ExecuteError::boxed("table not found"));
                        }
                        _ => {
                            return Err(ExecuteError::boxed(format!("{:?}", error)));
                        }
                    },
                }
            }
            AlterTableAction::None => {}
        }

        Ok(ExecuteResult {
            columns: (vec![ExecuteColumn {
                name: "desc".into(),
                data_type: ExecuteColumnType::String,
            }]),
            rows: (vec![ExecuteRow {
                fields: vec![ExecuteField::String("alter table".into())],
            }]),
        })
    }
}