use std::path::PathBuf;

use dadk_config::common::target_arch::TargetArch;
use serde::de::Error;
use toml::Value;

use super::{
    task::{Dependency, TaskEnv},
    InnerParserError, ParserError,
};

// DADK用户配置关键字
pub(super) enum DADKUserConfigKey {
    Name,
    Version,
    Description,
    BuildOnce,
    InstallOnce,
    RustTarget,
    TargetArch,
    TaskType,
    Type,
    Source,
    SourcePath,
    Revision,
    Branch,
    Build,
    BuildCommand,
    Install,
    InDragonosPath,
    Clean,
    CleanCommand,
    Depends,
    Envs,
    BuildFromSource,
    InstallFromPrebuilt,
    Git,
    Local,
    Archive,
}

impl Into<&str> for DADKUserConfigKey {
    fn into(self) -> &'static str {
        match self {
            DADKUserConfigKey::Name => "name",
            DADKUserConfigKey::Version => "version",
            DADKUserConfigKey::Description => "description",
            DADKUserConfigKey::BuildOnce => "build-once",
            DADKUserConfigKey::InstallOnce => "install-once",
            DADKUserConfigKey::RustTarget => "rust-target",
            DADKUserConfigKey::TargetArch => "target-arch",
            DADKUserConfigKey::TaskType => "task-type",
            DADKUserConfigKey::Type => "type",
            DADKUserConfigKey::Source => "source",
            DADKUserConfigKey::SourcePath => "source-path",
            DADKUserConfigKey::Revision => "revison",
            DADKUserConfigKey::Branch => "branch",
            DADKUserConfigKey::Build => "build",
            DADKUserConfigKey::BuildCommand => "build-command",
            DADKUserConfigKey::Install => "install",
            DADKUserConfigKey::InDragonosPath => "in-dragonos-path",
            DADKUserConfigKey::Clean => "clean",
            DADKUserConfigKey::CleanCommand => "clean-command",
            DADKUserConfigKey::Depends => "depends",
            DADKUserConfigKey::Envs => "envs",
            DADKUserConfigKey::BuildFromSource => "build_from_source",
            DADKUserConfigKey::InstallFromPrebuilt => "install_from_prebuilt",
            DADKUserConfigKey::Archive => "archive",
            DADKUserConfigKey::Git => "git",
            DADKUserConfigKey::Local => "local",
        }
    }
}

impl TryFrom<&str> for DADKUserConfigKey {
    type Error = ParserError;
    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value {
            "name" => Ok(DADKUserConfigKey::Name),
            "version" => Ok(DADKUserConfigKey::Version),
            "description" => Ok(DADKUserConfigKey::Description),
            "build-once" => Ok(DADKUserConfigKey::BuildOnce),
            "install-once" => Ok(DADKUserConfigKey::InstallOnce),
            "rust-target" => Ok(DADKUserConfigKey::RustTarget),
            "target-arch" => Ok(DADKUserConfigKey::TargetArch),
            "task-type" => Ok(DADKUserConfigKey::TaskType),
            "type" => Ok(DADKUserConfigKey::Type),
            "source" => Ok(DADKUserConfigKey::Source),
            "source-path" => Ok(DADKUserConfigKey::SourcePath),
            "revison" => Ok(DADKUserConfigKey::Revision),
            "branch" => Ok(DADKUserConfigKey::Branch),
            "build" => Ok(DADKUserConfigKey::Build),
            "build-command" => Ok(DADKUserConfigKey::BuildCommand),
            "install" => Ok(DADKUserConfigKey::Install),
            "in-dragonos-path" => Ok(DADKUserConfigKey::InDragonosPath),
            "clean" => Ok(DADKUserConfigKey::Clean),
            "clean-command" => Ok(DADKUserConfigKey::CleanCommand),
            "depends" => Ok(DADKUserConfigKey::Depends),
            "envs" => Ok(DADKUserConfigKey::Envs),
            "build_from_source" => Ok(DADKUserConfigKey::BuildFromSource),
            "install_from_prebuilt" => Ok(DADKUserConfigKey::InstallFromPrebuilt),
            "archive" => Ok(DADKUserConfigKey::Archive),
            "git" => Ok(DADKUserConfigKey::Git),
            "local" => Ok(DADKUserConfigKey::Local),
            _ => Err(ParserError {
                config_file: None,
                error: InnerParserError::TomlError(toml::de::Error::custom(format!(
                    "Unknown dadk_user_config_key: {}",
                    value
                ))),
            }),
        }
    }
}

pub(super) struct DADKUserConfig {
    pub(super) standard_config: DADKUserStandardConfig,
    pub(super) task_type_config: DADKUserTaskType,
    pub(super) build_config: DADKUserBuildConfig,
    pub(super) install_config: DADKUserInstallConfig,
    pub(super) clean_config: DADKUserCleanConfig,
    pub(super) depends_config: DADKUserDependsConfig,
    pub(super) envs_config: DADKUserEnvsConfig,
}

