//! Authenticated-call helper: run an API call with the saved access token,
//! transparently rotating it once on a 401 so an active session never forces a
//! re-login.

use crate::{api, config};

/// The saved API base URL + a usable access token.
pub fn load() -> anyhow::Result<(config::Config, String, String)> {
    let config = config::load()?;
    let api_url = config.api_url();
    let token = config
        .token
        .clone()
        .ok_or_else(|| anyhow::anyhow!("not logged in; run `sprouts login`"))?;
    Ok((config, api_url, token))
}

/// Call an API function with the saved token, refreshing once on `Unauthorized`.
pub fn authed<T>(
    call: impl Fn(&str, &str) -> Result<T, api::Error>,
) -> anyhow::Result<T> {
    let (mut config, api_url, token) = load()?;
    match call(&api_url, &token) {
        Ok(value) => Ok(value),
        Err(api::Error::Unauthorized) => {
            let refresh = config
                .refresh_token
                .clone()
                .ok_or_else(|| anyhow::anyhow!("session expired; run `sprouts login`"))?;
            let session = api::refresh(&api_url, &refresh)
                .map_err(|_| anyhow::anyhow!("session expired; run `sprouts login`"))?;
            config.token = Some(session.token.clone());
            config.refresh_token = Some(session.refresh_token);
            config.user_id = Some(session.user.id.clone());
            config.email = session.user.email.clone();
            config::save(&config)?;
            call(&api_url, &session.token).map_err(|e| anyhow::anyhow!("{e}"))
        }
        Err(e) => Err(anyhow::anyhow!("{e}")),
    }
}

/// Resolve a sprout id: an explicit `--id`, else the sprout named in the local
/// `sprout.toml`.
pub fn resolve_sprout_id(explicit: Option<String>, dir: &std::path::Path) -> anyhow::Result<String> {
    if let Some(id) = explicit {
        return Ok(id);
    }
    let name = crate::commands::deploy::manifest_name(dir)?;
    let sprouts = authed(api::list_sprouts)?;
    sprouts
        .into_iter()
        .find(|s| s.name == name)
        .map(|s| s.id)
        .ok_or_else(|| anyhow::anyhow!("no deployed sprout named `{name}`; run `sprouts deploy` first"))
}
