//! Age key management REST endpoints
//!
//! Provides HTTP API for SOPS integration with SecretumVault:
//! - Generate new Age keypairs
//! - Retrieve public keys for .sops.yaml configuration
//! - Retrieve private keys for SOPS decryption
//! - Rotate Age keypairs
//!
//! All operations are audited and logged for security compliance.

use std::sync::Arc;

use axum::{
    extract::Query,
    extract::State,
    http::StatusCode,
    response::{IntoResponse, Json, Response},
    routing::{get, post},
    Router,
};
use serde::{Deserialize, Serialize};
use tracing::{debug, error, info, warn};

use crate::age_keys::AgeKeyManager;
use crate::types::KmsError;

/// Age operations state
pub struct AgeState {
    pub key_manager: AgeKeyManager,
}

/// Query parameters for age operations
#[derive(Debug, Deserialize)]
pub struct AgeEnvironmentQuery {
    /// Environment: dev, staging, prod
    pub env: String,
}

/// Response for public key retrieval
#[derive(Debug, Serialize)]
pub struct PublicKeyResponse {
    /// Age public key (age1...)
    pub public_key: String,
    /// Environment this key is for
    pub environment: String,
    /// When this endpoint was called
    pub timestamp: String,
}

/// Response for private key retrieval (SENSITIVE)
#[derive(Debug, Serialize)]
pub struct PrivateKeyResponse {
    /// Age private key (AGE-SECRET-KEY-1...)
    /// CAUTION: This is sensitive material. Only returned over secure channels.
    pub private_key: String,
    /// Environment this key is for
    pub environment: String,
    /// Version for tracking rotations
    pub version: u32,
    /// Security notice
    #[serde(skip_serializing_if = "Option::is_none")]
    pub security_notice: Option<String>,
}

/// Response for keypair generation
#[derive(Debug, Serialize)]
pub struct GenerateKeypairResponse {
    /// Age public key
    pub public_key: String,
    /// Environment this key is for
    pub environment: String,
    /// When generated
    pub created_at: String,
    /// Version (1 for new keys)
    pub version: u32,
    /// Instruction for next steps
    pub next_steps: String,
}

/// Response for keypair rotation
#[derive(Debug, Serialize)]
pub struct RotateKeypairResponse {
    /// Age public key (new)
    pub public_key: String,
    /// Environment this key is for
    pub environment: String,
    /// Previous version
    pub previous_version: u32,
    /// New version
    pub new_version: u32,
    /// When rotated
    pub rotated_at: String,
    /// Action required
    pub action_required: String,
}

/// Error response
#[derive(Debug, Serialize)]
struct AgeErrorResponse {
    error: String,
    error_code: String,
}

impl IntoResponse for AgeErrorResponse {
    fn into_response(self) -> Response {
        let status = match self.error_code.as_str() {
            "not_found" => StatusCode::NOT_FOUND,
            "auth_failed" => StatusCode::UNAUTHORIZED,
            "config_error" => StatusCode::BAD_REQUEST,
            _ => StatusCode::INTERNAL_SERVER_ERROR,
        };
        (status, Json(self)).into_response()
    }
}

/// Create age-specific routes
pub fn create_age_routes(age_state: Arc<AgeState>) -> Router {
    Router::new()
        .route("/api/v1/age/generate", post(generate_keypair))
        .route("/api/v1/age/get-public", get(get_public_key))
        .route("/api/v1/age/get-private", get(get_private_key))
        .route("/api/v1/age/rotate", post(rotate_keypair))
        .with_state(age_state)
}

/// Generate a new Age keypair for an environment
///
/// # POST /api/v1/age/generate
///
/// Request: `?env=dev`
///
/// Response: `{"public_key": "age1...", "environment": "dev", ...}`
async fn generate_keypair(
    State(state): State<Arc<AgeState>>,
    Query(params): Query<AgeEnvironmentQuery>,
) -> Result<Json<GenerateKeypairResponse>, AgeErrorResponse> {
    info!("Generating Age keypair for environment: {}", params.env);

    // Validate environment
    if !validate_environment(&params.env) {
        warn!("Invalid environment requested: {}", params.env);
        return Err(AgeErrorResponse {
            error: format!(
                "Invalid environment: {}. Use dev, staging, or prod",
                params.env
            ),
            error_code: "config_error".to_string(),
        });
    }

    // Generate keypair
    match state.key_manager.generate_keypair(&params.env).await {
        Ok(keypair) => {
            info!(
                "✓ Age keypair generated for {}: version {}",
                params.env, keypair.version
            );

            let response = GenerateKeypairResponse {
                public_key: keypair.public_key,
                environment: keypair.environment,
                created_at: keypair.created_at,
                version: keypair.version,
                next_steps: "Update .sops.yaml with this public key and run: sops updatekeys \
                             <encrypted-files>"
                    .to_string(),
            };

            Ok(Json(response))
        }
        Err(e) => {
            error!("Failed to generate Age keypair for {}: {}", params.env, e);
            Err(AgeErrorResponse {
                error: e.to_string(),
                error_code: error_code_for_kms_error(&e),
            })
        }
    }
}

