mod config;
mod error;
mod exchange_client;
mod handlers;
mod state;

use std::collections::HashMap;
use std::sync::Arc;

use axum::{Router, routing::{delete, get, patch, post}};
use config::ApiConfig;
use state::AppState;
use tokio::sync::RwLock;
use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
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/api.json".to_owned());

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

    info!(?config_path, host = %config.host, port = config.port, "loaded API config");

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

    // Seed curated strategies (environment-specific, idempotent)
    if let Some(env) = &config.database.seed_env {
        swym_dal::seeds::run_seeds(&pool, env).await?;
    }

    let http_client = reqwest::Client::builder()
        .timeout(std::time::Duration::from_secs(30))
        .build()?;

    let state = AppState {
        pool,
        http_client,
        instrument_cache: Arc::new(RwLock::new(HashMap::new())),
    };

    let app = Router::new()
        .route("/health", get(handlers::health::health))
        .route("/api/v1/paper-runs", post(handlers::paper_runs::create_paper_run))
        .route("/api/v1/paper-runs", get(handlers::paper_runs::list_paper_runs))
        .route("/api/v1/paper-runs/compare", get(handlers::paper_runs::compare_paper_runs))
        .route("/api/v1/paper-runs/{id}", get(handlers::paper_runs::get_paper_run))
        .route("/api/v1/paper-runs/{id}/cancel", post(handlers::paper_runs::cancel_paper_run))
        .route("/api/v1/paper-runs/{id}/positions", get(handlers::paper_runs::list_paper_run_positions))
        .route("/api/v1/paper-runs/{id}/candles", get(handlers::paper_runs::list_paper_run_candles))
        .route("/api/v1/paper-runs/{id}/condition-audit", get(handlers::paper_runs::get_condition_audit_summary))
        .route("/api/v1/paper-runs/{id}/condition-audit/detail", get(handlers::paper_runs::get_condition_audit_detail))
        .route("/api/v1/market-subscriptions", post(handlers::market_subscriptions::create_subscriptions))
        .route("/api/v1/market-subscriptions", get(handlers::market_subscriptions::list_subscriptions))
        .route("/api/v1/market-subscriptions/{id}", delete(handlers::market_subscriptions::deactivate_subscription))
        .route("/api/v1/ingest-configs", get(handlers::ingest_configs::list_ingest_configs))
        .route("/api/v1/ingest-configs", post(handlers::ingest_configs::create_ingest_config))
        .route("/api/v1/ingest-configs/{id}", patch(handlers::ingest_configs::update_ingest_config))
        .route("/api/v1/ingestion-stats", get(handlers::ingestion_stats::get_ingestion_stats))
        .route("/api/v1/market-candles/backfill", post(handlers::market_candles::backfill_candles))
        .route("/api/v1/market-candles/coverage/{exchange}/{symbol}", get(handlers::market_candles::get_candle_coverage))
        .route("/api/v1/exchanges", get(handlers::exchanges::list_exchanges))
        .route("/api/v1/exchanges/{name}/sub-kinds", get(handlers::exchanges::get_exchange_sub_kinds))
        .route("/api/v1/exchanges/{name}/instruments", get(handlers::exchanges::get_exchange_instruments))
        .route("/api/v1/instruments/{exchange}/{name}/data-range", get(handlers::paper_runs::get_instrument_data_range))
        .route("/api/v1/strategies/validate", post(handlers::strategies::validate_strategy))
        .route("/api/v1/strategies", get(handlers::strategies::list_strategies))
        .route("/api/v1/strategies", post(handlers::strategies::create_strategy))
        .route("/api/v1/strategies/{id}", get(handlers::strategies::get_strategy))
        .layer(CorsLayer::permissive())
        .layer(TraceLayer::new_for_http())
        .with_state(state);

    let bind_addr = format!("{}:{}", config.host, config.port);
    info!(%bind_addr, "starting HTTP server");

    let listener = tokio::net::TcpListener::bind(&bind_addr).await?;
    axum::serve(listener, app).await?;

    Ok(())
}