impl DADKUserConfig {
    pub(super) fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserConfig, ParserError> {
        Ok(Self {
            standard_config: DADKUserStandardConfig::parse(config_file, table)?,
            task_type_config: DADKUserTaskType::parse(config_file, table)?,
            build_config: DADKUserBuildConfig::parse(config_file, table)?,
            install_config: DADKUserInstallConfig::parse(config_file, table)?,
            clean_config: DADKUserCleanConfig::parse(config_file, table)?,
            depends_config: DADKUserDependsConfig::parse(config_file, table)?,
            envs_config: DADKUserEnvsConfig::parse(config_file, table)?,
        })
    }
}

/// 标准信息配置
#[derive(Debug)]
pub(super) struct DADKUserStandardConfig {
    pub(super) name: String,
    pub(super) version: String,
    pub(super) description: String,
    pub(super) build_once: bool,
    pub(super) install_once: bool,
    pub(super) rust_target: Option<String>,
    pub(super) target_arch: Vec<TargetArch>,
}

impl DADKUserStandardConfig {
    fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserStandardConfig, ParserError> {
        let name: String =
            TomlValueParser::parse_string(config_file, table, DADKUserConfigKey::Name.into())?;
        let version =
            TomlValueParser::parse_string(config_file, table, DADKUserConfigKey::Version.into())?;
        let description = TomlValueParser::parse_string(
            config_file,
            table,
            DADKUserConfigKey::Description.into(),
        )?;
        let build_once =
            TomlValueParser::parse_bool(config_file, table, DADKUserConfigKey::BuildOnce.into())?;
        let install_once =
            TomlValueParser::parse_bool(config_file, table, DADKUserConfigKey::InstallOnce.into())?;
        let rust_target =
            TomlValueParser::parse_option_string(table, DADKUserConfigKey::RustTarget.into());
        let target_arch: Vec<TargetArch> = match TomlValueParser::parse_option_array(
            table,
            DADKUserConfigKey::TargetArch.into(),
        ) {
            Some(value_vec) => {
                let mut target_arch_vec = Vec::new();
                for value in value_vec {
                    let target_arch =
                        TargetArch::try_from(value.as_str().unwrap()).map_err(|e| ParserError {
                            config_file: Some(config_file.clone()),
                            error: InnerParserError::TomlError(toml::de::Error::custom(e)),
                        })?;
                    target_arch_vec.push(target_arch);
                }
                target_arch_vec
            }
            None => vec![TargetArch::X86_64],
        };

        Ok(Self {
            name,
            version,
            description,
            build_once,
            install_once,
            rust_target,
            target_arch,
        })
    }
}

/// task-type配置
#[derive(Debug)]
pub(super) struct DADKUserTaskType {
    pub(super) config_file: PathBuf,
    pub(super) task_type: String,
    pub(super) source: String,
    pub(super) source_path: String,
    // git独有
    pub(super) revision: Option<String>,
    pub(super) branch: Option<String>,
}

impl DADKUserTaskType {
    fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserTaskType, ParserError> {
        let task_type_table =
            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::TaskType.into())?;
        let task_type = TomlValueParser::parse_string(
            config_file,
            &task_type_table,
            DADKUserConfigKey::Type.into(),
        )?;
        let source = TomlValueParser::parse_string(
            config_file,
            &task_type_table,
            DADKUserConfigKey::Source.into(),
        )?;

        let source_path = TomlValueParser::parse_string(
            config_file,
            &task_type_table,
            DADKUserConfigKey::SourcePath.into(),
        )?;

        let (branch, revision) =
            if source.to_lowercase().trim() == Into::<&str>::into(DADKUserConfigKey::Git) {
                let branch = TomlValueParser::parse_option_string(
                    &task_type_table,
                    DADKUserConfigKey::Branch.into(),
                );
                let revision = TomlValueParser::parse_option_string(
                    &task_type_table,
                    DADKUserConfigKey::Revision.into(),
                );
                (branch, revision)
            } else {
                (None, None)
            };

        Ok(Self {
            config_file: config_file.clone(),
            task_type,
            source,
            source_path,
            revision,
            branch,
        })
    }
}

/// build配置
#[derive(Debug)]
pub(super) struct DADKUserBuildConfig {
    pub(super) build_command: Option<String>,
}

impl DADKUserBuildConfig {
    fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserBuildConfig, ParserError> {
        let build_table =
            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Build.into())?;
        let build_command = TomlValueParser::parse_option_string(
            &build_table,
            DADKUserConfigKey::BuildCommand.into(),
        );
        Ok(Self { build_command })
    }
}

/// install配置
#[derive(Debug)]
pub(super) struct DADKUserInstallConfig {
    pub(super) in_dragonos_path: Option<PathBuf>,
}

