//! NATS connection layer.
//!
//! Connection parameters follow the production-proven Vigilance profile:
//! infinite reconnects with capped exponential backoff, 30s pings to detect
//! zombie TCP in ~60s, and a deep client-side send queue so telemetry buffers
//! through broker outages instead of erroring.

use anyhow::{Context, Result};
use std::time::Duration;

use crate::config::Settings;

pub async fn connect(cfg: &Settings) -> Result<async_nats::Client> {
    let (url, force_tls) = normalize_url(&cfg.nats_url);

    let mut opts = async_nats::ConnectOptions::new()
        .name("corrosion-host-agent")
        .retry_on_initial_connect()
        .max_reconnects(None)
        .ping_interval(Duration::from_secs(30))
        .client_capacity(8192)
        .reconnect_delay_callback(|attempts| {
            Duration::from_millis(std::cmp::min(attempts as u64 * 100, 8_000))
        })
        .event_callback(|event| async move {
            match event {
                async_nats::Event::Disconnected => tracing::warn!("nats disconnected"),
                async_nats::Event::Connected => tracing::info!("nats connected"),
                other => tracing::debug!("nats event: {other}"),
            }
        });

    if force_tls {
        opts = opts.require_tls(true);
    }

    // Per-license auth: the broker maps user=license_id, password=derived
    // token to permissions scoped to corrosion.{license_id}.>. Falls back to
    // token-only or anonymous so the agent still works against a broker that
    // hasn't enforced auth yet (transition period).
    if let Some(password) = &cfg.nats_password {
        let user = cfg.nats_user.clone().unwrap_or_else(|| cfg.license_id.clone());
        opts = opts.user_and_password(user, password.clone());
    } else if let Some(token) = &cfg.nats_token {
        opts = opts.token(token.clone());
    }

    let client = opts
        .connect(&url)
        .await
        .with_context(|| format!("connecting to NATS at {url}"))?;

    Ok(client)
}

/// Accept `tls://` / `nats+tls://` URL schemes by translating to `nats://` +
/// an explicit TLS requirement.
fn normalize_url(raw: &str) -> (String, bool) {
    if let Some(rest) = raw.strip_prefix("tls://") {
        (format!("nats://{rest}"), true)
    } else if let Some(rest) = raw.strip_prefix("nats+tls://") {
        (format!("nats://{rest}"), true)
    } else {
        (raw.to_string(), false)
    }
}
