//! Break-Glass REST API Handlers
//!
//! Provides HTTP endpoints for break-glass emergency access system

use std::net::IpAddr;
use std::sync::Arc;

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

use super::types::*;
use super::BreakGlassService;

/// Break-glass API state
#[derive(Clone)]
pub struct BreakGlassState {
    pub service: Arc<BreakGlassService>,
}

impl BreakGlassState {
    pub fn new(service: Arc<BreakGlassService>) -> Self {
        Self { service }
    }
}

/// Create break-glass router
pub fn create_router(service: Arc<BreakGlassService>) -> Router {
    let state = BreakGlassState::new(service);

    Router::new()
        // Request endpoints
        .route("/request", post(create_request))
        .route("/requests", get(list_requests))
        .route("/requests/{id}", get(get_request))
        // Approval endpoints
        .route("/requests/{id}/approve", post(approve_request))
        .route("/requests/{id}/deny", post(deny_request))
        // Session endpoints
        .route("/requests/{id}/activate", post(activate_session))
        .route("/sessions", get(list_sessions))
        .route("/sessions/{id}", get(get_session))
        .route("/sessions/{id}/revoke", post(revoke_session))
        // Action endpoints
        .route("/sessions/{id}/actions", post(record_action))
        // Audit endpoints
        .route("/audit", get(get_audit_logs))
        // Statistics endpoints
        .route("/statistics", get(get_statistics))
        .with_state(state)
}

// ============================================================================
// Request Handlers
// ============================================================================

/// Create break-glass request
async fn create_request(
    State(state): State<BreakGlassState>,
    Json(payload): Json<CreateRequestPayload>,
) -> Result<Json<CreateRequestResponse>, ApiError> {
    info!(
        requester = %payload.requester.email,
        reason = %payload.reason,
        "Creating break-glass request"
    );

    // Create request
    let request = BreakGlassRequest::new(
        payload.requester,
        payload.reason,
        payload.justification,
        payload.target_resources,
        payload.requested_permissions,
        chrono::Duration::hours(payload.duration_hours),
    );

    // Submit to approval manager
    let request_id = state
        .service
        .approval_manager()
        .submit_request(request.clone())
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(CreateRequestResponse {
        request_id,
        status: request.status,
        expires_at: request.expires_at,
    }))
}

/// List break-glass requests
async fn list_requests(
    State(state): State<BreakGlassState>,
    Query(params): Query<ListRequestsQuery>,
) -> Result<Json<Vec<BreakGlassRequest>>, ApiError> {
    let requests = if params.pending_only.unwrap_or(false) {
        state
            .service
            .approval_manager()
            .list_pending_requests()
            .await
    } else {
        state.service.approval_manager().list_all_requests().await
    };

    Ok(Json(requests))
}

/// Get specific request
async fn get_request(
    State(state): State<BreakGlassState>,
    Path(id): Path<String>,
) -> Result<Json<GetRequestResponse>, ApiError> {
    let (request, approvals) = state
        .service
        .approval_manager()
        .get_request(&id)
        .await
        .map_err(|e| ApiError::NotFound(e.to_string()))?;

    Ok(Json(GetRequestResponse { request, approvals }))
}

// ============================================================================
// Approval Handlers
// ============================================================================

/// Approve break-glass request
async fn approve_request(
    State(state): State<BreakGlassState>,
    Path(id): Path<String>,
    Json(payload): Json<ApproveRequestPayload>,
) -> Result<Json<ApprovalResponse>, ApiError> {
    info!(request_id = %id, approver = %payload.approver.email, "Approving request");

    let result = state
        .service
        .approval_manager()
        .approve_request(&id, payload.approver, payload.reason, payload.ip_address)
        .await
        .map_err(|e| ApiError::BadRequest(e.to_string()))?;

    match result {
        super::approval::ApprovalResult::Approved {
            request_id,
            approvals,
        } => Ok(Json(ApprovalResponse {
            approved: true,
            request_id,
            approvals_count: approvals.len() as u32,
            message: "Request fully approved".to_string(),
        })),
        super::approval::ApprovalResult::PartiallyApproved {
            request_id,
            current_approvals,
            required_approvals,
        } => Ok(Json(ApprovalResponse {
            approved: false,
            request_id,
            approvals_count: current_approvals,
            message: format!(
                "Approval recorded. {} of {} required approvals received",
                current_approvals, required_approvals
            ),
        })),
    }
}

/// Deny break-glass request
async fn deny_request(
    State(state): State<BreakGlassState>,
    Path(id): Path<String>,
    Json(payload): Json<DenyRequestPayload>,
) -> Result<StatusCode, ApiError> {
    info!(request_id = %id, denier = %payload.denier.email, "Denying request");

    state
        .service
        .approval_manager()
        .deny_request(&id, payload.denier, payload.reason)
        .await
        .map_err(|e| ApiError::BadRequest(e.to_string()))?;

    Ok(StatusCode::OK)
}

// ============================================================================
// Session Handlers
// ============================================================================

/// Activate approved session
async fn activate_session(
    State(state): State<BreakGlassState>,
    Path(id): Path<String>,
) -> Result<Json<EmergencyAccessToken>, ApiError> {
    info!(request_id = %id, "Activating emergency session");

    // Get request and approvals
    let (request, approvals) = state
        .service
        .approval_manager()
        .get_request(&id)
        .await
        .map_err(|e| ApiError::NotFound(e.to_string()))?;

    // Create session
    let session_id = state
        .service
        .session_manager()
        .create_session(request, approvals)
        .await
        .map_err(|e| ApiError::BadRequest(e.to_string()))?;

    // Activate session and generate token
    let token = state
        .service
        .session_manager()
        .activate_session(&session_id)
        .await
        .map_err(|e| ApiError::Internal(e.to_string()))?;

    Ok(Json(token))
}

