//! Configuration discovery and loading functions.

#[cfg(not(target_arch = "wasm32"))]
use std::path::Path;

use super::SDKConfig;
use crate::error::LastIDError;

impl SDKConfig {
    /// Auto-discover configuration from standard locations.
    ///
    /// # Native (non-WASM)
    ///
    /// Searches for TOML files in order:
    /// 1. `./lastid.toml` (current directory)
    /// 2. `~/.config/lastid/config.toml` (user config)
    /// 3. `/etc/lastid/config.toml` (system config, Unix only)
    ///
    /// Environment variables override discovered values.
    ///
    /// # WASM
    ///
    /// Checks `localStorage` for key `lastid-sdk-config` (TOML format).
    /// Falls back to requiring explicit configuration via builder.
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::Config` if no configuration is found or parsing
    /// fails.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn discover() -> Result<Self, LastIDError> {
        // Check standard paths
        let paths = [
            Some("./lastid.toml".into()),
            dirs::config_dir().map(|p| p.join("lastid/config.toml")),
            #[cfg(unix)]
            Some("/etc/lastid/config.toml".into()),
            #[cfg(not(unix))]
            None,
        ];

        for path in paths.into_iter().flatten() {
            if path.exists() {
                let mut config = Self::from_file(&path)?;
                config.apply_env_overrides();
                return Ok(config);
            }
        }

        // No file found - try environment variables only
        let mut config = Self::default();
        config.apply_env_overrides();

        if config.idp_endpoint.is_empty() {
            return Err(LastIDError::config(
                "No configuration found. Searched paths:\n  \
                 - ./lastid.toml\n  \
                 - ~/.config/lastid/config.toml\n  \
                 - /etc/lastid/config.toml (Unix only)\n\n\
                 To fix, either:\n  \
                 1. Create a lastid.toml file with: idp_endpoint = \"https://human.lastid.co\"\n  \
                 2. Set environment variable: export LASTID_ENDPOINT=https://human.lastid.co",
            ));
        }

        Ok(config)
    }

    /// Auto-discover configuration in WASM environment.
    ///
    /// Checks `localStorage` for key `lastid-sdk-config` (TOML format).
    /// Falls back to requiring explicit configuration via builder.
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::Config` if no configuration is found or parsing
    /// fails.
    #[cfg(target_arch = "wasm32")]
    pub fn discover() -> Result<Self, LastIDError> {
        // Try to load from localStorage
        if let Some(config) = Self::from_local_storage()? {
            return Ok(config);
        }

        Err(LastIDError::config(
            "No configuration found. In WASM, use ClientBuilder::with_config() \
             or store TOML config in localStorage key 'lastid-sdk-config'.",
        ))
    }

    /// Load configuration from a TOML file.
    ///
    /// # Note
    ///
    /// This method is only available on native platforms (not WASM).
    /// In WASM, use `from_local_storage()` or [`from_toml`](Self::from_toml)
    /// instead.
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::Config` if the file cannot be read or parsed.
    #[cfg(not(target_arch = "wasm32"))]
    pub fn from_file(path: &Path) -> Result<Self, LastIDError> {
        let content = std::fs::read_to_string(path).map_err(|e| {
            LastIDError::config(format!(
                "Failed to read config file {}: {}",
                path.display(),
                e
            ))
        })?;

        Self::from_toml(&content)
    }

    /// Load configuration from browser `localStorage`.
    ///
    /// Looks for TOML configuration in the `lastid-sdk-config` key.
    ///
    /// # Example (JavaScript)
    ///
    /// ```javascript
    /// localStorage.setItem('lastid-sdk-config', `
    ///   idp_endpoint = "https://human.lastid.co"
    ///   timeout_seconds = 30
    /// `);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::Config` if localStorage is unavailable or parsing
    /// fails.
    #[cfg(target_arch = "wasm32")]
    pub fn from_local_storage() -> Result<Option<Self>, LastIDError> {
        use gloo_storage::{LocalStorage, Storage};

        const CONFIG_KEY: &str = "lastid-sdk-config";

        match LocalStorage::get::<String>(CONFIG_KEY) {
            Ok(toml_content) => {
                let config = Self::from_toml(&toml_content)?;
                Ok(Some(config))
            }
            Err(gloo_storage::errors::StorageError::KeyNotFound(_)) => Ok(None),
            Err(e) => Err(LastIDError::config(format!(
                "Failed to read from localStorage: {e}"
            ))),
        }
    }

