//! Platform Config - Unified configuration loading for platform services
//!
//! This crate provides a unified interface for loading service configurations
//! from both Nickel (NCL) and TOML sources, with a hierarchical loading
//! strategy.
//!
//! # Features
//!
//! - **Dual Format Support**: Load configurations from `.ncl` (Nickel) or
//!   `.toml` files
//! - **Hierarchical Loading**: Environment variables → File-based → Defaults
//! - **JSON Intermediate**: All configs converted to JSON for consistent
//!   processing
//! - **Environment Overrides**: Service-specific env var overrides
//! - **Error Handling**: Rich, actionable error types
//!
//! # Usage
//!
//! Implement the `ConfigLoader` trait for your config struct:
//!
//! ```ignore
//! use platform_config::ConfigLoader;
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Debug, Default, Serialize, Deserialize)]
//! pub struct MyServiceConfig {
//!     pub server: ServerConfig,
//!     pub database: DatabaseConfig,
//! }
//!
//! impl ConfigLoader for MyServiceConfig {
//!     fn service_name() -> &'static str {
//!         "my-service"
//!     }
//!
//!     fn collect_env_overrides() -> serde_json::Value {
//!         // Return env vars as JSON matching the NCL schema shape.
//!         // These get merged into the NCL config and validated by contracts
//!         // BEFORE deserialization — no post-hoc mutation.
//!         serde_json::Value::Object(serde_json::Map::new())
//!     }
//! }
//!
//! // Load config
//! let config = MyServiceConfig::load()?;
//! ```

pub mod deployment;
pub mod error;
pub mod extensions;
pub mod format;
pub mod git;
pub mod hierarchy;
pub mod loader;
pub mod nickel;
pub mod oci;
pub mod resolver;
pub mod sops;
pub mod startup;
pub mod team_configs;

// Re-export main types
use std::env;
use std::path::PathBuf;

pub use deployment::{load_deployment_mode, DeploymentConfig};
pub use error::{ConfigError, Result};
pub use extensions::{CachedExtension, ExtensionSchemaCache};
pub use format::ConfigLoader;
pub use git::{expand_path, GitRepoCache};
pub use hierarchy::{config_base_path, find_config_file, resolve_config_path};
pub use loader::{ConfigLoaderExt, ConfigValidator};
pub use nickel::{export_with_overrides, is_nickel_available};
pub use resolver::{find_config_in_dir, find_config_in_dir_with_mode, ConfigResolver};
pub use startup::PlatformStartup;

/// Load service configuration from {service-name}.ncl in the platform config
/// directory
///
/// Searches in this order:
/// 1. $HOME/Library/Application
///    Support/provisioning/platform/config/{service-name}.ncl (macOS)
/// 2. $HOME/.config/provisioning/platform/config/{service-name}.ncl (Linux)
///
/// Returns the parsed JSON configuration or error if not found
pub fn load_service_config_from_ncl(service_name: &str) -> Result<serde_json::Value> {
    load_service_config_from_ncl_with_overrides(service_name, &serde_json::Value::Null)
}

/// Load service config from NCL with dynamic overrides validated by Nickel contracts.
/// Overrides are merged into the NCL config before export; contracts apply to the
/// merged result, so invalid overrides (e.g., port out of range) are rejected by Nickel.
pub fn load_service_config_from_ncl_with_overrides(
    service_name: &str,
    overrides: &serde_json::Value,
) -> Result<serde_json::Value> {
    let config_dir = if let Ok(home) = env::var("HOME") {
        #[cfg(target_os = "macos")]
        {
            format!(
                "{}/Library/Application Support/provisioning/platform/config",
                home
            )
        }
        #[cfg(not(target_os = "macos"))]
        {
            format!("{}/.config/provisioning/platform/config", home)
        }
    } else {
        return Err(ConfigError::not_found("Could not determine HOME directory"));
    };

    let ncl_path = format!("{}/{}.ncl", config_dir, service_name);
    let ncl_file = PathBuf::from(&ncl_path);

    if !ncl_file.exists() {
        return Err(ConfigError::not_found(format!(
            "{}.ncl not found in platform config directory",
            service_name
        )));
    }

    let json_str = nickel::export_with_overrides(&ncl_path, overrides)?;
    serde_json::from_str(&json_str).map_err(|e| {
        ConfigError::deserialization_failed(format!(
            "Failed to parse {} config as JSON: {}",
            service_name, e
        ))
    })
}
