//! Phase 3 Stream C entrypoint: drain `hrrr_fetch_tasks`.
//!
//! Claims one task at a time, fetches the HRRR cycle once, extracts each
//! requested point, and upserts into `hrrr_profiles`. FOR UPDATE SKIP
//! LOCKED allows N replicas with no coordination.

use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};

use prop_grid_rs::{db, fetcher::HrrrClient, hrrr_points, telemetry};
use tracing::{error, info, warn};

const IDLE_SLEEP: Duration = Duration::from_secs(10);

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _telemetry = telemetry::init("hrrr-point-worker");

    let database_url = std::env::var("DATABASE_URL")
        .map_err(|_| "DATABASE_URL environment variable is required")?;
    let hrrr_base = std::env::var("HRRR_BASE_URL")
        .unwrap_or_else(|_| prop_grid_rs::fetcher::HRRR_BASE_DEFAULT.to_string());
    let max_conn: u32 = std::env::var("PROP_GRID_RS_PG_CONNS")
        .ok()
        .and_then(|v| v.parse().ok())
        .unwrap_or(4);
    let tmp_dir = PathBuf::from(std::env::var("TMP_DIR").unwrap_or_else(|_| "/tmp".to_string()));

    let pool = db::connect(&database_url, max_conn).await?;
    let client = Arc::new(HrrrClient::new(hrrr_base)?);

    let shutdown = Arc::new(AtomicBool::new(false));
    spawn_signal_handler(shutdown.clone());

    info!(pg_conns = max_conn, "hrrr-point-worker started");

    while !shutdown.load(Ordering::Relaxed) {
        match db::claim_next_hrrr_task(&pool).await {
            Ok(Some(task)) => {
                let task_id = task.id;
                let valid_time = task.valid_time;
                let points = task.points.clone();
                let started = Instant::now();

                match hrrr_points::process_batch(&client, &pool, valid_time, &points, &tmp_dir)
                    .await
                {
                    Ok(stats) => {
                        let elapsed = started.elapsed();
                        info!(
                            %task_id,
                            valid_time = %valid_time,
                            points_requested = stats.points_requested,
                            profiles_inserted = stats.profiles_inserted,
                            duration_s = elapsed.as_secs_f64(),
                            "hrrr batch done"
                        );
                        if let Err(e) = db::complete_hrrr_task(&pool, task_id).await {
                            error!(%task_id, error = %e, "failed to mark task done");
                        }
                    }
                    Err(e) => {
                        warn!(%task_id, error = %e, "hrrr batch failed");
                        if let Err(dbe) = db::fail_hrrr_task(&pool, task_id, &e.to_string()).await {
                            error!(%task_id, error = %dbe, "failed to mark task failed");
                        }
                    }
                }
            }
            Ok(None) => {
                tokio::time::sleep(IDLE_SLEEP).await;
            }
            Err(e) => {
                error!(error = %e, "claim_next_hrrr_task failed; backing off");
                tokio::time::sleep(IDLE_SLEEP).await;
            }
        }
    }

    info!("hrrr-point-worker shutting down");
    Ok(())
}

#[cfg(unix)]
fn spawn_signal_handler(shutdown: Arc<AtomicBool>) {
    use tokio::signal::unix::{signal, SignalKind};
    tokio::spawn(async move {
        let mut term = signal(SignalKind::terminate()).expect("install SIGTERM handler");
        let mut intr = signal(SignalKind::interrupt()).expect("install SIGINT handler");
        tokio::select! {
            _ = term.recv() => info!("SIGTERM received"),
            _ = intr.recv() => info!("SIGINT received"),
        };
        shutdown.store(true, Ordering::Relaxed);
    });
}

#[cfg(not(unix))]
fn spawn_signal_handler(shutdown: Arc<AtomicBool>) {
    tokio::spawn(async move {
        let _ = tokio::signal::ctrl_c().await;
        shutdown.store(true, Ordering::Relaxed);
    });
}
