// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;

use crate::{ConfigLayer, ConfigResolver, LayeredConfig};

/// Builder for creating a LayeredConfig with custom layers and resolver
#[derive(Debug)]
pub struct LayeredConfigBuilder {
    layers: Vec<ConfigLayer>,
    resolver: Option<ConfigResolver>,
    #[cfg(feature = "schema")]
    schema: Option<serde_json::Value>,
}

impl LayeredConfigBuilder {
    /// Create a new configuration builder
    pub fn new() -> Self {
        Self {
            layers: Vec::new(),
            resolver: None,
            #[cfg(feature = "schema")]
            schema: None,
        }
    }

    /// Add a configuration layer
    /// Layers are processed in the order they are added, with later layers having higher precedence
    pub fn with_layer(mut self, layer: ConfigLayer) -> Self {
        self.layers.push(layer);
        self
    }

    /// Add multiple layers at once
    pub fn with_layers(mut self, layers: Vec<ConfigLayer>) -> Self {
        self.layers.extend(layers);
        self
    }

    /// Set a custom resolver (optional - will use default if not provided)
    pub fn with_resolver(mut self, resolver: ConfigResolver) -> Self {
        self.resolver = Some(resolver);
        self
    }

    /// Set a JSON Schema for validation (requires "schema" feature)
    #[cfg(feature = "schema")]
    pub fn with_schema(mut self, schema: serde_json::Value) -> Self {
        self.schema = Some(schema);
        self
    }

    /// Build the LayeredConfig
    pub fn build(self) -> Result<LayeredConfig> {
        let resolver = self
            .resolver
            .unwrap_or_else(|| ConfigResolver::new(self.layers.clone()));

        // Apply schema validation if provided
        #[cfg(feature = "schema")]
        if let Some(schema) = self.schema {
            let resolver = resolver.with_schema(schema)?;
            return Ok(LayeredConfig::new(resolver));
        }

        Ok(LayeredConfig::new(resolver))
    }

    /// Convenience method to build without error handling (panics on error)
    pub fn build_unchecked(self) -> LayeredConfig {
        self.build().expect("Failed to build LayeredConfig")
    }
}

impl Default for LayeredConfigBuilder {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use super::*;
    use crate::layers::{ConfigLayerSource, InMemoryLayer};

    #[test]
    fn test_builder_basic() {
        let mut default_values = HashMap::new();
        default_values.insert("key1".to_string(), "value1".to_string());

        let layer = InMemoryLayer::new(ConfigLayerSource::Default)
            .with_values(default_values)
            .build();

        let config = LayeredConfigBuilder::new()
            .with_layer(layer)
            .build()
            .unwrap();

        let value: String = config.get("key1").unwrap();
        assert_eq!(value, "value1");
    }

    #[test]
    fn test_builder_multiple_layers() {
        let mut default_values = HashMap::new();
        default_values.insert("key1".to_string(), "default_value".to_string());
        default_values.insert("key2".to_string(), "only_in_default".to_string());

        let default_layer = InMemoryLayer::new(ConfigLayerSource::Default)
            .with_values(default_values)
            .build();

        let mut override_values = HashMap::new();
        override_values.insert("key1".to_string(), "override_value".to_string());

        let override_layer = InMemoryLayer::new(ConfigLayerSource::CommandLineArgs)
            .with_values(override_values)
            .build();

        let config = LayeredConfigBuilder::new()
            .with_layer(default_layer)
            .with_layer(override_layer)
            .build()
            .unwrap();

        // Override should win
        let value1: String = config.get("key1").unwrap();
        assert_eq!(value1, "override_value");

        // Default should be available
        let value2: String = config.get("key2").unwrap();
        assert_eq!(value2, "only_in_default");
    }
}
