use serde::{Deserialize, Serialize};
use std::fmt;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ConfigType {
    Bool,
    Int,
    Hex,
    String,
    Choice,
}

impl ConfigType {
    pub fn format_value_c(&self, val: &toml::Value) -> Option<String> {
        match self {
            ConfigType::Bool => val
                .as_bool()
                .map(|b| if b { "1".into() } else { "0".into() }),
            ConfigType::Int => val.as_integer().map(|i| i.to_string()),
            ConfigType::Hex => val.as_integer().map(|i| format!("0x{:x}", i)),
            ConfigType::String | ConfigType::Choice => val.as_str().map(|s| format!("\"{}\"", s)),
        }
    }

    pub fn format_value_rust(&self, val: &toml::Value) -> Option<String> {
        match self {
            ConfigType::Bool => val.as_bool().map(|b| b.to_string()),
            ConfigType::Int => val.as_integer().map(|i| i.to_string()),
            ConfigType::Hex => val.as_integer().map(|i| format!("0x{:x}", i)),
            ConfigType::String | ConfigType::Choice => val.as_str().map(|s| format!("\"{}\"", s)),
        }
    }

    pub fn rust_type(&self) -> &'static str {
        match self {
            ConfigType::Bool => "bool",
            ConfigType::Int => "i64",
            ConfigType::Hex => "u64",
            ConfigType::String | ConfigType::Choice => "&str",
        }
    }
}

impl fmt::Display for ConfigType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigItem {
    pub name: String,
    #[serde(rename = "type")]
    pub config_type: ConfigType,
    pub default: Option<toml::Value>,
    pub desc: String,
    pub depends_on: Option<String>,
    pub help: Option<String>,
    pub options: Option<Vec<String>>,
    pub feature: Option<Vec<String>>,
    pub range: Option<(i64, i64)>,
    pub regex: Option<String>,
    pub rust_type: Option<String>,
}

impl ConfigItem {
    pub fn validate(&self, value: &toml::Value) -> Result<(), String> {
        match self.config_type {
            ConfigType::Bool => {
                if !value.is_bool() {
                    return Err(format!(
                        "Config '{}' expected bool, found {:?}",
                        self.name, value
                    ));
                }
            }
            ConfigType::Int | ConfigType::Hex => {
                let val = value.as_integer().ok_or_else(|| {
                    format!("Config '{}' expected integer, found {:?}", self.name, value)
                })?;
                if let Some((min, max)) = self.range {
                    if val < min || val > max {
                        return Err(format!(
                            "Config '{}' value {} out of range [{}, {}]",
                            self.name, val, min, max
                        ));
                    }
                }
                if let Some(rt) = &self.rust_type {
                    match rt.as_str() {
                        "u8" => {
                            if val < 0 || val > u8::MAX as i64 {
                                return Err(format!(
                                    "Config '{}' value {} exceeds u8 range",
                                    self.name, val
                                ));
                            }
                        }
                        "i8" => {
                            if val < i8::MIN as i64 || val > i8::MAX as i64 {
                                return Err(format!(
                                    "Config '{}' value {} exceeds i8 range",
                                    self.name, val
                                ));
                            }
                        }
                        "u16" => {
                            if val < 0 || val > u16::MAX as i64 {
                                return Err(format!(
                                    "Config '{}' value {} exceeds u16 range",
                                    self.name, val
                                ));
                            }
                        }
                        "i16" => {
                            if val < i16::MIN as i64 || val > i16::MAX as i64 {
                                return Err(format!(
                                    "Config '{}' value {} exceeds i16 range",
                                    self.name, val
                                ));
                            }
                        }
                        "u32" => {
                            if val < 0 || val > u32::MAX as i64 {
                                return Err(format!(
                                    "Config '{}' value {} exceeds u32 range",
                                    self.name, val
                                ));
                            }
                        }
                        "i32" => {
                            if val < i32::MIN as i64 || val > i32::MAX as i64 {
                                return Err(format!(
                                    "Config '{}' value {} exceeds i32 range",
                                    self.name, val
                                ));
                            }
                        }
                        "u64" => {
                            if val < 0 {
                                return Err(format!(
                                    "Config '{}' value {} cannot be negative for u64",
                                    self.name, val
                                ));
                            }
                        }
                        "usize" => {
                            if val < 0 {
                                return Err(format!(
                                    "Config '{}' value {} cannot be negative for usize",
                                    self.name, val
                                ));
                            }
                        }
                        _ => {}
                    }
                }
            }
            ConfigType::String => {
                let val = value.as_str().ok_or_else(|| {
                    format!("Config '{}' expected string, found {:?}", self.name, value)
                })?;
                if let Some(regex_str) = &self.regex {
                    let re = regex::Regex::new(regex_str)
                        .map_err(|e| format!("Invalid regex for config '{}': {}", self.name, e))?;
                    if !re.is_match(val) {
                        return Err(format!(
                            "Config '{}' value \"{}\" does not match regex / {} /",
                            self.name, val, regex_str
                        ));
                    }
                }
            }
            ConfigType::Choice => {
                let val = value.as_str().ok_or_else(|| {
                    format!(
                        "Config '{}' expected string (choice), found {:?}",
                        self.name, value
                    )
                })?;
                if let Some(options) = &self.options {
                    if !options.contains(&val.to_string()) {
                        return Err(format!(
                            "Config '{}' value \"{}\" is not a valid option. Valid options are: {:?}",
                            self.name, val, options
                        ));
                    }
                }
            }
        }
        Ok(())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Menu {
    pub title: String,
    pub desc: Option<String>,
    pub depends_on: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KconfigFile {
    pub title: Option<String>,
    pub desc: Option<String>,
    pub help: Option<String>,
    pub depends_on: Option<String>,
    #[serde(rename = "config")]
    pub configs: Option<Vec<ConfigItem>>,
    pub menu: Option<std::collections::BTreeMap<String, String>>,
}

/// Represents a node in the configuration hierarchy
#[derive(Debug, Clone)]
pub struct ConfigNode {
    pub desc: String,
    pub description: Option<String>,
    pub help: Option<String>,
    pub configs: Vec<ConfigItem>,
    pub children: Vec<ConfigNode>,
    pub path: String,
    pub depends_on: Option<String>,
}
