use std::time::Duration;

use crate::AppConfig;

impl AppConfig {
    pub fn cache_local_max_capacity(&self) -> anyhow::Result<u64> {
        self.parse_env("APP_CACHE_LOCAL_MAX_CAPACITY", 10_000)
    }

    pub fn cache_local_ttl(&self) -> anyhow::Result<Option<Duration>> {
        self.parse_optional_duration_secs(
            "APP_CACHE_LOCAL_TTL_SECONDS",
            Some(300),
        )
    }

    pub fn cache_local_tti(&self) -> anyhow::Result<Option<Duration>> {
        self.parse_optional_duration_secs("APP_CACHE_LOCAL_TTI_SECONDS", None)
    }

    pub fn cache_default_ttl(&self) -> anyhow::Result<Option<Duration>> {
        self.parse_optional_duration_secs(
            "APP_CACHE_DEFAULT_TTL_SECONDS",
            Some(300),
        )
    }

    pub fn cache_cluster_enabled(&self) -> anyhow::Result<bool> {
        self.parse_env("APP_CACHE_CLUSTER_ENABLED", false)
    }

    pub fn cache_cluster_key_prefix(&self) -> Option<String> {
        self.env
            .get("APP_CACHE_CLUSTER_KEY_PREFIX")
            .map(|value| value.trim().to_string())
            .filter(|value| !value.is_empty())
    }

    pub fn cache_cluster_command_timeout(&self) -> anyhow::Result<Duration> {
        self.parse_duration_secs("APP_CACHE_CLUSTER_COMMAND_TIMEOUT_SECONDS", 3)
    }

    pub fn cache_cluster_write_through(&self) -> anyhow::Result<bool> {
        self.parse_env("APP_CACHE_CLUSTER_WRITE_THROUGH", true)
    }

    pub fn parse_env<T>(&self, key: &str, default: T) -> anyhow::Result<T>
    where
        T: std::str::FromStr,
        T::Err: std::error::Error + Send + Sync + 'static,
    {
        match self.env.get(key).map(|value| value.trim()) {
            Some(value) if !value.is_empty() => Ok(value.parse::<T>()?),
            _ => Ok(default),
        }
    }

    pub fn parse_duration_secs(
        &self,
        key: &str,
        default_secs: u64,
    ) -> anyhow::Result<Duration> {
        Ok(Duration::from_secs(self.parse_env(key, default_secs)?))
    }

    pub fn parse_optional_duration_secs(
        &self,
        key: &str,
        default_secs: Option<u64>,
    ) -> anyhow::Result<Option<Duration>> {
        match self.env.get(key).map(|value| value.trim()) {
            Some("0") => Ok(None),
            Some(value) if !value.is_empty() => {
                Ok(Some(Duration::from_secs(value.parse()?)))
            }
            _ => Ok(default_secs.map(Duration::from_secs)),
        }
    }
}