/// Get public key for an environment
///
/// # GET /api/v1/age/get-public
///
/// Request: `?env=dev`
///
/// Response: `{"public_key": "age1...", "environment": "dev"}`
///
/// This endpoint is safe to call without authentication since public keys are,
/// well, public.
async fn get_public_key(
    State(state): State<Arc<AgeState>>,
    Query(params): Query<AgeEnvironmentQuery>,
) -> Result<Json<PublicKeyResponse>, AgeErrorResponse> {
    debug!("Retrieving Age public key for: {}", params.env);

    // Validate environment
    if !validate_environment(&params.env) {
        return Err(AgeErrorResponse {
            error: format!("Invalid environment: {}", params.env),
            error_code: "config_error".to_string(),
        });
    }

    match state.key_manager.get_public_key(&params.env).await {
        Ok(public_key) => {
            let response = PublicKeyResponse {
                public_key,
                environment: params.env,
                timestamp: chrono::Utc::now().to_rfc3339(),
            };
            Ok(Json(response))
        }
        Err(e) => {
            error!("Failed to retrieve Age public key: {}", e);
            Err(AgeErrorResponse {
                error: e.to_string(),
                error_code: error_code_for_kms_error(&e),
            })
        }
    }
}

/// Get private key for an environment (SENSITIVE - requires auth)
///
/// # GET /api/v1/age/get-private
///
/// Request: `?env=dev` with auth token in X-Vault-Token header
///
/// Response: `{"private_key": "AGE-SECRET-KEY-1...", "environment": "dev",
/// ...}`
///
/// ⚠️  SENSITIVE: This endpoint returns the Age private key.
/// - Only use over secure channels (TLS)
/// - Requires proper authentication
/// - All requests are audited
/// - Used by CI/CD pipelines for SOPS decryption
async fn get_private_key(
    State(state): State<Arc<AgeState>>,
    Query(params): Query<AgeEnvironmentQuery>,
) -> Result<Json<PrivateKeyResponse>, AgeErrorResponse> {
    info!(
        "SENSITIVE: Age private key requested for: {} (AUDIT THIS)",
        params.env
    );

    // Validate environment
    if !validate_environment(&params.env) {
        warn!("Invalid environment for private key: {}", params.env);
        return Err(AgeErrorResponse {
            error: format!("Invalid environment: {}", params.env),
            error_code: "config_error".to_string(),
        });
    }

    match state
        .key_manager
        .get_private_key_with_version(&params.env)
        .await
    {
        Ok((private_key, version)) => {
            info!(
                "✓ Age private key served for {} (version {}, check audit logs)",
                params.env, version
            );

            let response = PrivateKeyResponse {
                private_key,
                environment: params.env,
                version,
                security_notice: Some(
                    "This private key is sensitive material. Use only for SOPS decryption."
                        .to_string(),
                ),
            };

            Ok(Json(response))
        }
        Err(e) => {
            error!("SECURITY: Failed to retrieve Age private key: {}", e);
            Err(AgeErrorResponse {
                error: "Failed to retrieve private key".to_string(),
                error_code: error_code_for_kms_error(&e),
            })
        }
    }
}

/// Rotate Age keypair for an environment
///
/// # POST /api/v1/age/rotate
///
/// Request: `?env=dev` with auth token
///
/// Response: `{"public_key": "age1...", "new_version": 2, ...}`
///
/// Generates a new keypair and increments the version.
/// Existing SOPS files need to be re-encrypted with `sops updatekeys`.
async fn rotate_keypair(
    State(state): State<Arc<AgeState>>,
    Query(params): Query<AgeEnvironmentQuery>,
) -> Result<Json<RotateKeypairResponse>, AgeErrorResponse> {
    info!("Rotating Age keypair for environment: {}", params.env);

    // Validate environment
    if !validate_environment(&params.env) {
        return Err(AgeErrorResponse {
            error: format!("Invalid environment: {}", params.env),
            error_code: "config_error".to_string(),
        });
    }

    match state.key_manager.rotate_keypair(&params.env).await {
        Ok(keypair) => {
            info!(
                "✓ Age keypair rotated for {}: version {}",
                params.env, keypair.version
            );

            let response = RotateKeypairResponse {
                public_key: keypair.public_key,
                environment: keypair.environment,
                previous_version: keypair.version - 1,
                new_version: keypair.version,
                rotated_at: keypair.created_at,
                action_required: format!(
                    "Re-encrypt all SOPS files for {} with: for f in config/secrets/{}/*.yaml; do \
                     sops updatekeys $f; done",
                    params.env, params.env
                ),
            };

            Ok(Json(response))
        }
        Err(e) => {
            error!("Failed to rotate Age keypair for {}: {}", params.env, e);
            Err(AgeErrorResponse {
                error: e.to_string(),
                error_code: error_code_for_kms_error(&e),
            })
        }
    }
}

/// Validate environment name
fn validate_environment(env: &str) -> bool {
    matches!(env, "dev" | "staging" | "prod")
}

/// Map KMS errors to error codes for client
fn error_code_for_kms_error(err: &KmsError) -> String {
    match err {
        KmsError::KeyNotFound(_) => "not_found".to_string(),
        KmsError::AuthenticationFailed(_) => "auth_failed".to_string(),
        KmsError::ConfigurationError(_) => "config_error".to_string(),
        _ => "internal_error".to_string(),
    }
}
