use std::path::Path;

use serde::de::DeserializeOwned;
use serde_json::Value;

use crate::error::{ConfigError, Result};
use crate::hierarchy;
use crate::nickel;

/// Carga un archivo de configuración (NCL, TOML o JSON) como JSON Value
///
/// # Arguments
/// - `path`: Ruta al archivo de configuración (.ncl, .toml o .json)
///
/// # Returns
/// `serde_json::Value` con el contenido parseado
///
/// # Errors
/// - `InvalidFormat` si la extensión no es .ncl, .toml o .json
/// - `NickelExportFailed` si el export de NCL falla
/// - `TomlParseFailed` o `JsonParseFailed` si el parsing falla
pub fn load_config<P: AsRef<Path>>(path: P) -> Result<Value> {
    let path = path.as_ref();

    match path.extension().and_then(|s| s.to_str()) {
        Some("ncl") => load_ncl_as_json(path),
        Some("toml") => load_toml_as_json(path),
        Some("json") => load_json_as_json(path),
        _ => {
            tracing::error!("Invalid config format: {:?}", path);
            Err(ConfigError::invalid_format(path))
        }
    }
}

/// Carga un archivo NCL y lo convierte a JSON
fn load_ncl_as_json<P: AsRef<Path>>(path: P) -> Result<Value> {
    let path = path.as_ref();
    let json_string = nickel::export_to_json(path)?;

    serde_json::from_str::<Value>(&json_string)
        .map_err(|e| ConfigError::json_failed(format!("Failed to parse NCL export as JSON: {}", e)))
}

/// Carga un archivo TOML y lo convierte a JSON para consistencia
fn load_toml_as_json<P: AsRef<Path>>(path: P) -> Result<Value> {
    let path = path.as_ref();

    let content = std::fs::read_to_string(path).map_err(|e| {
        ConfigError::io_error(format!("Failed to read TOML file {:?}: {}", path, e))
    })?;

    let value: toml::Value = toml::from_str(&content).map_err(|e| {
        ConfigError::toml_failed(format!("Failed to parse TOML file {:?}: {}", path, e))
    })?;

    // Convierte TOML a JSON para consistencia
    let json_value = serde_json::to_value(value)
        .map_err(|e| ConfigError::json_failed(format!("Failed to convert TOML to JSON: {}", e)))?;

    Ok(json_value)
}

/// Carga un archivo JSON directamente (pre-generado por nickel export en CI/CD)
fn load_json_as_json<P: AsRef<Path>>(path: P) -> Result<Value> {
    let path = path.as_ref();

    let content = std::fs::read_to_string(path).map_err(|e| {
        ConfigError::io_error(format!("Failed to read JSON file {:?}: {}", path, e))
    })?;

    serde_json::from_str::<Value>(&content).map_err(|e| {
        ConfigError::json_failed(format!("Failed to parse JSON file {:?}: {}", path, e))
    })
}

/// Trait que deben implementar todas las estructuras de configuración
pub trait ConfigLoader: DeserializeOwned + Sized + Default {
    /// Retorna el nombre del servicio (ej: "orchestrator", "mcp-server")
    fn service_name() -> &'static str;

    /// Carga la configuración usando la jerarquía estándar
    /// Usa el tipo de error específico de la implementación
    fn load() -> std::result::Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        let mut config = Self::load_from_hierarchy()?;
        config.apply_env_overrides()?;
        Ok(config)
    }

    /// Carga desde la jerarquía de configuración (env vars → archivos →
    /// defaults)
    fn load_from_hierarchy() -> std::result::Result<Self, Box<dyn std::error::Error + Send + Sync>>
    {
        let service = Self::service_name();

        // Intenta cargar desde la jerarquía
        if let Some(path) = hierarchy::resolve_config_path(service) {
            return Self::from_path(&path);
        }

        // Fallback a defaults
        tracing::debug!("No config file found for {} - using defaults", service);
        Ok(Self::default())
    }

    /// Carga desde un archivo específico
    fn from_path<P: AsRef<Path>>(
        path: P,
    ) -> std::result::Result<Self, Box<dyn std::error::Error + Send + Sync>> {
        let path = path.as_ref();
        let json_value = load_config(path)?;

        serde_json::from_value(json_value).map_err(|e| {
            Box::new(ConfigError::deserialization_failed(format!(
                "Failed to deserialize config from {:?}: {}",
                path, e
            ))) as Box<dyn std::error::Error + Send + Sync>
        })
    }

    /// Aplica overrides desde variables de entorno
    fn apply_env_overrides(
        &mut self,
    ) -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // Base implementation - servicios específicos pueden sobrescribir
        Ok(())
    }
}

/// Carga y deserializa una configuración de tipo genérico
/// Retorna un Box<dyn Error> como el trait ConfigLoader requiere
pub fn load_typed_config<T: ConfigLoader>(
) -> std::result::Result<T, Box<dyn std::error::Error + Send + Sync>> {
    T::load()
}

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

    #[test]
    fn test_load_config_invalid_extension() {
        let result = load_config("test.txt");
        assert!(matches!(result, Err(ConfigError::InvalidFormat { .. })));
    }
}
