use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;

use crate::constants::DEFAULT_API_URL;

/// Profile configuration stored in the config file
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Profile {
    pub api_key: String,
    pub project_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub api_url: Option<String>,
}

/// Config file schema for ~/.config/mithril/config.yaml
#[derive(Debug, Serialize, Deserialize)]
pub struct ConfigFile {
    pub current_profile: String,
    pub profiles: HashMap<String, Profile>,
}

/// Get the path to the config file
pub fn get_config_path() -> Result<PathBuf> {
    let config_home = match std::env::var("XDG_CONFIG_HOME") {
        Ok(path) => path,
        Err(_) => {
            let home = std::env::var("HOME").context("HOME environment variable not set")?;
            format!("{}/.config", home)
        }
    };
    Ok(PathBuf::from(config_home)
        .join("mithril")
        .join("config.yaml"))
}

/// Load an existing config file, returns None if it doesn't exist
pub fn load_config_file(path: &PathBuf) -> Result<Option<ConfigFile>> {
    if !path.exists() {
        return Ok(None);
    }

    let content = std::fs::read_to_string(path)
        .with_context(|| format!("Failed to read config from {}", path.display()))?;

    let config: ConfigFile = serde_yaml::from_str(&content)
        .with_context(|| format!("Failed to parse config from {}", path.display()))?;

    Ok(Some(config))
}

/// Load the current profile from config file
pub fn load_current_profile() -> Result<Option<Profile>> {
    let path = get_config_path()?;
    let config_file = load_config_file(&path)?;

    let Some(config) = config_file else {
        return Ok(None);
    };

    // MITHRIL_PROFILE env var overrides current_profile from config file
    let profile_name =
        std::env::var("MITHRIL_PROFILE").unwrap_or_else(|_| config.current_profile.clone());

    let profile = config
        .profiles
        .get(&profile_name)
        .with_context(|| format!("Profile '{}' not found in config", profile_name))?
        .clone();

    Ok(Some(profile))
}

/// Resolve the effective API URL from env var, config file, or default
pub fn resolve_api_url(default: &str) -> String {
    std::env::var("MITHRIL_API_URL")
        .ok()
        .or_else(|| {
            load_current_profile()
                .ok()
                .flatten()
                .and_then(|p| p.api_url)
        })
        .unwrap_or_else(|| default.to_string())
}

/// Resolved configuration for Mithril operations (used by Python via PyO3)
#[derive(Debug, Clone)]
#[cfg_attr(feature = "extension-module", pyo3::pyclass(get_all))]
pub struct MithrilConfig {
    pub api_key: String,
    pub project_id: String,
    pub api_url: String,
}

/// Load and validate configuration from environment and config file.
///
/// Priority: env vars > config file > defaults
///
/// Returns MithrilConfig or error message if required fields are missing.
pub fn load_config() -> std::result::Result<MithrilConfig, String> {
    // Start with env vars (highest precedence)
    let mut api_key = std::env::var("MITHRIL_API_KEY").ok();
    let mut project_id = std::env::var("MITHRIL_PROJECT").ok();
    let mut api_url = std::env::var("MITHRIL_API_URL").ok();

    // Fill in from config file (lower precedence)
    if let Ok(Some(profile)) = load_current_profile() {
        if api_key.is_none() {
            api_key = Some(profile.api_key);
        }
        if project_id.is_none() {
            project_id = Some(profile.project_id);
        }
        if api_url.is_none() {
            api_url = profile.api_url;
        }
    }

    // Validate required fields
    let api_key = api_key
        .filter(|s| !s.is_empty())
        .ok_or("MITHRIL_API_KEY not set. Run `ml setup` to configure.")?;

    let project_id = project_id
        .filter(|s| !s.is_empty())
        .ok_or("MITHRIL_PROJECT not set. Run `ml setup` to configure.")?;

    // Apply default for optional fields
    let api_url = api_url
        .filter(|s| !s.is_empty())
        .unwrap_or_else(|| DEFAULT_API_URL.to_string());

    Ok(MithrilConfig {
        api_key,
        project_id,
        api_url,
    })
}

// PyO3 bindings (only compiled with extension-module feature)
#[cfg(feature = "extension-module")]
pub mod python {
    use super::*;
    use pyo3::prelude::*;

    /// Load and validate Mithril configuration.
    ///
    /// Raises RuntimeError if required configuration is missing.
    #[pyfunction]
    #[pyo3(name = "load_config")]
    pub fn py_load_config() -> PyResult<MithrilConfig> {
        load_config().map_err(pyo3::exceptions::PyRuntimeError::new_err)
    }
}
