//! Control Center Main Binary
//!
//! JWT authentication service with user management, role-based access control,
//! and real-time WebSocket events.

use std::time::Duration;
use std::{path::PathBuf, sync::Arc};

use axum::{
    middleware,
    routing::{get, post},
    Router,
};

mod api_catalog;
use clap::Parser;
use control_center::handlers::{
    auth::*,
    iac_deployment::{
        create_deployment, get_deployment, get_deployment_status, list_deployments,
        submit_deployment, update_deployment,
    },
    iac_detection::{analyze_project, get_detection, list_detections},
    iac_rules::{
        create_rule, delete_rule, get_rule, list_org_rules, list_rules, test_rule, update_rule,
    },
    permission::list_permissions,
    secrets::{
        create_grant, create_secret, delete_secret, force_rotate_secret, get_alert_summary,
        get_dashboard_metrics, get_expiring_secrets, get_rotation_status, get_secret,
        get_secret_history, list_secrets, restore_secret_version, revoke_grant, update_secret,
    },
    websocket::websocket_handler,
};
use control_center::middleware::{
    auth::{auth_middleware, solo_auth_middleware},
    cors::create_cors_from_env,
    rate_limit::{RateLimitConfig, RateLimitLayer},
};
use control_center::{AppState, Config, Result};
use hyper::http::StatusCode;
use tokio::signal;
use tower::ServiceBuilder;
use tower_http::{compression::CompressionLayer, timeout::TimeoutLayer, trace::TraceLayer};
use tracing::{error, info, warn};

#[derive(Parser)]
#[command(name = "control-center")]
#[command(about = "Control Center - JWT Authentication & User Management Service")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(
    after_help = "CONFIGURATION HIERARCHY (highest to lowest priority):\n  1. CLI: -c/--config \
                  <path> (explicit file)\n  2. CLI: --config-dir <dir> --mode <mode> (directory + \
                  mode)\n  3. CLI: --config-dir <dir> (searches for \
                  control-center.ncl|toml|json)\n  4. CLI: --mode <mode> (searches in \
                  provisioning/platform/config/)\n  5. ENV: CONTROL_CENTER_CONFIG (explicit \
                  file)\n  6. ENV: PROVISIONING_CONFIG_DIR (searches for \
                  control-center.ncl|toml|json)\n  7. ENV: CONTROL_CENTER_MODE (mode-based in \
                  default path)\n  8. Built-in defaults"
)]
struct Cli {
    /// Configuration file path (highest priority)
    ///
    /// Accepts absolute or relative path. Supports .ncl, .toml, and .json
    /// formats.
    #[arg(short = 'c', long, env = "CONTROL_CENTER_CONFIG")]
    config: Option<PathBuf>,

    /// Configuration directory (searches for control-center.ncl|toml|json)
    ///
    /// Searches for configuration files in order of preference: .ncl > .toml >
    /// .json Can also search for mode-specific files:
    /// control-center.{mode}.{ncl|toml|json}
    #[arg(long, env = "PROVISIONING_CONFIG_DIR")]
    config_dir: Option<PathBuf>,

    /// Deployment mode (solo, multiuser, cicd, enterprise)
    ///
    /// Determines which configuration profile to use. Searches in:
    /// provisioning/platform/config/control-center.{mode}.{ncl|toml}
    #[arg(short = 'm', long, env = "CONTROL_CENTER_MODE")]
    mode: Option<String>,

    /// Server port (overrides config file)
    #[arg(short = 'p', long)]
    port: Option<u16>,

    /// Server host (overrides config file)
    #[arg(long)]
    host: Option<String>,

    /// Enable debug logging
    #[arg(short, long)]
    debug: bool,

    /// Generate default configuration file
    #[arg(long)]
    generate_config: bool,

    /// Print all #[onto_api] registered routes as JSON and exit.
    /// Pipe to api-catalog-control-center.json: `just export-api-catalog`
    #[arg(long)]
    dump_api_catalog: bool,
}

