use std::sync::Arc;
use tokio::sync::mpsc;
use tracing::info;

use dd0c_route::{
    AppConfig, LocalAuthProvider, RouterBrain, ProxyState, TelemetryEvent, create_router,
};

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

    // Load config
    let config = Arc::new(AppConfig::from_env()?);
    info!(port = config.proxy_port, "Starting dd0c/route proxy");

    // Connect to databases
    let pg_pool = sqlx::PgPool::connect(&config.database_url).await?;
    let redis_cfg = deadpool_redis::Config::from_url(&config.redis_url);
    let redis_pool = redis_cfg.create_pool(Some(deadpool_redis::Runtime::Tokio1))?;

    // Telemetry channel (bounded, non-blocking)
    let (telemetry_tx, mut telemetry_rx) = mpsc::channel::<TelemetryEvent>(config.telemetry_channel_size);

    // Spawn telemetry worker (writes to TimescaleDB)
    let ts_url = config.timescale_url.clone();
    tokio::spawn(async move {
        let ts_pool = sqlx::PgPool::connect(&ts_url).await.expect("TimescaleDB connection failed");
        while let Some(event) = telemetry_rx.recv().await {
            if let Err(e) = sqlx::query(
                "INSERT INTO request_events (org_id, original_model, routed_model, provider, strategy, latency_ms, status_code, is_streaming, prompt_tokens, completion_tokens, created_at)
                 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)"
            )
            .bind(&event.org_id)
            .bind(&event.original_model)
            .bind(&event.routed_model)
            .bind(&event.provider)
            .bind(&event.strategy)
            .bind(event.latency_ms as i32)
            .bind(event.status_code as i16)
            .bind(event.is_streaming)
            .bind(event.prompt_tokens as i32)
            .bind(event.completion_tokens as i32)
            .bind(event.timestamp)
            .execute(&ts_pool)
            .await {
                tracing::warn!(error = %e, "Failed to persist telemetry event");
            }
        }
    });

    // Build proxy state
    let state = Arc::new(ProxyState {
        auth: Arc::new(LocalAuthProvider::new(
            pg_pool.clone(),
            config.jwt_secret.clone(),
            redis_pool.clone(),
        )),
        router: Arc::new(RouterBrain::new()),
        telemetry_tx,
        http_client: reqwest::Client::builder()
            .timeout(std::time::Duration::from_secs(300)) // 5min for long completions
            .build()?,
        config: config.clone(),
    });

    // Start server
    let app = create_router(state);
    let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", config.proxy_port)).await?;
    info!(port = config.proxy_port, "Proxy listening");
    axum::serve(listener, app).await?;

    Ok(())
}
