//! etcd connection utilities and key helpers.

use std::{future, time::Duration};

use etcd_client::Client;
use tokio::{sync::broadcast, time};
use tracing::{error, info};

/// Connects to etcd with exponential backoff retry.
///
/// Attempts connection immediately on first call, then applies exponential
/// backoff (1s → 2s → 4s → ... → 30s max) on failures.
///
/// Returns `None` if shutdown signal is received during retry.
pub async fn connect_with_retry(
    etcd_endpoints: &[String],
    mut shutdown: Option<broadcast::Receiver<()>>,
) -> Option<Client> {
    match Client::connect(etcd_endpoints, None).await {
        Ok(client) => {
            info!("Connected to etcd");
            return Some(client);
        }
        Err(e) => {
            error!("Initial etcd connection failed: {}", e);
        }
    }

    // Apply exponential backoff for retries
    // Track actual retry count separately from backoff delay
    let mut backoff = 1u64;
    let mut retries = 0u32;

    loop {
        // Check for shutdown before sleeping
        if let Some(ref mut rx) = shutdown {
            if rx.try_recv().is_ok() {
                info!("Shutdown signal received during etcd connection retry");
                return None;
            }
        }

        // Create shutdown future (if provided) or never-resolving future
        let shutdown_future = async {
            if let Some(ref mut rx) = shutdown {
                rx.recv().await.ok()
            } else {
                // Never resolves
                future::pending().await
            }
        };

        tokio::select! {
            _ = shutdown_future => {
                info!("Shutdown signal received during etcd connection retry");
                return None;
            }
            _ = time::sleep(Duration::from_secs(backoff)) => {
                retries += 1;
                match Client::connect(etcd_endpoints, None).await {
                    Ok(client) => {
                        info!("Reconnected to etcd after {} retries", retries);
                        return Some(client);
                    }
                    Err(e) => {
                        error!(
                            "etcd connection failed (attempt {}) - next retry in {}s: {}",
                            retries + 1, // +1 because we count from initial attempt
                            std::cmp::min(backoff * 2, 30),
                            e
                        );
                        backoff = std::cmp::min(backoff * 2, 30);
                    }
                }
            }
        }
    }
}

/// Returns the etcd key prefix for node registry.
///
/// Format: `registry/{group_name}/`
#[inline]
pub fn nodes_prefix(group_name: &str) -> String {
    format!("registry/{}/", group_name)
}

/// Returns the etcd key for a specific node.
///
/// Format: `registry/{group_name}/{node_id}`
#[inline]
pub fn node_key(group_name: &str, node_id: &str) -> String {
    format!("{}{}", nodes_prefix(group_name), node_id)
}

/// Returns the etcd key prefix for leader election.
///
/// Format: `election/{group_name}`
#[inline]
pub fn election_prefix(group_name: &str) -> String {
    format!("election/{}", group_name)
}
