use std::sync::Arc;

use axum::Router;
use sqlx::postgres::PgPoolOptions;
use tokio::net::TcpListener;
use tower_http::cors::{Any, CorsLayer};
use tower_http::trace::TraceLayer;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

mod api;
mod config;
mod db;
mod middleware;
mod models;
mod services;

use config::AppConfig;

/// Shared application state available to all handlers
pub struct AppState {
    pub db: sqlx::PgPool,
    pub nats: Option<async_nats::Client>,
    pub config: AppConfig,
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Load environment variables
    dotenvy::dotenv().ok();

    // Initialize tracing
    tracing_subscriber::registry()
        .with(tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
            "corrosion_api=debug,tower_http=debug".into()
        }))
        .with(tracing_subscriber::fmt::layer())
        .init();

    let config = AppConfig::from_env()?;

    // Database connection pool
    let db = PgPoolOptions::new()
        .max_connections(config.database_max_connections)
        .connect(&config.database_url)
        .await?;

    tracing::info!("Connected to PostgreSQL");

    // Run migrations
    sqlx::migrate!("./migrations").run(&db).await?;
    tracing::info!("Database migrations applied");

    // NATS connection (optional for dev)
    let nats = match async_nats::connect(&config.nats_url).await {
        Ok(client) => {
            tracing::info!("Connected to NATS at {}", config.nats_url);
            Some(client)
        }
        Err(e) => {
            tracing::warn!("NATS not available ({}), running without event bus", e);
            None
        }
    };

    // Bootstrap: create admin user + license on first run
    bootstrap_admin(&db).await;

    // Initialize background services if NATS is available
    if let Some(ref nats_client) = nats {
        let nats_bridge = Arc::new(services::nats_bridge::NatsBridge::new(nats_client.clone()));

        // Start stats consumer
        let stats_consumer = services::stats_consumer::StatsConsumerService::new(
            db.clone(),
            nats_bridge.clone(),
        );
        if let Err(e) = stats_consumer.start().await {
            tracing::error!("Failed to start stats consumer: {}", e);
        }

        // Start scheduler service
        let scheduler = services::scheduler::SchedulerService::new(
            db.clone(),
            nats_bridge.clone(),
        )
        .await?;

        // Register stats jobs
        if let Err(e) = scheduler.register_stats_aggregation().await {
            tracing::error!("Failed to register stats aggregation job: {}", e);
        }
        if let Err(e) = scheduler.register_stats_cleanup().await {
            tracing::error!("Failed to register stats cleanup job: {}", e);
        }

        if let Err(e) = scheduler.start().await {
            tracing::error!("Failed to start scheduler: {}", e);
        } else {
            tracing::info!("Scheduler service started");
        }
    } else {
        tracing::warn!("Skipping background services (NATS not available)");
    }

    let state = Arc::new(AppState { db, nats, config });

    // CORS — permissive in dev, locked down in production
    let cors = CorsLayer::new()
        .allow_origin(Any)
        .allow_methods(Any)
        .allow_headers(Any);

    // Build router
    let app = Router::new()
        .nest("/api/auth", api::auth::router())
        .nest("/api/servers", api::servers::router())
        .nest("/api/wipes", api::wipes::router())
        .nest("/api/maps", api::maps::router())
        .nest("/api/plugins", api::plugins::router())
        .nest("/api/panels", api::panels::router())
        .nest("/api/schedules", api::schedules::router())
        .nest("/api/logs", api::logs::router())
        .nest("/api/public", api::public::router())
        .nest("/api/team", api::team::router())
        .nest("/api/notifications", api::notifications::router())
        .nest("/api/license", api::license::router())
        .nest("/api/store", api::store::router())
        .nest("/api/early-access", api::early_access::router())
        .nest("/api/admin", api::admin::router())
        .nest("/api/ws", api::ws::router())
        .nest("/api/analytics", api::analytics::router())
        .layer(cors)
        .layer(TraceLayer::new_for_http())
        .with_state(state);

    let bind_addr = "0.0.0.0:3000";
    let listener = TcpListener::bind(bind_addr).await?;
    tracing::info!("Corrosion API listening on {}", bind_addr);

    axum::serve(listener, app).await?;

    Ok(())
}

/// Bootstrap: if no users exist and ADMIN_EMAIL/ADMIN_PASSWORD are set,
/// create the initial admin user and a dev license key.
async fn bootstrap_admin(db: &sqlx::PgPool) {
    let admin_email = std::env::var("ADMIN_EMAIL").unwrap_or_default();
    let admin_password = std::env::var("ADMIN_PASSWORD").unwrap_or_default();
    let admin_username = std::env::var("ADMIN_USERNAME").unwrap_or_else(|_| "Commander".to_string());

    if admin_email.is_empty() || admin_password.is_empty() {
        return;
    }

    // Check if any users exist
    let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
        .fetch_one(db)
        .await
        .unwrap_or((0,));

    if count.0 > 0 {
        tracing::debug!("Users already exist, skipping bootstrap");
        return;
    }

    tracing::info!("No users found — bootstrapping admin user: {}", admin_email);

    // Hash password
    let password_hash = match services::auth::hash_password(&admin_password) {
        Ok(h) => h,
        Err(e) => {
            tracing::error!("Failed to hash admin password: {e}");
            return;
        }
    };

    // Create admin user
    let user_id = match db::users::create_user(db, &admin_email, &admin_username, &password_hash).await {
        Ok(id) => id,
        Err(e) => {
            tracing::error!("Failed to create admin user: {e}");
            return;
        }
    };

    // Flag as super-admin
    if let Err(e) = sqlx::query("UPDATE users SET is_super_admin = true WHERE id = $1")
        .bind(user_id)
        .execute(db)
        .await
    {
        tracing::error!("Failed to set super-admin flag: {e}");
    }

    // Create a license for the admin
    let license_key = std::env::var("ADMIN_LICENSE_KEY")
        .unwrap_or_else(|_| format!("CORROSION-{}", services::encryption::generate_token(8).to_uppercase()));

    match db::licenses::create_license(db, &license_key, user_id).await {
        Ok(_) => {
            tracing::info!("Bootstrap complete — admin: {}, license: {}", admin_email, license_key);
        }
        Err(e) => {
            tracing::error!("Failed to create admin license: {e}");
        }
    }
}
