mod api;
mod config;
mod error;
mod state;
mod openapi;
#[cfg(test)]
mod openapi_tests;
#[cfg(test)]
mod registry_unit_test;

use crate::config::AppConfig;
use crate::error::Result;
use crate::state::AppState;
use std::io::{self, Write};
use std::process;
use tower_http::trace::TraceLayer;
use tracing::{error, info};
use tracing_subscriber::EnvFilter;

#[tokio::main]
async fn main() {
    // Ensure panics are clearly printed with backtraces (independent of env var)
    std::panic::set_hook(Box::new(|panic_info| {
        eprintln!("panic in api-gateway: {}", panic_info);
        eprintln!(
            "backtrace:\n{:?}",
            std::backtrace::Backtrace::force_capture()
        );
    }));

    // Emit earliest visibility logs to stderr for containers that restart quickly
    let ts = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);
    eprintln!(
        "api-gateway launching: pid={}, ts_unix={}",
        process::id(),
        ts
    );
    // Print critical environment variables relevant to configuration (no secrets)
    eprintln!(
        "env: SERVER_PORT={:?}, NATS_ADDR={:?}, DATA_PERSISTENCE_SERVICE_URL={:?}",
        std::env::var("SERVER_PORT").ok(),
        std::env::var("NATS_ADDR").ok(),
        std::env::var("DATA_PERSISTENCE_SERVICE_URL").ok(),
    );
    let _ = io::stderr().flush();

    if let Err(e) = run().await {
        eprintln!("api-gateway failed to start: {}", e);
        process::exit(1);
    }
}

async fn run() -> Result<()> {
    // Initialize deterministic logging, default to info if not provided
    let env_filter = EnvFilter::try_from_default_env()
        .unwrap_or_else(|_| EnvFilter::new("info,axum=info,hyper=info"));
    tracing_subscriber::fmt()
        .with_env_filter(env_filter)
        .with_target(true)
        .with_thread_ids(true)
        .with_thread_names(true)
        .with_ansi(false)
        .compact()
        .init();

    info!("Starting api-gateway service...");

    // Load configuration
    let config = AppConfig::load().map_err(|e| error::AppError::Configuration(e.to_string()))?;
    let port = config.server_port;
    info!(
        server_port = port,
        nats_addr = %config.nats_addr,
        persistence_url = %config.data_persistence_service_url,
        "Loaded configuration"
    );
    // info!("Configured provider services: {:?}", config.provider_services); // Removed in favor of dynamic registry

    // Initialize application state
    let app_state = AppState::new(config).await?;

    // Create the Axum router
    let app = api::create_router(app_state)
        // Request-level tracing for better observability in Tilt/Compose logs
        .layer(TraceLayer::new_for_http());

    // Start the HTTP server
    let addr = format!("0.0.0.0:{}", port);
    let listener = match tokio::net::TcpListener::bind(&addr).await {
        Ok(l) => l,
        Err(e) => {
            error!(%addr, err = %e, "Failed to bind TCP listener");
            return Err(error::AppError::Internal(anyhow::anyhow!(e)));
        }
    };
    info!("HTTP server listening on port {}", port);
    if let Err(e) = axum::serve(listener, app).await {
        error!(err = %e, "HTTP server terminated with error");
        return Err(error::AppError::Internal(anyhow::anyhow!(e)));
    }

    Ok(())
}
