//! Application-wide server state available in both Axum handlers and
//! Dioxus server functions via `extract()`.
//!
//! ```rust,ignore
//! // Inside a #[server] function:
//! let state: ServerState = extract().await?;
//! ```

use std::{ops::Deref, sync::Arc};

use super::{
    config::{KeycloakConfig, LlmProvidersConfig, ServiceUrls, SmtpConfig, StripeConfig},
    database::Database,
    Error,
};

/// Cheap-to-clone handle to the shared server state.
///
/// Stored as an Axum `Extension` so it is accessible from both
/// route handlers and Dioxus `#[server]` functions.
#[derive(Clone)]
pub struct ServerState(Arc<ServerStateInner>);

impl Deref for ServerState {
    type Target = ServerStateInner;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<ServerStateInner> for ServerState {
    fn from(value: ServerStateInner) -> Self {
        Self(Arc::new(value))
    }
}

/// Inner struct holding all long-lived application resources.
///
/// Config references are `&'static` because they are `Box::leak`ed
/// at startup -- they never change at runtime.
pub struct ServerStateInner {
    /// MongoDB connection pool.
    pub db: Database,
    /// Keycloak / OAuth2 settings.
    pub keycloak: &'static KeycloakConfig,
    /// Outbound email settings.
    pub smtp: &'static SmtpConfig,
    /// URLs for LiteLLM, SearXNG, LangChain, S3, etc.
    pub services: &'static ServiceUrls,
    /// Stripe billing keys.
    pub stripe: &'static StripeConfig,
    /// Enabled LLM provider list.
    pub llm_providers: &'static LlmProvidersConfig,
}

// `FromRequestParts` lets us `extract::<ServerState>()` inside
// Dioxus server functions and regular Axum handlers alike.
impl<S> axum::extract::FromRequestParts<S> for ServerState
where
    S: Send + Sync,
{
    type Rejection = Error;

    async fn from_request_parts(
        parts: &mut axum::http::request::Parts,
        _state: &S,
    ) -> Result<Self, Self::Rejection> {
        parts
            .extensions
            .get::<ServerState>()
            .cloned()
            .ok_or(Error::StateError("ServerState extension not found".into()))
    }
}
