//! CLI configuration loader helper for all backends
//!
//! Provides a unified pattern for loading backend configuration files
//! with support for both explicit `-c FILE` and environment-based search.

use crate::error::{Error, Result};
use std::path::Path;

/// Load backend-specific configuration file
///
/// If `cli_config_path` is provided, uses that file exclusively.
/// Otherwise, searches in order:
/// 1. `~/.config/typedialog/{backend_name}/{TYPEDIALOG_ENV}.toml`
/// 2. `~/.config/typedialog/{backend_name}/config.toml`
/// 3. Returns default value
///
/// # Arguments
/// * `backend_name` - Name of backend (cli, tui, web, ai)
/// * `cli_config_path` - Optional explicit config file path from `-c` flag
/// * `default` - Default config to return if file not found
///
/// # Example
///
/// ```ignore
/// use typedialog_core::config::load_backend_config;
/// use std::path::PathBuf;
///
/// let config = load_backend_config::<MyBackendConfig>(
///     "cli",
///     Some(PathBuf::from("custom.toml").as_path()),
///     MyBackendConfig::default()
/// )?;
/// ```
pub fn load_backend_config<T>(
    backend_name: &str,
    cli_config_path: Option<&Path>,
    default: T,
) -> Result<T>
where
    T: serde::de::DeserializeOwned + Default,
{
    // If CLI path provided, use it exclusively
    if let Some(path) = cli_config_path {
        return load_from_file(path);
    }

    // Otherwise try search order
    let config_dir = dirs::config_dir()
        .unwrap_or_else(|| {
            std::path::PathBuf::from(std::env::var("HOME").unwrap_or_else(|_| ".".to_string()))
        })
        .join("typedialog")
        .join(backend_name);

    // Try environment-specific config first
    let env = std::env::var("TYPEDIALOG_ENV").unwrap_or_else(|_| "default".to_string());
    let env_config_path = config_dir.join(format!("{}.toml", env));
    if env_config_path.exists() {
        if let Ok(config) = load_from_file::<T>(&env_config_path) {
            return Ok(config);
        }
    }

    // Try generic config.toml
    let generic_config_path = config_dir.join("config.toml");
    if generic_config_path.exists() {
        if let Ok(config) = load_from_file::<T>(&generic_config_path) {
            return Ok(config);
        }
    }

    // Return default
    Ok(default)
}

/// Load configuration from TOML file
fn load_from_file<T>(path: &Path) -> Result<T>
where
    T: serde::de::DeserializeOwned,
{
    let content = std::fs::read_to_string(path).map_err(|e| {
        Error::validation_failed(format!(
            "Failed to read config file '{}': {}",
            path.display(),
            e
        ))
    })?;

    toml::from_str(&content).map_err(|e| {
        Error::validation_failed(format!(
            "Failed to parse config file '{}': {}",
            path.display(),
            e
        ))
    })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[derive(serde::Serialize, serde::Deserialize, Debug, Default, PartialEq, Clone)]
    struct TestConfig {
        name: String,
        value: i32,
    }

    #[test]
    fn test_load_with_default() {
        let default = TestConfig {
            name: "default".to_string(),
            value: 42,
        };
        let config = load_backend_config::<TestConfig>("test", None, default.clone()).unwrap();
        // Should return default when no config found
        assert_eq!(config, default);
    }

    #[test]
    fn test_load_with_explicit_path() {
        // Create test config file
        let temp_dir = std::env::temp_dir();
        let test_config_path = temp_dir.join("test-backend-config.toml");
        let test_content = r#"
name = "test"
value = 100
"#;
        std::fs::write(&test_config_path, test_content).ok();

        let default = TestConfig::default();
        let config =
            load_backend_config::<TestConfig>("test", Some(test_config_path.as_path()), default)
                .unwrap();

        assert_eq!(config.name, "test");
        assert_eq!(config.value, 100);

        // Cleanup
        std::fs::remove_file(test_config_path).ok();
    }
}
