use crate::errors::KamError;
use clap::{Args, Subcommand};
use dialoguer::{Confirm, Input, Select, theme::ColorfulTheme};
use std::fs;
use std::path::{Path, PathBuf};

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public struct 'ConfigArgs'. Replace with real documentation. (2025-12-30T22:23:21Z)
#[derive(Args, Debug)]
pub struct ConfigArgs {
    /// Use the global configuration file (~/.kam/config.toml)
    #[arg(long)]
    pub global: bool,
    /// Force use of the local configuration file (project `.kam/config.toml`)
    #[arg(long, conflicts_with = "global")]
    pub local: bool,

    /// Interactive mode: guide the user through common configuration (language, root manager, etc.)
    #[arg(short = 'i', long = "interactive")]
    pub interactive: bool,

    #[command(subcommand)]
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public field 'command' in 'ConfigArgs'. Replace with real documentation. (2025-12-30T22:23:21Z)
    pub command: Option<ConfigCommand>,
}

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public enum 'ConfigCommand'. Replace with real documentation. (2025-12-30T22:23:21Z)
#[derive(Subcommand, Debug)]
pub enum ConfigCommand {
    /// Get a configuration value by key (dot-separated path)
    Get {
        /// Configuration key (dot-separated path, e.g., "ui.language")
        key: String,
    },
    /// Set a configuration value by key
    Set {
        /// Configuration key (dot-separated path, e.g., "ui.language")
        key: String,
        /// Configuration value to set
        value: String,
    },
    /// Unset (remove) a configuration value by key
    Unset {
        /// Configuration key (dot-separated path, e.g., "ui.language")
        key: String,
    },
    /// List all config values in the target file
    List,
    /// Show built-in configuration keys and their descriptions
    Show,
}

// 获取配置文件路径
// 支持全局配置（~/.kam/config.toml）和本地配置（项目/.kam/config.toml）
/// # Errors
///
/// Returns `KamError` when the path cannot be resolved (e.g., I/O failures when locating project or HOME).
pub fn get_config_paths(global: bool, local: bool) -> Result<PathBuf, KamError> {
    if global {
        // 强制使用全局配置。支持 `KAM_HOME` 环境变量来控制 kam 的家目录（默认：$HOME/.kam）
        let dir = crate::utils::kam_home_dir()?;
        Ok(dir.join("config.toml"))
    } else if local {
        // 强制使用本地配置
        // 向上查找kam.toml找到项目根目录，找不到就用当前目录
        let mut cwd = std::env::current_dir().map_err(KamError::Io)?;
        loop {
            if cwd.join("kam.toml").exists() {
                break;
            }
            if !cwd.pop() {
                break;
            }
        }
        // 如果找不到kam.toml，就用当前目录（虽然可能不太对）
        if !cwd.join("kam.toml").exists() {
            cwd = std::env::current_dir().map_err(KamError::Io)?;
        }
        let dir = cwd.join(".kam");
        Ok(dir.join("config.toml"))
    } else {
        // 默认行为：如果在项目里就用本地配置，否则用全局配置
        let mut cwd = std::env::current_dir().map_err(KamError::Io)?;
        loop {
            if cwd.join("kam.toml").exists() {
                let dir = cwd.join(".kam");
                return Ok(dir.join("config.toml"));
            }
            if !cwd.pop() {
                break;
            }
        }
        // 不在项目里，用全局配置
        let dir = crate::utils::kam_home_dir()?;
        Ok(dir.join("config.toml"))
    }
}

// 读取TOML配置文件
// 文件不存在就返回空表（这样不会报错）
/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public function 'read_language_from_config'. Replace with real documentation. (2025-12-30T22:23:21Z)
#[must_use]
pub fn read_language_from_config() -> Option<String> {
    // Prefer local config's language first, then fallback to global if not set locally.
    // This explicitly checks both local and global config files so a missing language
    // in the project's config will still allow a global preference to be used.

    // 1) Try forced local config (prefer a project-local setting)
    if let Ok(local_path) = get_config_paths(false, true)
        && let Ok(local_toml) = read_toml(&local_path)
    {
        // First check ui.language (preferred)
        if let Some(val) = get_value_by_path(&local_toml, "ui.language")
            && let Some(s) = val.as_str()
        {
            return Some(s.to_string());
        }
        // Fallback to `language`
        if let Some(val) = get_value_by_path(&local_toml, "language")
            && let Some(s) = val.as_str()
        {
            return Some(s.to_string());
        }
    }

    // 2) Fallback to global config if local didn't provide a language
    if let Ok(global_path) = get_config_paths(true, false)
        && let Ok(global_toml) = read_toml(&global_path)
    {
        // First check ui.language (preferred)
        if let Some(val) = get_value_by_path(&global_toml, "ui.language")
            && let Some(s) = val.as_str()
        {
            return Some(s.to_string());
        }
        // Fallback to `language`
        if let Some(val) = get_value_by_path(&global_toml, "language")
            && let Some(s) = val.as_str()
        {
            return Some(s.to_string());
        }
    }

    None
}

