//! SSH Key Management REST API
//!
//! Provides HTTP endpoints for SSH key generation, deployment, and management.

use std::sync::Arc;

use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::Json,
    routing::{get, post},
    Router,
};
use serde::{Deserialize, Serialize};
use tracing::{error, info};

use super::{SshKeyManager, SshKeyRequest, SshKeyType};

/// Shared SSH manager state
pub type SshManagerState = Arc<SshKeyManager>;

/// API response wrapper
#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
    pub success: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<T>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
}

impl<T> ApiResponse<T> {
    pub fn success(data: T) -> Self {
        Self {
            success: true,
            data: Some(data),
            error: None,
        }
    }

    pub fn error(message: String) -> Self {
        Self {
            success: false,
            data: None,
            error: Some(message),
        }
    }
}

/// Generate SSH key request
#[derive(Debug, Deserialize)]
pub struct GenerateKeyRequest {
    pub key_type: SshKeyType,
    pub user: String,
    pub target_server: String,
    #[serde(default)]
    pub ttl_seconds: Option<i64>,
    pub allowed_ip: Option<String>,
    pub principal: Option<String>,
}

/// Deploy key request
#[derive(Debug, Deserialize)]
pub struct DeployKeyRequest {
    pub key_id: String,
}

/// List keys request
#[derive(Debug, Deserialize)]
pub struct ListKeysRequest {
    #[serde(default)]
    pub include_expired: bool,
}

/// Create SSH API router
pub fn create_ssh_routes() -> Router<SshManagerState> {
    Router::new()
        .route("/api/v1/ssh/generate", post(generate_ssh_key))
        .route("/api/v1/ssh/{id}/deploy", post(deploy_ssh_key))
        .route("/api/v1/ssh/{id}/revoke", post(revoke_ssh_key))
        .route("/api/v1/ssh/{id}", get(get_ssh_key))
        .route("/api/v1/ssh/keys", get(list_ssh_keys))
        .route("/api/v1/ssh/cleanup", post(cleanup_expired_keys))
        .route("/api/v1/ssh/stats", get(get_ssh_stats))
}

/// Generate SSH key handler
async fn generate_ssh_key(
    State(manager): State<SshManagerState>,
    Json(req): Json<GenerateKeyRequest>,
) -> Result<Json<ApiResponse<super::TemporalSshKey>>, StatusCode> {
    info!(
        "API: Generating SSH key type={:?} user={} server={}",
        req.key_type, req.user, req.target_server
    );

    let ttl = if let Some(seconds) = req.ttl_seconds {
        chrono::Duration::seconds(seconds)
    } else {
        chrono::Duration::hours(1)
    };

    let request = SshKeyRequest {
        key_type: req.key_type,
        user: req.user,
        target_server: req.target_server,
        ttl,
        allowed_ip: req.allowed_ip,
        principal: req.principal,
    };

    match manager.generate_key(request).await {
        Ok(key) => {
            info!("API: Successfully generated SSH key: {}", key.id);
            Ok(Json(ApiResponse::success(key)))
        }
        Err(e) => {
            error!("API: Failed to generate SSH key: {}", e);
            Ok(Json(ApiResponse::error(e.to_string())))
        }
    }
}

/// Deploy SSH key handler
async fn deploy_ssh_key(
    State(manager): State<SshManagerState>,
    Path(key_id): Path<String>,
) -> Result<Json<ApiResponse<super::SshKeyDeployment>>, StatusCode> {
    info!("API: Deploying SSH key: {}", key_id);

    match manager.deploy_key(&key_id).await {
        Ok(deployment) => {
            if deployment.success {
                info!("API: Successfully deployed SSH key: {}", key_id);
            } else {
                error!(
                    "API: Failed to deploy SSH key {}: {:?}",
                    key_id, deployment.error
                );
            }
            Ok(Json(ApiResponse::success(deployment)))
        }
        Err(e) => {
            error!("API: Failed to deploy SSH key {}: {}", key_id, e);
            Ok(Json(ApiResponse::error(e.to_string())))
        }
    }
}

