//! Core `AgentIdPClient` — entry point for all SDK operations.
//!
//! Create a client via [`AgentIdPClient::new`] or [`AgentIdPClient::from_env`],
//! then call methods that correspond to each API endpoint. The client manages
//! token acquisition transparently through the embedded [`TokenManager`].

use std::env;
use std::sync::Arc;

use tokio::sync::Mutex;

use crate::error::AgentIdPError;
use crate::token_manager::TokenManager;

/// The top-level client for the SentryAgent.ai AgentIdP API.
///
/// All methods are `async` and require a `tokio` runtime. The client is
/// cheap to clone — the underlying HTTP connection pool and token cache are
/// shared via `Arc`.
///
/// # Example
///
/// ```rust,no_run
/// use sentryagent_idp::AgentIdPClient;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let client = AgentIdPClient::from_env()?;
///     let agents = client.list_agents(Some(1), Some(20)).await?;
///     println!("Total agents: {}", agents.total);
///     Ok(())
/// }
/// ```
pub struct AgentIdPClient {
    /// Base URL of the AgentIdP API (no trailing slash).
    pub(crate) base_url: String,
    /// Reusable `reqwest` HTTP client — created once, shared across all requests.
    pub(crate) http: reqwest::Client,
    /// Shared, async-safe token manager.
    pub(crate) token_manager: Arc<Mutex<TokenManager>>,
}

impl AgentIdPClient {
    /// Creates a new `AgentIdPClient`. No network calls are made at construction time.
    ///
    /// # Arguments
    ///
    /// * `base_url` — Root URL of the AgentIdP API, e.g. `"https://api.sentryagent.ai"`.
    /// * `client_id` — OAuth 2.0 client identifier.
    /// * `client_secret` — OAuth 2.0 client secret.
    pub fn new(base_url: &str, client_id: &str, client_secret: &str) -> Self {
        let clean_url = base_url.trim_end_matches('/').to_owned();
        let tm = TokenManager::new(&clean_url, client_id, client_secret);
        Self {
            base_url: clean_url,
            http: reqwest::Client::new(),
            token_manager: Arc::new(Mutex::new(tm)),
        }
    }

    /// Creates a client from environment variables.
    ///
    /// Reads the following variables:
    ///
    /// | Variable | Purpose |
    /// |---|---|
    /// | `AGENTIDP_API_URL` | Base URL of the API |
    /// | `AGENTIDP_CLIENT_ID` | OAuth 2.0 client ID |
    /// | `AGENTIDP_CLIENT_SECRET` | OAuth 2.0 client secret |
    ///
    /// Returns [`AgentIdPError::ConfigError`] if any variable is missing.
    pub fn from_env() -> Result<Self, AgentIdPError> {
        let api_url = env::var("AGENTIDP_API_URL").map_err(|_| {
            AgentIdPError::ConfigError(
                "AGENTIDP_API_URL environment variable is not set".to_owned(),
            )
        })?;
        let client_id = env::var("AGENTIDP_CLIENT_ID").map_err(|_| {
            AgentIdPError::ConfigError(
                "AGENTIDP_CLIENT_ID environment variable is not set".to_owned(),
            )
        })?;
        let client_secret = env::var("AGENTIDP_CLIENT_SECRET").map_err(|_| {
            AgentIdPError::ConfigError(
                "AGENTIDP_CLIENT_SECRET environment variable is not set".to_owned(),
            )
        })?;

        Ok(Self::new(&api_url, &client_id, &client_secret))
    }

    /// Returns a `Bearer <token>` string for the `Authorization` header.
    ///
    /// Delegates to [`TokenManager::get_token`], which handles caching and
    /// automatic refresh transparently.
    pub(crate) async fn get_auth_header(&self) -> Result<String, AgentIdPError> {
        let tm = self.token_manager.lock().await;
        let token = tm.get_token().await?;
        Ok(format!("Bearer {}", token))
    }
}