impl DADKUserInstallConfig {
    fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserInstallConfig, ParserError> {
        let install_table =
            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Install.into())?;
        let in_dragonos_path = TomlValueParser::parse_option_string(
            &install_table,
            DADKUserConfigKey::InDragonosPath.into(),
        )
        .map(|path| PathBuf::from(path));

        Ok(Self { in_dragonos_path })
    }
}

/// clean配置
#[derive(Debug)]
pub(super) struct DADKUserCleanConfig {
    pub(super) clean_command: Option<String>,
}

impl DADKUserCleanConfig {
    fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserCleanConfig, ParserError> {
        let clean_table =
            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Clean.into())?;
        let clean_command = TomlValueParser::parse_option_string(
            &clean_table,
            DADKUserConfigKey::CleanCommand.into(),
        );
        Ok(Self { clean_command })
    }
}

/// depends配置
#[derive(Debug)]
pub(super) struct DADKUserDependsConfig {
    pub(super) depends: Vec<Dependency>,
}

impl DADKUserDependsConfig {
    fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserDependsConfig, ParserError> {
        let depends_table =
            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Depends.into())?;
        let depends = depends_table
            .iter()
            .map(|(key, value)| Dependency {
                name: key.clone(),
                version: value.as_str().unwrap().to_string(),
            })
            .collect::<Vec<Dependency>>();
        Ok(Self { depends })
    }
}
/// envs配置
#[derive(Debug)]
pub(super) struct DADKUserEnvsConfig {
    pub(super) envs: Option<Vec<TaskEnv>>,
}

impl DADKUserEnvsConfig {
    fn parse(
        config_file: &PathBuf,
        table: &toml::value::Table,
    ) -> Result<DADKUserEnvsConfig, ParserError> {
        let envs_table: toml::map::Map<String, Value> =
            TomlValueParser::parse_table(config_file, table, DADKUserConfigKey::Envs.into())?;
        let envs_vec = if !envs_table.is_empty() {
            Some(
                envs_table
                    .iter()
                    .map(|(key, value)| TaskEnv {
                        key: key.clone(),
                        value: value.as_str().unwrap().to_string(),
                    })
                    .collect::<Vec<TaskEnv>>(),
            )
        } else {
            None
        };

        Ok(DADKUserEnvsConfig { envs: envs_vec })
    }
}

struct TomlValueParser;

impl TomlValueParser {
    // 解析String类型的值
    fn parse_string(
        config_file: &PathBuf,
        table: &toml::value::Table,
        key: &'static str,
    ) -> Result<String, ParserError> {
        let value = table.get(key).ok_or(ParserError {
            config_file: Some(config_file.clone()),
            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
        })?;
        Ok(value.as_str().unwrap().to_string())
    }

    // 解析Option<String>类型的值
    fn parse_option_string(table: &toml::value::Table, key: &'static str) -> Option<String> {
        let value = table.get(key);
        value.map(|v| v.as_str().unwrap().to_string())
    }

    // 解析Table类型的值
    fn parse_table(
        config_file: &PathBuf,
        table: &toml::value::Table,
        key: &'static str,
    ) -> Result<toml::value::Table, ParserError> {
        let value = table.get(key).ok_or(ParserError {
            config_file: Some(config_file.clone()),
            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
        })?;
        let table = value.as_table().ok_or(ParserError {
            config_file: Some(config_file.clone()),
            error: InnerParserError::TomlError(toml::de::Error::custom(format!(
                "{} is not a table",
                key
            ))),
        })?;
        Ok(table.clone())
    }

    #[allow(dead_code)]
    // 解析Array类型的值
    fn parse_array(
        config_file: &PathBuf,
        table: &toml::value::Table,
        key: &'static str,
    ) -> Result<Vec<toml::Value>, ParserError> {
        let value = table.get(key).ok_or(ParserError {
            config_file: Some(config_file.clone()),
            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
        })?;
        let array = value.as_array().ok_or(ParserError {
            config_file: Some(config_file.clone()),
            error: InnerParserError::TomlError(toml::de::Error::custom(format!(
                "{} is not an array",
                key
            ))),
        })?;
        Ok(array.clone())
    }

    // 解析Option<Array>类型的值
    fn parse_option_array(
        table: &toml::value::Table,
        key: &'static str,
    ) -> Option<Vec<toml::Value>> {
        let value = table.get(key);
        value.map(|v| v.as_array().unwrap().clone())
    }

    // 解析Boolean类型的值
    fn parse_bool(
        config_file: &PathBuf,
        table: &toml::value::Table,
        key: &'static str,
    ) -> Result<bool, ParserError> {
        let value = table.get(key).ok_or(ParserError {
            config_file: Some(config_file.clone()),
            error: InnerParserError::TomlError(toml::de::Error::missing_field(key)),
        })?;
        let boolean = value.as_bool().ok_or(ParserError {
            config_file: Some(config_file.clone()),
            error: InnerParserError::TomlError(toml::de::Error::custom(format!(
                "{} is not a boolean",
                key
            ))),
        })?;
        Ok(boolean)
    }
}
