use chrono::Utc;
use std::sync::Arc;
use tracing::{error, info};

use dd0c_route::AppConfig;

mod anomaly;
mod digest;

/// Refresh model pricing from known provider pricing pages.
/// Falls back to hardcoded defaults if fetch fails.
async fn refresh_pricing(pool: &sqlx::PgPool) -> anyhow::Result<()> {
    // Hardcoded pricing table — updated manually or via provider API scraping
    // Format: (provider, model, input_per_1k_tokens, output_per_1k_tokens)
    let pricing = vec![
        ("openai", "gpt-4o", 0.0025, 0.01),
        ("openai", "gpt-4o-mini", 0.00015, 0.0006),
        ("openai", "gpt-4-turbo", 0.01, 0.03),
        ("openai", "gpt-3.5-turbo", 0.0005, 0.0015),
        ("anthropic", "claude-sonnet-4-20250514", 0.003, 0.015),
        ("anthropic", "claude-3-5-haiku-20241022", 0.001, 0.005),
        ("anthropic", "claude-opus-4-20250514", 0.015, 0.075),
        ("google", "gemini-2.0-flash", 0.0001, 0.0004),
        ("google", "gemini-1.5-pro", 0.00125, 0.005),
    ];

    for (provider, model, input_cost, output_cost) in &pricing {
        sqlx::query(
            "INSERT INTO model_pricing (provider, model, input_cost_per_1k, output_cost_per_1k, updated_at)
             VALUES ($1, $2, $3, $4, now())
             ON CONFLICT (provider, model) DO UPDATE SET
               input_cost_per_1k = $3, output_cost_per_1k = $4, updated_at = now()"
        )
        .bind(provider)
        .bind(model)
        .bind(input_cost)
        .bind(output_cost)
        .execute(pool)
        .await?;
    }

    info!(models = pricing.len(), "Pricing table refreshed");
    Ok(())
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "dd0c_route=info".into()),
        )
        .json()
        .init();

    let config = Arc::new(AppConfig::from_env()?);
    info!("Starting dd0c/route worker");

    let ts_pool = sqlx::PgPool::connect(&config.timescale_url).await?;
    let pg_pool = sqlx::PgPool::connect(&config.database_url).await?;

    // Spawn periodic tasks
    let ts1 = ts_pool.clone();
    let pg1 = pg_pool.clone();
    let _config1 = config.clone();

    // Hourly: check for cost anomalies
    tokio::spawn(async move {
        let mut interval = tokio::time::interval(std::time::Duration::from_secs(3600));
        loop {
            interval.tick().await;
            if let Err(e) = anomaly::check_anomalies(&ts1, &pg1).await {
                error!(error = %e, "Anomaly check failed");
            }
        }
    });

    // Weekly: generate cost digest emails
    let ts2 = ts_pool.clone();
    let pg2 = pg_pool.clone();
    tokio::spawn(async move {
        // Run every Monday at 9 AM UTC
        loop {
            let now = Utc::now();
            let next_monday = digest::next_monday_9am(now);
            let sleep_duration = (next_monday - now)
                .to_std()
                .unwrap_or(std::time::Duration::from_secs(3600));
            tokio::time::sleep(sleep_duration).await;

            info!("Generating weekly digests");
            if let Err(e) = digest::generate_all_digests(&ts2, &pg2).await {
                error!(error = %e, "Digest generation failed");
            }
        }
    });

    // Cost table refresh: daily at midnight UTC
    let pg3 = pg_pool.clone();
    tokio::spawn(async move {
        let mut interval = tokio::time::interval(std::time::Duration::from_secs(86400));
        loop {
            interval.tick().await;
            info!("Refreshing cost tables");
            if let Err(e) = refresh_pricing(&pg3).await {
                error!(error = %e, "Pricing refresh failed");
            }
        }
    });

    info!("Worker running — anomaly checks hourly, digests weekly");

    // Keep the main task alive
    tokio::signal::ctrl_c().await?;
    info!("Worker shutting down");
    Ok(())
}
