use std::sync::Arc;

use tower_http::cors::CorsLayer;
use tower_http::trace::TraceLayer;
use tracing::{error, info};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use vault_service::api::create_router;
use vault_service::service::KmsService;
use vault_service::types::KmsBackendConfig;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize tracing
    tracing_subscriber::registry()
        .with(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "vault_service=info,tower_http=debug".into()),
        )
        .with(tracing_subscriber::fmt::layer())
        .init();

    info!("Starting KMS service...");

    // Load configuration
    let config = load_config().await?;
    info!("Configuration loaded successfully");

    // Create KMS service
    let kms_service = KmsService::new(config).await?;
    info!(
        "KMS service initialized with backend: {}",
        kms_service.get_backend_name()
    );

    // Perform initial health check
    match kms_service.health_check().await {
        Ok(true) => info!("KMS backend health check: PASSED"),
        Ok(false) => {
            error!("KMS backend health check: FAILED");
            return Err("KMS backend is not healthy".into());
        }
        Err(e) => {
            error!("KMS backend health check error: {}", e);
            return Err(e.into());
        }
    }

    let kms_service = Arc::new(kms_service);

    // Create API router
    let app = create_router(kms_service)
        .layer(CorsLayer::permissive())
        .layer(TraceLayer::new_for_http());

    // Get bind address from environment or use default
    let bind_addr = std::env::var("KMS_BIND_ADDR").unwrap_or_else(|_| "0.0.0.0:8081".to_string());

    info!("KMS service listening on {}", bind_addr);

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

    Ok(())
}

/// Load KMS configuration from file or environment
async fn load_config() -> Result<KmsBackendConfig, Box<dyn std::error::Error>> {
    // Try to load from config file first
    let config_path = std::env::var("KMS_CONFIG_PATH")
        .unwrap_or_else(|_| "provisioning/config/kms.toml".to_string());

    if std::path::Path::new(&config_path).exists() {
        info!("Loading configuration from: {}", config_path);
        let config_str = tokio::fs::read_to_string(&config_path).await?;
        let config: KmsConfig = toml::from_str(&config_str)?;
        return Ok(config.kms);
    }

    // Fallback to environment variables
    info!("Config file not found, loading from environment variables");
    load_config_from_env()
}

/// Load configuration from environment variables
fn load_config_from_env() -> Result<KmsBackendConfig, Box<dyn std::error::Error>> {
    let env = std::env::var("PROVISIONING_ENV")
        .unwrap_or_else(|_| "dev".to_string())
        .to_lowercase();

    match env.as_str() {
        "dev" | "development" => {
            // Age backend for development
            let public_key_path = std::env::var("AGE_PUBLIC_KEY_PATH")
                .unwrap_or_else(|_| "~/.config/provisioning/age/public_key.txt".to_string());
            let private_key_path = std::env::var("AGE_PRIVATE_KEY_PATH")
                .unwrap_or_else(|_| "~/.config/provisioning/age/private_key.txt".to_string());

            Ok(KmsBackendConfig::Age {
                public_key_path,
                private_key_path,
            })
        }
        "prod" | "production" => {
            // Cosmian backend for production
            let server_url = std::env::var("COSMIAN_KMS_URL")
                .map_err(|_| "COSMIAN_KMS_URL environment variable is required for production")?;
            let api_key = std::env::var("COSMIAN_API_KEY")
                .map_err(|_| "COSMIAN_API_KEY environment variable is required for production")?;
            let default_key_id = std::env::var("COSMIAN_KEY_ID")
                .unwrap_or_else(|_| "provisioning-master-key".to_string());
            let tls_verify = std::env::var("COSMIAN_TLS_VERIFY")
                .unwrap_or_else(|_| "true".to_string())
                .parse()
                .unwrap_or(true);

            Ok(KmsBackendConfig::Cosmian {
                server_url,
                api_key,
                default_key_id,
                tls_verify,
            })
        }
        _ => Err(format!(
            "Invalid PROVISIONING_ENV: {} (must be 'dev' or 'prod')",
            env
        )
        .into()),
    }
}

/// Configuration file structure
#[derive(Debug, serde::Deserialize)]
struct KmsConfig {
    kms: KmsBackendConfig,
}