#[tokio::main]
async fn main() -> Result<()> {
    let cli = Cli::parse();

    if cli.dump_api_catalog {
        println!("{}", ontoref_ontology::api::dump_catalog_json());
        return Ok(());
    }

    // Generate default config if requested
    if cli.generate_config {
        let config_path = cli.config.unwrap_or_else(|| PathBuf::from("config.toml"));
        control_center::simple_config::create_default_config_file(&config_path)?;
        info!("Default configuration file created at: {:?}", config_path);
        return Ok(());
    }

    // Initialize centralized observability (logging, metrics, health checks)
    let _guard = observability::init_from_env("control-center", env!("CARGO_PKG_VERSION"))
        .map_err(|e| control_center::ControlCenterError::from(anyhow::anyhow!(e)))?;

    // Check if control-center is enabled in deployment-mode.ncl
    if let Ok(deployment) = platform_config::load_deployment_mode() {
        if let Ok(enabled) = deployment.is_service_enabled("control_center") {
            if !enabled {
                warn!("⚠ Control Center is DISABLED in deployment-mode.ncl");
                std::process::exit(1);
            }
            info!("✓ Control Center is ENABLED in deployment-mode.ncl");
        }
    }

    // Load configuration from NCL using the same pattern as orchestrator and vault-service:
    // ControlCenterNclConfig implements ConfigLoader which reads the NCL via
    // PROVISIONING_CONFIG_DIR, exports to JSON, and deserializes into typed structs.
    use control_center::ncl_config::ControlCenterNclConfig;
    use platform_config::format::ConfigLoader as _;
    let ncl = ControlCenterNclConfig::load().map_err(|e| {
        control_center::ControlCenterError::Infrastructure(
            control_center::error::infrastructure::InfrastructureError::Configuration(format!(
                "Failed to load control-center NCL config: {}",
                e
            )),
        )
    })?;
    let mut config = ncl.to_runtime_config()?;

    // Apply explicit CLI overrides on top of NCL values
    if let Some(path) = &cli.config {
        // If a TOML config file was explicitly given, merge it on top of NCL
        let toml_config = Config::load_from_file(path)?;
        config = toml_config;
    }

    // Apply CLI overrides
    if let Some(port) = cli.port {
        config.server.port = port;
    }
    if let Some(host) = cli.host {
        config.server.host = host;
    }

    info!(
        "Starting Control Center v{} on {}",
        env!("CARGO_PKG_VERSION"),
        config.bind_address()
    );

    // Initialize application state
    let solo_mode = cli.mode.as_deref() == Some("solo");
    if solo_mode {
        warn!("⚠  Solo mode: JWT authentication is DISABLED — local operator access only");
    }
    let app_state = Arc::new(AppState::new(config.clone(), solo_mode).await?);

    // Health check
    if let Err(e) = app_state.health_check().await {
        error!("Health check failed: {}", e);
        return Err(e);
    }

    info!("All services initialized successfully");

    // Create router
    let app = create_router(app_state.clone()).await?;

    // Start session cleanup task
    start_background_tasks(app_state.clone()).await;

    // Start server
    let bind_address = config.bind_address();
    let listener = tokio::net::TcpListener::bind(&bind_address).await?;

    info!("Control Center listening on http://{}", bind_address);
    info!("WebSocket endpoint available at: ws://{}/ws", bind_address);

    // Graceful shutdown
    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await?;

    info!("Control Center shutdown complete");
    Ok(())
}

