//! Configuration loader for typedialog-prov-gen.
//!
//! Loads configuration from:
//! 1. ~/.config/typedialog/prov-gen/{TYPEDIALOG_ENV}.toml
//! 2. ~/.config/typedialog/prov-gen/default.toml
//! 3. Hardcoded defaults

use crate::error::Result;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
    pub provisioning: ProvisioningConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProvisioningConfig {
    pub output_dir: String,
    pub default_providers: Vec<String>,
    pub generation: GenerationConfig,
    pub templates: TemplatesConfig,
    pub infrastructure: InfrastructureConfig,
    pub nickel: NickelConfig,
    pub ai: AiConfig,
    pub logging: LoggingConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerationConfig {
    pub overwrite: bool,
    pub dry_run: bool,
    pub verbose: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplatesConfig {
    pub base_path: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub custom_path: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InfrastructureConfig {
    pub environment: String,
    pub region: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NickelConfig {
    pub validate_schemas: bool,
    pub generate_defaults: bool,
    pub use_constraints: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AiConfig {
    pub enabled: bool,
    pub provider: String,
    pub model: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LoggingConfig {
    pub level: String,
    pub file: bool,
}

impl Config {
    /// Load configuration from explicit path or standard locations or defaults.
    pub fn load(config_path: Option<&Path>) -> Result<Self> {
        // If explicit config provided, use it exclusively
        if let Some(path) = config_path {
            return Self::from_file(path);
        }

        // Try environment-specific config
        if let Ok(env) = std::env::var("TYPEDIALOG_ENV") {
            if let Some(config_dir) = dirs::config_dir() {
                let config_path = config_dir
                    .join("typedialog")
                    .join("prov-gen")
                    .join(format!("{}.toml", env));
                if config_path.exists() {
                    return Self::from_file(&config_path);
                }
            }
        }

        // Try default user config
        if let Some(config_dir) = dirs::config_dir() {
            let config_path = config_dir
                .join("typedialog")
                .join("prov-gen")
                .join("config.toml");
            if config_path.exists() {
                return Self::from_file(&config_path);
            }
        }

        // Try project config
        let project_config_path = Path::new("config/prov-gen/default.toml");
        if project_config_path.exists() {
            return Self::from_file(project_config_path);
        }

        // Return hardcoded defaults
        Ok(Self::default())
    }

    /// Load configuration from a specific file.
    fn from_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)?;
        toml::from_str(&content).map_err(|e| {
            std::io::Error::new(
                std::io::ErrorKind::InvalidData,
                format!("Failed to parse config: {}", e),
            )
            .into()
        })
    }

    /// Get the absolute path to templates directory.
    pub fn templates_dir(&self) -> PathBuf {
        if let Some(custom) = &self.provisioning.templates.custom_path {
            return Self::expand_path(custom);
        }

        let base = &self.provisioning.templates.base_path;

        // Expand ~ to home directory
        let expanded = Self::expand_path(base);

        // If expanded path is absolute, use it
        if expanded.is_absolute() && expanded.exists() {
            return expanded;
        }

        // Try relative to project
        let project_path = Path::new(".").join(base);
        if project_path.exists() {
            return project_path;
        }

        // Try relative to binary location
        if let Ok(exe_path) = std::env::current_exe() {
            if let Some(parent) = exe_path.parent() {
                let relative_path = parent
                    .join("..")
                    .join("share")
                    .join("typedialog-prov-gen")
                    .join(base);
                if relative_path.exists() {
                    return relative_path;
                }
            }
        }

        // Return expanded path as-is (will fail at runtime if not found)
        expanded
    }

    /// Expand ~ to home directory.
    fn expand_path(path: &str) -> PathBuf {
        if path.starts_with("~/") || path == "~" {
            if let Some(home_dir) = dirs::home_dir() {
                let suffix = if path == "~" {
                    String::new()
                } else {
                    path[2..].to_string()
                };
                return home_dir.join(suffix);
            }
        }
        PathBuf::from(path)
    }
}

impl Default for Config {
    fn default() -> Self {
        Self {
            provisioning: ProvisioningConfig {
                output_dir: "./provisioning".to_string(),
                default_providers: vec!["aws".to_string(), "hetzner".to_string()],
                generation: GenerationConfig {
                    overwrite: false,
                    dry_run: false,
                    verbose: false,
                },
                templates: TemplatesConfig {
                    base_path: "~/.config/typedialog/prov-gen/templates".to_string(),
                    custom_path: None,
                },
                infrastructure: InfrastructureConfig {
                    environment: "development".to_string(),
                    region: "us-east-1".to_string(),
                },
                nickel: NickelConfig {
                    validate_schemas: true,
                    generate_defaults: true,
                    use_constraints: true,
                },
                ai: AiConfig {
                    enabled: false,
                    provider: "claude".to_string(),
                    model: "claude-3-5-sonnet-20241022".to_string(),
                },
                logging: LoggingConfig {
                    level: "info".to_string(),
                    file: false,
                },
            },
        }
    }
}