fn read_toml(path: &Path) -> Result<toml::Value, KamError> {
    if !path.exists() {
        return Ok(toml::Value::Table(toml::value::Table::default()));
    }
    let s = fs::read_to_string(path).map_err(KamError::Io)?;
    let v: toml::Value = toml::from_str(&s)
        .map_err(|e| KamError::CommandFailed(format!("Failed to parse config file: {e}")))?;
    Ok(v)
}

// 写入TOML配置文件
// 用pretty格式，这样看起来比较好看
fn write_toml(path: &Path, v: &toml::Value) -> Result<(), KamError> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent).map_err(KamError::Io)?;
    }
    let s = toml::to_string_pretty(v)
        .map_err(|e| KamError::CommandFailed(format!("Failed to serialize config: {e}")))?;
    fs::write(path, s).map_err(KamError::Io)?;
    Ok(())
}

#[allow(clippy::unwrap_used)] // TODO: refactor this helper to avoid unwraps and overlapping borrows
fn set_value_by_path(value: &mut toml::Value, path: &str, new_value: &str) {
    // Simpler, well-known implementation that uses `unwrap()` in places we control.
    // This is a temporary measure: a future refactor should remove unwraps and
    // make traversal safer / more ergonomic.
    let v = value;
    if !v.is_table() {
        *v = toml::Value::Table(toml::value::Table::default());
    }
    let parts: Vec<&str> = path.split('.').collect();
    let mut current = v.as_table_mut().unwrap();
    for (i, &part) in parts.iter().enumerate() {
        if i == parts.len() - 1 {
            if part == "versionCode" {
                if let Ok(num) = new_value.parse::<i64>() {
                    current.insert(part.to_string(), toml::Value::Integer(num));
                } else {
                    current.insert(part.to_string(), toml::Value::String(new_value.to_string()));
                }
            } else {
                current.insert(part.to_string(), toml::Value::String(new_value.to_string()));
            }
            return;
        }
        if !current.contains_key(part) {
            current.insert(
                part.to_string(),
                toml::Value::Table(toml::value::Table::default()),
            );
        }
        current = current[part].as_table_mut().unwrap();
    }
}

fn get_value_by_path(value: &toml::Value, path: &str) -> Option<toml::Value> {
    let mut current = value;
    let mut it = path.split('.').peekable();
    while let Some(part) = it.next() {
        // Ensure current is a table and fetch the next part; early-return on None
        let tbl = current.as_table()?;
        current = tbl.get(part)?;
        // If this is the last component, return the found value
        if it.peek().is_none() {
            return Some(current.clone());
        }
    }
    None
}

#[allow(clippy::unwrap_used)] // TODO: refactor this helper to avoid unwraps and return proper Result
fn unset_value_by_path(value: &mut toml::Value, path: &str) -> bool {
    let parts: Vec<&str> = path.split('.').collect();
    if !value.is_table() {
        return false;
    }
    let mut current_tbl = value.as_table_mut().unwrap();
    for (i, &part) in parts.iter().enumerate() {
        if i == parts.len() - 1 {
            return current_tbl.remove(part).is_some();
        } else if let Some(next) = current_tbl.get_mut(part) {
            if next.is_table() {
                current_tbl = next.as_table_mut().unwrap();
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    false
}

/// Built-in configuration keys and their descriptions
struct BuiltinConfigKey {
    key: &'static str,
    description_key: &'static str,
    example: &'static str,
}

const BUILTIN_KEYS: &[BuiltinConfigKey; 6] = &[
    BuiltinConfigKey {
        key: "ui.language",
        description_key: "UI language preference (preferred over 'language')",
        example: "en, zh, zh-CN",
    },
    BuiltinConfigKey {
        key: "language",
        description_key: "UI language preference (fallback, use 'ui.language' instead)",
        example: "en, zh, zh-CN",
    },
    BuiltinConfigKey {
        key: "prop.author",
        description_key: "Default author name for new projects (saved during init)",
        example: "Your Name",
    },
    BuiltinConfigKey {
        key: "prop.name",
        description_key: "Default module name for new projects (saved during init)",
        example: "My Module",
    },
    BuiltinConfigKey {
        key: "prop.version",
        description_key: "Default version for new projects (saved during init)",
        example: "1.0.0",
    },
    BuiltinConfigKey {
        key: "root.manager",
        description_key: "Preferred root manager for device installs (Magisk|KernelSU|APatchSU)",
        example: "Magisk",
    },
];

fn show_builtin_keys() {
    use crate::i18n::tr;

    println!("{}", tr("config.builtin_keys"));
    println!();

    for key_info in BUILTIN_KEYS {
        println!("  {}", key_info.key);
        println!("    {}", tr(key_info.description_key));
        println!("    {} {}", tr("config.example"), key_info.example);
        println!();
    }

    println!("{}", tr("config.note_custom_keys"));
}

