//! Configuration source types.
//!
//! This module defines the various sources from which configuration
//! can be loaded or derived.

use std::path::PathBuf;

/// Source of configuration data.
///
/// Represents where configuration values originate from,
/// useful for debugging and change tracking.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum ConfigSource {
    /// Configuration from a file.
    File(PathBuf),

    /// Configuration from an environment variable.
    Env(String),

    /// Configuration from an in-memory value.
    Memory(serde_json::Value),

    /// Configuration from a remote source (URL, service name, etc.).
    Remote(String),

    /// Configuration from a custom source.
    Custom(String),
}

impl ConfigSource {
    /// Create a file source.
    #[must_use]
    pub const fn file(path: PathBuf) -> Self {
        Self::File(path)
    }

    /// Create an environment variable source.
    #[must_use]
    pub const fn env(var: String) -> Self {
        Self::Env(var)
    }

    /// Create a memory source from a JSON value.
    #[must_use]
    pub const fn memory(value: serde_json::Value) -> Self {
        Self::Memory(value)
    }

    /// Create a remote source.
    #[must_use]
    pub const fn remote(location: String) -> Self {
        Self::Remote(location)
    }

    /// Create a custom source.
    #[must_use]
    pub const fn custom(name: String) -> Self {
        Self::Custom(name)
    }

    /// Get a string representation of the source.
    #[must_use]
    pub fn as_str(&self) -> String {
        match self {
            Self::File(p) => format!("file:{}", p.display()),
            Self::Env(v) => format!("env:{v}"),
            Self::Memory(_) => "memory".to_string(),
            Self::Remote(r) => format!("remote:{r}"),
            Self::Custom(c) => format!("custom:{c}"),
        }
    }
}

impl From<PathBuf> for ConfigSource {
    fn from(path: PathBuf) -> Self {
        Self::File(path)
    }
}

impl From<String> for ConfigSource {
    fn from(s: String) -> Self {
        Self::Custom(s)
    }
}

impl From<serde_json::Value> for ConfigSource {
    fn from(value: serde_json::Value) -> Self {
        Self::Memory(value)
    }
}

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

    #[test]
    fn test_source_creation() {
        let file = ConfigSource::file(PathBuf::from("/path/to/config.toml"));
        assert!(matches!(file, ConfigSource::File(_)));

        let env = ConfigSource::env("MYAPP_PORT".to_string());
        assert!(matches!(env, ConfigSource::Env(_)));

        let memory = ConfigSource::memory(serde_json::json!({"key": "value"}));
        assert!(matches!(memory, ConfigSource::Memory(_)));
    }

    #[test]
    fn test_source_as_str() {
        let source = ConfigSource::file(PathBuf::from("/path/to/config.toml"));
        assert!(source.as_str().starts_with("file:"));

        let source = ConfigSource::env("PORT".to_string());
        assert_eq!(source.as_str(), "env:PORT");
    }

    #[test]
    fn test_source_from() {
        let path: PathBuf = "/test/path".into();
        let source = ConfigSource::from(path);
        assert!(matches!(source, ConfigSource::File(_)));

        let value = serde_json::json!({"test": true});
        let source = ConfigSource::from(value);
        assert!(matches!(source, ConfigSource::Memory(_)));
    }
}
