mod config;
mod fetcher;
mod parser;
mod worker;

use std::sync::Arc;

use config::IngestConfig;
use tokio::sync::Semaphore;
use tracing::info;
use tracing_subscriber::{fmt, EnvFilter};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    fmt().with_env_filter(EnvFilter::from_default_env()).init();

    let config_path = std::env::args()
        .nth(1)
        .unwrap_or_else(|| "config/dev/ingest_binance.json".to_owned());

    let config: IngestConfig = {
        let file = std::fs::File::open(&config_path)?;
        serde_json::from_reader(std::io::BufReader::new(file))?
    };

    info!(config_path, "loaded ingest-binance config");

    let pool = swym_dal::connect(&config.database).await?;
    let client = Arc::new(fetcher::BinanceS3Client::new()?);
    let semaphore = Arc::new(Semaphore::new(config.max_concurrent));

    let poll_interval = std::time::Duration::from_secs(config.poll_interval_secs);
    let mut poll_timer = tokio::time::interval(poll_interval);
    poll_timer.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);

    info!(
        poll_interval_secs = config.poll_interval_secs,
        max_concurrent = config.max_concurrent,
        "ingest-binance started"
    );

    loop {
        tokio::select! {
            _ = poll_timer.tick() => {
                let configs = match swym_dal::repo::ingest_config::list_active(&pool).await {
                    Ok(c) => c,
                    Err(e) => {
                        tracing::error!(?e, "failed to load ingest configs");
                        continue;
                    }
                };

                if configs.is_empty() {
                    tracing::info!("no active ingest configs");
                    continue;
                }

                info!(count = configs.len(), "processing ingest configs");

                let mut handles = Vec::with_capacity(configs.len());
                for cfg in configs {
                    let pool = pool.clone();
                    let client = Arc::clone(&client);
                    let sem = Arc::clone(&semaphore);
                    let batch_size = config.batch_size;

                    let handle = tokio::spawn(async move {
                        let _permit = sem.acquire().await.expect("semaphore closed");
                        if let Err(e) = worker::process_config(&pool, &*client, &cfg, batch_size).await {
                            tracing::error!(symbol = %cfg.symbol, ?e, "ingestion failed");
                        }
                    });
                    handles.push(handle);
                }

                for handle in handles {
                    let _ = handle.await;
                }
            }
            _ = tokio::signal::ctrl_c() => {
                info!("shutdown signal received");
                break;
            }
        }
    }

    info!("ingest-binance stopped");
    Ok(())
}
