use chrono;
use serde::{Deserialize, Serialize};
use toml;

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public module 'sections'. Replace with real documentation. (2025-12-30T22:23:21Z)
pub mod sections;
use sections::{KamSection, MmrlSection, PropSection, TmplSection, ToolSection};

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public struct 'RuleConfig'. Replace with real documentation. (2025-12-30T22:23:21Z)
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
pub struct RuleConfig {
    /// Whether this rule is enabled for the current project.
    /// When omitted the default is to enable the rule.
    pub enabled: Option<bool>,

    /// Whether this rule is allowed to perform auto-fixes when `--fix` is used.
    /// When omitted the default is to allow fixes.
    pub fix: Option<bool>,
}

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public module 'enums'. Replace with real documentation. (2025-12-30T22:23:21Z)
pub mod enums;

// KamToml：module.prop、update.json和其他元数据的超集
// 设计灵感来自pyproject.toml，使用分层section结构
/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public struct 'KamToml'. Replace with real documentation. (2025-12-30T22:23:21Z)
#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(non_snake_case)]
pub struct KamToml {
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public field 'prop' in 'KamToml'. Replace with real documentation. (2025-12-30T22:23:21Z)
    pub prop: PropSection,
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public field 'mmrl' in 'KamToml'. Replace with real documentation. (2025-12-30T22:23:21Z)
    pub mmrl: Option<MmrlSection>,
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public field 'kam' in 'KamToml'. Replace with real documentation. (2025-12-30T22:23:21Z)
    pub kam: KamSection,

    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public field 'tmpl' in 'KamToml'. Replace with real documentation. (2025-12-30T22:23:21Z)
    pub tmpl: Option<TmplSection>,
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public field 'tool' in 'KamToml'. Replace with real documentation. (2025-12-30T22:23:21Z)
    pub tool: Option<ToolSection>,

    /// Per-project rule configuration. Keys are rule IDs (e.g. "trailing_whitespace"),
    /// values are per-rule options (enabled/fix). If omitted, all builtin rules use
    /// their default behavior.
    pub rules: Option<std::collections::HashMap<String, RuleConfig>>,

    // lib字段在kam.lib!
    #[serde(skip)]
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public field 'raw' in 'KamToml'. Replace with real documentation. (2025-12-30T22:23:21Z)
    pub raw: String,
}
impl Default for KamToml {
    fn default() -> Self {
        // Use defaults from section Default impls where appropriate.
        let mut default = Self::from_prop(PropSection::default());
        default.mmrl = Some(MmrlSection::default());
        default.kam = KamSection::default();
        default.raw = String::new();
        default
    }
}

impl KamToml {
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public function 'generate_description'. Replace with real documentation. (2025-12-30T22:23:21Z)
    #[must_use]
    pub fn generate_description(module_type: &enums::ModuleType) -> String {
        match module_type {
            enums::ModuleType::Kam => "A kam module",
            enums::ModuleType::Template => "A template module",
        }
        .to_string()
    }

    // 从PropSection构造KamToml（用于默认值组合）
    // 这个辅助函数保持和其他构造函数相同的签名
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public function 'from_prop'. Replace with real documentation. (2025-12-30T22:23:21Z)
    #[must_use]
    pub fn from_prop(prop: PropSection) -> Self {
        Self {
            prop,
            mmrl: Some(MmrlSection::default()),
            kam: KamSection::default(),
            tmpl: Some(TmplSection::default()),
            tool: Some(ToolSection::default()),
            // Initialize rules to None by default (per-project override can populate this)
            rules: None,
            raw: String::new(),
        }
    }

    /// Create a new KamToml with current timestamp for versionCode
    #[must_use]
    pub fn new_with_current_timestamp(
        id: String,
        name: String,
        version: String,
        author: Option<String>,
        description: String,
        update_json: Option<String>,
        module_type: Option<enums::ModuleType>,
    ) -> Self {
        let mut kt = Self::from_prop(PropSection {
            id,
            name,
            version,
            versionCode: chrono::Utc::now().timestamp_millis(),
            author,
            description,
            updateJson: update_json,
            metamodule: false,
        });
        if let Some(mt) = module_type {
            kt.kam.module_type = mt;
        }
        kt
    }

    /// Load `KamToml` from a directory by reading `kam.toml`.
    ///
    /// # Errors
    /// Returns an error when `kam.toml` is missing, cannot be read, or cannot be parsed.
    pub fn load_from_dir<P: AsRef<std::path::Path>>(dir: P) -> crate::errors::Result<Self> {
        let path = dir.as_ref().join("kam.toml");
        Self::load_from_file(path)
    }

    /// Load `KamToml` from a TOML file.
    ///
    /// # Errors
    /// Returns an error when the file cannot be read or its TOML content is invalid.
    pub fn load_from_file<P: AsRef<std::path::Path>>(path: P) -> crate::errors::Result<Self> {
        let path_buf = path.as_ref().to_path_buf();
        let content = std::fs::read_to_string(&path_buf).map_err(|e| {
            if e.kind() == std::io::ErrorKind::NotFound {
                crate::errors::KamTomlError::NotFound(path_buf.display().to_string()).into()
            } else {
                crate::errors::KamError::Io(e)
            }
        })?;
        let mut kt: Self = toml::from_str(&content)?;
        kt.raw = content;
        Ok(kt)
    }

    /// Write `KamToml` to a directory as `kam.toml`.
    ///
    /// # Errors
    /// Returns an error when serialization fails or the destination file cannot be written.
    pub fn write_to_dir<P: AsRef<std::path::Path>>(&self, dir: P) -> crate::errors::Result<()> {
        let path = dir.as_ref().join("kam.toml");
        let content = toml::to_string_pretty(self)?;
        std::fs::write(path, content)?;
        Ok(())
    }

    /// Apply template variables to the `KamToml` structure.
    ///
    /// # Errors
    /// Returns an error when the current TOML cannot be serialized, parsed, or reloaded
    /// after applying variables.
    pub fn apply_vars(&mut self, kam_vars: Vec<(String, String)>) -> crate::errors::Result<()> {
        // If `raw` is empty, fall back to the current struct by serializing it to TOML
        let mut value: toml::Value = if self.raw.trim().is_empty() {
            let s = toml::to_string_pretty(self)?;
            toml::from_str(&s)?
        } else {
            toml::from_str(&self.raw)?
        };
        for (key, val) in kam_vars {
            let key = key.strip_prefix('#').unwrap_or(&key);
            Self::set_value_by_path(&mut value, key, &val);
        }
        self.raw = toml::to_string_pretty(&value)?;
        *self = toml::from_str(&self.raw)?;
        Ok(())
    }

    fn set_value_by_path(value: &mut toml::Value, path: &str, new_value: &str) {
        let parts: Vec<&str> = path.split('.').collect();
        let mut current = value.as_table_mut().unwrap();
        for (_i, &part) in parts.iter().enumerate().take(parts.len() - 1) {
            if !current.contains_key(part) {
                current.insert(
                    part.to_string(),
                    toml::Value::Table(toml::map::Map::default()),
                );
            }
            current = current[part].as_table_mut().unwrap();
        }
        let last = &parts[parts.len() - 1];
        if *last == "versionCode" {
            if let Ok(num) = new_value.parse::<i64>() {
                current.insert(last.to_string(), toml::Value::Integer(num));
            }
        } else {
            current.insert(last.to_string(), toml::Value::String(new_value.to_string()));
        }
    }
}
