mod config;
mod ingest;
mod subscription_manager;

use config::WorkerConfig;
use ingest::{IngestBuffer, ResolvedEvent};
use std::time::Duration;
use subscription_manager::SubscriptionManager;
use tokio::sync::mpsc;
use tracing::info;
use tracing_subscriber::{EnvFilter, fmt};

#[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/market_worker.json".to_owned());

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

    info!(?config_path, "loaded worker config");

    // Connect to database (runs migrations)
    let pool = swym_dal::connect(&config.database).await?;

    // Channel for resolved events from subscription streams
    let (event_tx, mut event_rx) = mpsc::channel::<ResolvedEvent>(10_000);

    // Subscription manager polls DB and spawns streams
    let mut sub_manager = SubscriptionManager::new(pool.clone(), event_tx);

    // Do an initial poll immediately
    sub_manager.poll_and_spawn().await;

    let mut buffer = IngestBuffer::new(pool, config.batch_size);
    let flush_interval = Duration::from_millis(config.flush_interval_ms);
    let poll_interval = Duration::from_secs(config.poll_interval_secs);

    let mut flush_timer = tokio::time::interval(flush_interval);
    flush_timer.tick().await; // consume the immediate first tick

    let mut poll_timer = tokio::time::interval(poll_interval);
    poll_timer.tick().await; // consume the immediate first tick

    info!("market-worker started, polling for subscriptions every {poll_interval:?}");

    loop {
        tokio::select! {
            event = event_rx.recv() => {
                match event {
                    Some(resolved) => {
                        let should_flush = buffer.push(&resolved);
                        if should_flush {
                            buffer.flush().await;
                        }
                    }
                    None => {
                        info!("all event senders dropped, shutting down");
                        break;
                    }
                }
            }
            _ = flush_timer.tick() => {
                if !buffer.is_empty() {
                    buffer.flush().await;
                }
            }
            _ = poll_timer.tick() => {
                sub_manager.poll_and_spawn().await;
            }
            _ = tokio::signal::ctrl_c() => {
                info!("shutdown signal received");
                break;
            }
        }
    }

    // Flush remaining
    if !buffer.is_empty() {
        info!("flushing remaining buffers before shutdown");
        buffer.flush().await;
    }

    info!("market-worker shut down cleanly");
    Ok(())
}