/// List sessions
async fn list_sessions(
    State(state): State<BreakGlassState>,
    Query(params): Query<ListSessionsQuery>,
) -> Result<Json<Vec<BreakGlassSession>>, ApiError> {
    let sessions = if params.active_only.unwrap_or(false) {
        state.service.session_manager().list_active_sessions().await
    } else {
        state.service.session_manager().list_all_sessions().await
    };

    Ok(Json(sessions))
}

/// Get specific session
async fn get_session(
    State(state): State<BreakGlassState>,
    Path(id): Path<String>,
) -> Result<Json<BreakGlassSession>, ApiError> {
    let session = state
        .service
        .session_manager()
        .get_session(&id)
        .await
        .map_err(|e| ApiError::NotFound(e.to_string()))?;

    Ok(Json(session))
}

/// Revoke session
async fn revoke_session(
    State(state): State<BreakGlassState>,
    Path(id): Path<String>,
    Json(payload): Json<RevokeSessionPayload>,
) -> Result<StatusCode, ApiError> {
    info!(session_id = %id, reason = %payload.reason, "Revoking session");

    state
        .service
        .session_manager()
        .revoke_session(&id, payload.reason)
        .await
        .map_err(|e| ApiError::BadRequest(e.to_string()))?;

    Ok(StatusCode::OK)
}

/// Record action in session
async fn record_action(
    State(state): State<BreakGlassState>,
    Path(id): Path<String>,
    Json(payload): Json<RecordActionPayload>,
) -> Result<StatusCode, ApiError> {
    let action = EmergencyAction::new(payload.action_type, payload.resource, payload.parameters);

    state
        .service
        .session_manager()
        .record_action(&id, action)
        .await
        .map_err(|e| ApiError::BadRequest(e.to_string()))?;

    Ok(StatusCode::OK)
}

// ============================================================================
// Audit Handlers
// ============================================================================

/// Get audit logs
async fn get_audit_logs(
    State(_state): State<BreakGlassState>,
    Query(_params): Query<AuditLogsQuery>,
) -> Result<Json<Vec<BreakGlassAuditEvent>>, ApiError> {
    // TODO: Implement audit log retrieval from audit system
    Ok(Json(Vec::new()))
}

// ============================================================================
// Statistics Handlers
// ============================================================================

/// Get statistics
async fn get_statistics(
    State(state): State<BreakGlassState>,
) -> Result<Json<BreakGlassStatistics>, ApiError> {
    let approval_stats = state.service.approval_manager().get_statistics().await;

    let session_stats = state.service.session_manager().get_statistics().await;

    let revocation_stats = state.service.revocation_monitor().get_statistics().await;

    Ok(Json(BreakGlassStatistics {
        approval: approval_stats,
        session: session_stats,
        revocation: revocation_stats,
    }))
}

// ============================================================================
// Request/Response Types
// ============================================================================

#[derive(Debug, Deserialize)]
pub struct CreateRequestPayload {
    pub requester: User,
    pub reason: String,
    pub justification: String,
    pub target_resources: Vec<String>,
    pub requested_permissions: Vec<Permission>,
    pub duration_hours: i64,
}

#[derive(Debug, Serialize)]
pub struct CreateRequestResponse {
    pub request_id: String,
    pub status: RequestStatus,
    pub expires_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Deserialize)]
pub struct ListRequestsQuery {
    pub pending_only: Option<bool>,
}

#[derive(Debug, Serialize)]
pub struct GetRequestResponse {
    pub request: BreakGlassRequest,
    pub approvals: Vec<Approval>,
}

#[derive(Debug, Deserialize)]
pub struct ApproveRequestPayload {
    pub approver: User,
    pub reason: String,
    pub ip_address: IpAddr,
}

#[derive(Debug, Serialize)]
pub struct ApprovalResponse {
    pub approved: bool,
    pub request_id: String,
    pub approvals_count: u32,
    pub message: String,
}

#[derive(Debug, Deserialize)]
pub struct DenyRequestPayload {
    pub denier: User,
    pub reason: String,
}

#[derive(Debug, Deserialize)]
pub struct ListSessionsQuery {
    pub active_only: Option<bool>,
}

#[derive(Debug, Deserialize)]
pub struct RevokeSessionPayload {
    pub reason: String,
}

#[derive(Debug, Deserialize)]
pub struct RecordActionPayload {
    pub action_type: String,
    pub resource: String,
    pub parameters: serde_json::Value,
}

#[derive(Debug, Deserialize)]
pub struct AuditLogsQuery {
    pub from: Option<chrono::DateTime<chrono::Utc>>,
    pub to: Option<chrono::DateTime<chrono::Utc>>,
    pub session_id: Option<String>,
    pub event_type: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct BreakGlassStatistics {
    pub approval: super::approval::ApprovalStatistics,
    pub session: super::session::SessionStatistics,
    pub revocation: super::revocation::RevocationStatistics,
}

// ============================================================================
// Error Types
// ============================================================================

#[derive(Debug)]
pub enum ApiError {
    BadRequest(String),
    NotFound(String),
    Internal(String),
}

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let (status, message) = match self {
            ApiError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
            ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
            ApiError::Internal(msg) => {
                error!("Internal error: {}", msg);
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    "Internal server error".to_string(),
                )
            }
        };

        let body = Json(serde_json::json!({
            "error": message,
            "status": status.as_u16(),
        }));

        (status, body).into_response()
    }
}

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

    #[test]
    fn test_api_error_response() {
        let err = ApiError::BadRequest("Invalid input".to_string());
        let response = err.into_response();
        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
    }
}