    /// Save configuration to browser `localStorage`.
    ///
    /// Stores TOML configuration in the `lastid-sdk-config` key.
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::Config` if localStorage is unavailable or
    /// serialization fails.
    #[cfg(target_arch = "wasm32")]
    pub fn to_local_storage(&self) -> Result<(), LastIDError> {
        use gloo_storage::{LocalStorage, Storage};

        const CONFIG_KEY: &str = "lastid-sdk-config";

        let toml_content = toml::to_string_pretty(self)
            .map_err(|e| LastIDError::config(format!("Failed to serialize config: {e}")))?;

        LocalStorage::set(CONFIG_KEY, toml_content)
            .map_err(|e| LastIDError::config(format!("Failed to write to localStorage: {e}")))
    }

    /// Clear configuration from browser `localStorage`.
    #[cfg(target_arch = "wasm32")]
    pub fn clear_local_storage() {
        use gloo_storage::{LocalStorage, Storage};

        const CONFIG_KEY: &str = "lastid-sdk-config";
        LocalStorage::delete(CONFIG_KEY);
    }

    /// Parse configuration from a TOML string.
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::TomlParse` if parsing fails.
    pub fn from_toml(content: &str) -> Result<Self, LastIDError> {
        toml::from_str(content).map_err(LastIDError::from)
    }

    /// Parse configuration from a TOML string with environment variable
    /// interpolation.
    ///
    /// Supports `${VAR_NAME}` syntax in string values. Variables that don't
    /// exist are replaced with empty strings.
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// use lastid_sdk::config::SDKConfig;
    ///
    /// // In 2024 edition, set_var requires unsafe
    /// unsafe { std::env::set_var("MY_ENDPOINT", "https://test.lastid.co"); }
    ///
    /// let toml = r#"
    ///     idp_endpoint = "${MY_ENDPOINT}"
    /// "#;
    ///
    /// let config = SDKConfig::from_toml_with_env(toml).unwrap();
    /// assert_eq!(config.idp_endpoint, "https://test.lastid.co");
    /// ```
    ///
    /// # Errors
    ///
    /// Returns `LastIDError::TomlParse` if parsing fails after interpolation.
    pub fn from_toml_with_env(content: &str) -> Result<Self, LastIDError> {
        let interpolated = Self::interpolate_env_vars(content);
        Self::from_toml(&interpolated)
    }

    /// Interpolate environment variables in a string.
    ///
    /// Replaces `${VAR_NAME}` with the value of the environment variable.
    /// Unknown variables are replaced with empty strings.
    pub(crate) fn interpolate_env_vars(content: &str) -> String {
        let re = regex::Regex::new(r"\$\{([^}]+)\}").expect("valid regex");
        re.replace_all(content, |caps: &regex::Captures| {
            let var_name = &caps[1];
            std::env::var(var_name).unwrap_or_default()
        })
        .into_owned()
    }

    /// Apply environment variable overrides.
    ///
    /// Environment variables take precedence over file configuration.
    pub fn apply_env_overrides(&mut self) {
        if let Ok(endpoint) = std::env::var("LASTID_ENDPOINT") {
            self.idp_endpoint = endpoint;
        }

        if let Ok(client_id) = std::env::var("LASTID_CLIENT_ID") {
            self.client_id = client_id;
        }

        if let Ok(endpoint) = std::env::var("LASTID_TRUST_REGISTRY_ENDPOINT") {
            self.trust_registry_endpoint = Some(endpoint);
        }

        if let Ok(timeout) = std::env::var("LASTID_TIMEOUT")
            && let Ok(secs) = timeout.parse()
        {
            self.timeout_seconds = secs;
        }

        if let Ok(timeout) = std::env::var("LASTID_POLLING_TIMEOUT")
            && let Ok(secs) = timeout.parse()
        {
            self.polling.max_duration_seconds = secs;
        }

        if let Ok(attempts) = std::env::var("LASTID_RETRY_MAX_ATTEMPTS")
            && let Ok(n) = attempts.parse()
        {
            self.retry.max_attempts = n;
        }

        if let Ok(enabled) = std::env::var("LASTID_CACHE_ENABLED") {
            self.cache.enabled = enabled.parse().unwrap_or(true);
        }

        if let Ok(ttl) = std::env::var("LASTID_CACHE_TTL")
            && let Ok(secs) = ttl.parse()
        {
            self.cache.ttl_seconds = secs;
        }
    }
}