/// Revoke SSH key handler
async fn revoke_ssh_key(
    State(manager): State<SshManagerState>,
    Path(key_id): Path<String>,
) -> Result<Json<ApiResponse<String>>, StatusCode> {
    info!("API: Revoking SSH key: {}", key_id);

    match manager.revoke_key(&key_id).await {
        Ok(()) => {
            info!("API: Successfully revoked SSH key: {}", key_id);
            Ok(Json(ApiResponse::success(format!(
                "Key {} revoked successfully",
                key_id
            ))))
        }
        Err(e) => {
            error!("API: Failed to revoke SSH key {}: {}", key_id, e);
            Ok(Json(ApiResponse::error(e.to_string())))
        }
    }
}

/// Get SSH key handler
async fn get_ssh_key(
    State(manager): State<SshManagerState>,
    Path(key_id): Path<String>,
) -> Result<Json<ApiResponse<super::TemporalSshKey>>, StatusCode> {
    info!("API: Getting SSH key: {}", key_id);

    match manager.get_key(&key_id).await {
        Ok(key) => Ok(Json(ApiResponse::success(key))),
        Err(e) => {
            error!("API: Failed to get SSH key {}: {}", key_id, e);
            Ok(Json(ApiResponse::error(e.to_string())))
        }
    }
}

/// List SSH keys handler
async fn list_ssh_keys(
    State(manager): State<SshManagerState>,
) -> Result<Json<ApiResponse<Vec<super::TemporalSshKey>>>, StatusCode> {
    info!("API: Listing SSH keys");

    match manager.list_keys(false).await {
        Ok(keys) => {
            info!("API: Found {} active SSH keys", keys.len());
            Ok(Json(ApiResponse::success(keys)))
        }
        Err(e) => {
            error!("API: Failed to list SSH keys: {}", e);
            Ok(Json(ApiResponse::error(e.to_string())))
        }
    }
}

/// Cleanup expired keys handler
async fn cleanup_expired_keys(
    State(manager): State<SshManagerState>,
) -> Result<Json<ApiResponse<CleanupResponse>>, StatusCode> {
    info!("API: Triggering manual cleanup of expired SSH keys");

    match manager.cleanup_expired().await {
        Ok(cleaned_keys) => {
            info!("API: Cleaned up {} expired SSH keys", cleaned_keys.len());
            Ok(Json(ApiResponse::success(CleanupResponse {
                cleaned_count: cleaned_keys.len(),
                cleaned_key_ids: cleaned_keys,
            })))
        }
        Err(e) => {
            error!("API: Failed to cleanup expired SSH keys: {}", e);
            Ok(Json(ApiResponse::error(e.to_string())))
        }
    }
}

/// Get SSH key statistics handler
async fn get_ssh_stats(
    State(manager): State<SshManagerState>,
) -> Result<Json<ApiResponse<super::SshKeyStats>>, StatusCode> {
    info!("API: Getting SSH key statistics");

    match manager.get_stats().await {
        Ok(stats) => Ok(Json(ApiResponse::success(stats))),
        Err(e) => {
            error!("API: Failed to get SSH key statistics: {}", e);
            Ok(Json(ApiResponse::error(e.to_string())))
        }
    }
}

/// Cleanup response
#[derive(Debug, Serialize)]
pub struct CleanupResponse {
    pub cleaned_count: usize,
    pub cleaned_key_ids: Vec<String>,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_api_response_success() {
        let response: ApiResponse<String> = ApiResponse::success("test".to_string());
        assert!(response.success);
        assert!(response.data.is_some());
        assert!(response.error.is_none());
    }

    #[test]
    fn test_api_response_error() {
        let response: ApiResponse<String> = ApiResponse::error("error message".to_string());
        assert!(!response.success);
        assert!(response.data.is_none());
        assert!(response.error.is_some());
    }

    #[test]
    fn test_generate_key_request_deserialization() {
        let json = r#"{
            "key_type": "dynamickeypair",
            "user": "root",
            "target_server": "test.example.com",
            "ttl_seconds": 3600
        }"#;

        let request: Result<GenerateKeyRequest, _> = serde_json::from_str(json);
        assert!(request.is_ok());

        let req = request.unwrap();
        assert_eq!(req.user, "root");
        assert_eq!(req.target_server, "test.example.com");
        assert_eq!(req.ttl_seconds, Some(3600));
    }
}
