use crate::db::Database;
use std::path::Path;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, SystemTime};

/// Counter for tracking credential refresh attempts
static REFRESH_COUNT: AtomicU64 = AtomicU64::new(0);

const VAULT_SECRETS_PATH: &str = "/vault/secrets/.env";
const FILE_POLL_INTERVAL: Duration = Duration::from_secs(10);

/// Perform the credential refresh
async fn do_refresh(db: &Database, source: &str) {
    let count = REFRESH_COUNT.fetch_add(1, Ordering::SeqCst) + 1;
    tracing::warn!(
        refresh_count = count,
        source = source,
        "Starting credential refresh"
    );

    let start = std::time::Instant::now();
    match db.refresh().await {
        Ok(()) => {
            let elapsed = start.elapsed();
            tracing::info!(
                refresh_count = count,
                elapsed_ms = elapsed.as_millis() as u64,
                "Credential refresh completed successfully"
            );
        }
        Err(e) => {
            let elapsed = start.elapsed();
            tracing::error!(
                refresh_count = count,
                elapsed_ms = elapsed.as_millis() as u64,
                error = %e,
                "Failed to refresh credentials - DATABASE MAY BE STALE"
            );
        }
    }
}

#[cfg(unix)]
pub async fn handle_sighup(db: Database) {
    use tokio::signal::unix::{signal, SignalKind};

    let mut stream = signal(SignalKind::hangup())
        .expect("Failed to create SIGHUP handler");

    let pid = std::process::id();
    tracing::info!(
        pid = pid,
        "SIGHUP handler registered for credential refresh"
    );

    while stream.recv().await.is_some() {
        do_refresh(&db, "SIGHUP").await;
    }

    tracing::error!("SIGHUP handler loop exited unexpectedly!");
}

#[cfg(not(unix))]
pub async fn handle_sighup(_db: Database) {
    tracing::warn!("SIGHUP handling not supported on this platform");
}

/// Watch the secrets file for changes and trigger refresh
/// This is a fallback for when SIGHUP signaling doesn't work
pub async fn watch_secrets_file(db: Database) {
    let path = Path::new(VAULT_SECRETS_PATH);

    // Get initial mtime
    let mut last_mtime = get_mtime(path);

    tracing::info!(
        path = VAULT_SECRETS_PATH,
        poll_interval_secs = FILE_POLL_INTERVAL.as_secs(),
        "File watcher started for credential refresh"
    );

    loop {
        tokio::time::sleep(FILE_POLL_INTERVAL).await;

        let current_mtime = get_mtime(path);

        if current_mtime != last_mtime {
            tracing::info!(
                path = VAULT_SECRETS_PATH,
                "Secrets file changed, triggering refresh"
            );
            last_mtime = current_mtime;
            do_refresh(&db, "file_watch").await;
        }
    }
}

fn get_mtime(path: &Path) -> Option<SystemTime> {
    path.metadata().ok().and_then(|m| m.modified().ok())
}
