/// SecretsResolver — loads all required secrets at startup.
///
/// The resolver validates that all required secrets are present before the
/// application starts. If any are missing, startup fails with a clear error
/// listing what's missing — not a runtime panic deep in the app.

use std::collections::HashMap;
use crate::error::SecretsError;
use crate::secret::Secret;
use crate::source::SecretsSource;

/// A resolved, validated set of secrets.
///
/// Created by SecretsResolver at startup. Once resolved, all secrets are
/// available and guaranteed to have been present at startup time.
pub struct ResolvedSecrets {
    values: HashMap<String, Secret<String>>,
}

impl ResolvedSecrets {
    /// Get a secret by key.
    ///
    /// Returns None if the key wasn't declared as required.
    /// In normal use you will always have the keys you declared.
    pub fn get(&self, key: &str) -> Option<&Secret<String>> {
        self.values.get(key)
    }

    /// Get a secret or panic with a clear message.
    ///
    /// Use this for secrets that are truly required and were declared in
    /// the resolver — if the resolver passed, this will always succeed.
    pub fn require(&self, key: &str) -> &Secret<String> {
        self.values.get(key).unwrap_or_else(|| {
            panic!(
                "Secret '{}' was not declared as required in SecretsResolver. \
                 Declare all required secrets before calling resolve().",
                key
            )
        })
    }

    /// The number of resolved secrets.
    pub fn len(&self) -> usize {
        self.values.len()
    }

    pub fn is_empty(&self) -> bool {
        self.values.is_empty()
    }
}

/// Loads and validates all required secrets at startup.
///
/// Usage:
/// ```ignore
/// let secrets = SecretsResolver::new()
///     .source(EnvVarSource::new())
///     .require("jwt.secret_key")
///     .require("database.password")
///     .resolve()?;
///
/// let jwt_key = secrets.require("jwt.secret_key");
/// ```
pub struct SecretsResolver {
    sources: Vec<Box<dyn SecretsSource>>,
    required: Vec<String>,
}

impl SecretsResolver {
    pub fn new() -> Self {
        Self {
            sources: Vec::new(),
            required: Vec::new(),
        }
    }

    /// Add a secret source. Sources are tried in order; first success wins.
    pub fn source(mut self, source: Box<dyn SecretsSource>) -> Self {
        self.sources.push(source);
        self
    }

    /// Declare a required secret key.
    ///
    /// All required keys must be present in at least one source.
    /// resolve() fails if any are missing.
    pub fn require(mut self, key: impl Into<String>) -> Self {
        self.required.push(key.into());
        self
    }

    /// Declare multiple required secret keys.
    pub fn require_all(mut self, keys: &[&str]) -> Self {
        for key in keys {
            self.required.push(key.to_string());
        }
        self
    }

    /// Load all required secrets and validate they are all present.
    ///
    /// Returns an error listing ALL missing secrets — not just the first one —
    /// so you can fix them all in one go.
    pub fn resolve(self) -> Result<ResolvedSecrets, SecretsError> {
        let mut values = HashMap::new();
        let mut missing = Vec::new();

        for key in &self.required {
            match self.fetch_from_sources(key) {
                Ok(secret) => {
                    values.insert(key.clone(), secret);
                }
                Err(_) => {
                    missing.push(key.clone());
                }
            }
        }

        if !missing.is_empty() {
            return Err(SecretsError::MissingRequired { keys: missing });
        }

        Ok(ResolvedSecrets { values })
    }

    /// Attempt to fetch a key from sources in order.
    fn fetch_from_sources(&self, key: &str) -> Result<Secret<String>, SecretsError> {
        for source in &self.sources {
            if let Ok(secret) = source.get(key) {
                return Ok(secret);
            }
        }
        Err(SecretsError::NotFoundInAnySource {
            key: key.to_string(),
        })
    }

    /// Resolve without requiring all keys — useful when you want
    /// to load whatever is available.
    pub fn resolve_optional(self) -> ResolvedSecrets {
        let mut values = HashMap::new();
        for key in &self.required {
            if let Ok(secret) = self.fetch_from_sources(key) {
                values.insert(key.clone(), secret);
            }
        }
        ResolvedSecrets { values }
    }
}

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

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

    fn make_source(pairs: &[(&str, &str)]) -> InMemorySource {
        let mut src = InMemorySource::new();
        for (k, v) in pairs {
            src.insert(*k, *v);
        }
        src
    }

    #[test]
    fn resolve_all_present() {
        let src = make_source(&[("jwt.key", "secret"), ("db.password", "pass123")]);
        let secrets = SecretsResolver::new()
            .source(Box::new(src))
            .require("jwt.key")
            .require("db.password")
            .resolve()
            .unwrap();

        assert_eq!(secrets.require("jwt.key").expose(), "secret");
        assert_eq!(secrets.require("db.password").expose(), "pass123");
    }

    #[test]
    fn resolve_missing_fails_with_list() {
        let src = make_source(&[("jwt.key", "secret")]);
        let result = SecretsResolver::new()
            .source(Box::new(src))
            .require("jwt.key")
            .require("db.password") // missing
            .require("stripe.key")  // also missing
            .resolve();

        match result {
            Err(SecretsError::MissingRequired { keys }) => {
                assert!(keys.contains(&"db.password".to_string()));
                assert!(keys.contains(&"stripe.key".to_string()));
                assert_eq!(keys.len(), 2);
            }
            _ => panic!("expected MissingRequired error"),
        }
    }

    #[test]
    fn resolve_optional_skips_missing() {
        let src = make_source(&[("jwt.key", "secret")]);
        let secrets = SecretsResolver::new()
            .source(Box::new(src))
            .require("jwt.key")
            .require("missing.key")
            .resolve_optional();

        assert!(secrets.get("jwt.key").is_some());
        assert!(secrets.get("missing.key").is_none());
    }

    #[test]
    fn multiple_sources_fallback() {
        let mut primary = InMemorySource::new();
        primary.insert("jwt.key", "from-primary");
        let mut secondary = InMemorySource::new();
        secondary.insert("db.password", "from-secondary");

        let secrets = SecretsResolver::new()
            .source(Box::new(primary))
            .source(Box::new(secondary))
            .require("jwt.key")
            .require("db.password")
            .resolve()
            .unwrap();

        assert_eq!(secrets.require("jwt.key").expose(), "from-primary");
        assert_eq!(secrets.require("db.password").expose(), "from-secondary");
    }

    #[test]
    fn first_source_wins() {
        let mut s1 = InMemorySource::new();
        s1.insert("key", "value-1");
        let mut s2 = InMemorySource::new();
        s2.insert("key", "value-2");

        let secrets = SecretsResolver::new()
            .source(Box::new(s1))
            .source(Box::new(s2))
            .require("key")
            .resolve()
            .unwrap();

        assert_eq!(secrets.require("key").expose(), "value-1");
    }

    #[test]
    fn require_all() {
        let src = make_source(&[("a", "1"), ("b", "2"), ("c", "3")]);
        let secrets = SecretsResolver::new()
            .source(Box::new(src))
            .require_all(&["a", "b", "c"])
            .resolve()
            .unwrap();

        assert_eq!(secrets.len(), 3);
    }

    #[test]
    fn resolved_len() {
        let src = make_source(&[("k", "v")]);
        let secrets = SecretsResolver::new()
            .source(Box::new(src))
            .require("k")
            .resolve()
            .unwrap();
        assert_eq!(secrets.len(), 1);
    }

    #[test]
    fn resolved_is_empty() {
        let resolved = ResolvedSecrets { values: HashMap::new() };
        assert!(resolved.is_empty());
    }
}