/// Create the main application router
async fn create_router(app_state: Arc<AppState>) -> Result<Router> {
    // Create rate limiting layers
    let auth_rate_limit = RateLimitLayer::new(RateLimitConfig::auth());
    let general_rate_limit = RateLimitLayer::new(RateLimitConfig::default());

    // Create CORS layer
    let cors_layer = create_cors_from_env();

    // Public routes (no authentication required)
    let public_routes = Router::new()
        .route("/health", get(health_check))
        .route("/auth/login", post(login))
        .route("/auth/refresh", post(refresh_token))
        .route("/api/catalog", get(api_catalog::api_catalog))
        .layer(auth_rate_limit);

    // Protected routes (authentication required)
    let protected_routes = Router::new()
        // Authentication routes
        .route("/auth/logout", post(logout))
        // .route("/auth/verify", get(verify_token))
        // .route("/auth/sessions", get(get_sessions))
        // .route("/auth/sessions/invalidate", post(invalidate_all_sessions))
        // .route("/auth/change-password", post(change_password))
        // User routes
        // .route("/users", get(list_users).post(create_user))
        // .route("/users/me", get(get_current_user).put(update_current_user))
        // .route(
        //     "/users/:id",
        //     get(get_user_by_id)
        //         .put(update_user_by_id)
        //         .delete(delete_user),
        // )
        // .route("/users/:id/activate", post(activate_user))
        // .route("/users/:id/deactivate", post(deactivate_user))
        // .route("/users/:id/verify", post(verify_user))
        // .route("/users/:id/roles/:role", post(add_user_role).delete(remove_user_role))
        // Role routes
        // .route("/roles", get(list_roles).post(create_role))
        // .route("/roles/:id", get(get_role_by_id).put(update_role).delete(delete_role))
        // Permission routes
        .route("/permissions", get(list_permissions))
        // .route("/permissions/resources", get(get_resources))
        // .route("/permissions/actions", get(get_actions))
        // Detection routes (Infrastructure-from-Code)
        .route("/detections", get(list_detections))
        .route("/detections/{id}", get(get_detection))
        .route("/detections/analyze", post(analyze_project))
        // Rules routes (Inference rules)
        .route("/rules", get(list_rules).post(create_rule))
        .route("/rules/org/{org}", get(list_org_rules))
        .route(
            "/rules/{id}",
            get(get_rule).put(update_rule).delete(delete_rule),
        )
        .route("/rules/{id}/test", post(test_rule))
        // Deployment routes
        .route(
            "/deployments",
            get(list_deployments).post(create_deployment),
        )
        .route(
            "/deployments/{id}",
            get(get_deployment).put(update_deployment),
        )
        .route("/deployments/{id}/submit", post(submit_deployment))
        .route("/deployments/{id}/status", get(get_deployment_status))
        // Secrets routes (Phase 1.5 - Now active with SecretsService state initialization)
        .route("/secrets", post(create_secret).get(list_secrets))
        .route(
            "/secrets/{path}",
            get(get_secret).put(update_secret).delete(delete_secret),
        )
        .route("/secrets/{path}/history", get(get_secret_history))
        .route(
            "/secrets/{path}/restore/{version}",
            post(restore_secret_version),
        )
        // Secrets Phase 3.1: Rotation routes
        .route("/secrets/{path}/rotate", post(force_rotate_secret))
        .route("/secrets/{path}/rotation-status", get(get_rotation_status))
        // Secrets Phase 3.2: Sharing routes
        .route("/secrets/{path}/grant", post(create_grant))
        .route("/secrets/grant/{grant_id}/revoke", post(revoke_grant))
        // Secrets Phase 3.4: Monitoring routes
        .route("/secrets/monitoring/dashboard", get(get_dashboard_metrics))
        .route("/secrets/monitoring/alerts", get(get_alert_summary))
        .route("/secrets/monitoring/expiring", get(get_expiring_secrets))
        // WebSocket route
        .route("/ws", get(websocket_handler));

    // In solo mode skip JWT validation and inject a fixed local-admin context
    // instead.
    let protected_routes = if app_state.solo_mode {
        protected_routes.route_layer(middleware::from_fn(solo_auth_middleware))
    } else {
        protected_routes.route_layer(middleware::from_fn_with_state(
            app_state.jwt_service.clone(),
            auth_middleware,
        ))
    };

    // Combine all routes
    let app = Router::new()
        .merge(public_routes)
        .merge(protected_routes)
        .layer(
            ServiceBuilder::new()
                .layer(TraceLayer::new_for_http())
                .layer(CompressionLayer::new())
                .layer(TimeoutLayer::with_status_code(
                    StatusCode::REQUEST_TIMEOUT,
                    Duration::from_secs(30),
                ))
                .layer(cors_layer)
                .layer(general_rate_limit),
        )
        .with_state(app_state);

    Ok(app)
}

/// Start background tasks
async fn start_background_tasks(app_state: Arc<AppState>) {
    let cleanup_interval =
        Duration::from_secs(app_state.config.security.session_cleanup_interval_minutes * 60);

    // Session cleanup task
    let app_state_cleanup = app_state.clone();
    tokio::spawn(async move {
        let mut interval = tokio::time::interval(cleanup_interval);
        loop {
            interval.tick().await;

            if let Err(e) = app_state_cleanup
                .auth_service
                .cleanup_expired_sessions()
                .await
            {
                error!("Failed to cleanup expired sessions: {}", e);
            } else {
                info!("Cleaned up expired sessions");
            }
        }
    });

    // NATS → WebSocket bridge: forward task status events to all connected WS
    // clients
    #[cfg(feature = "nats")]
    if let Some(nats) = &app_state.nats {
        use control_center::handlers::nats_bridge::spawn_nats_bridge;
        spawn_nats_bridge(Arc::clone(nats), Arc::clone(&app_state.websocket_manager));
        info!("NATS→WebSocket bridge started");
    }

    info!("Background tasks started");
}

/// Graceful shutdown signal handler
async fn shutdown_signal() {
    let ctrl_c = async {
        signal::ctrl_c()
            .await
            .expect("Failed to install Ctrl+C handler");
    };

    #[cfg(unix)]
    let terminate = async {
        signal::unix::signal(signal::unix::SignalKind::terminate())
            .expect("Failed to install signal handler")
            .recv()
            .await;
    };

    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }

    info!("Shutdown signal received, starting graceful shutdown");
}
