//! HTTP handlers for circuit breaker management (Phase 7D).
//!
//! # Security Warning
//!
//! These admin endpoints do NOT include authentication middleware.
//! In production deployments, these endpoints MUST be protected by one of:
//!
//! 1. **Network-level protection**: Run admin endpoints on a separate port
//!    that is only accessible from trusted networks (e.g., internal VPN).
//!
//! 2. **Reverse proxy authentication**: Use nginx/envoy/etc. to require
//!    authentication before requests reach these endpoints.
//!
//! 3. **Custom middleware**: Implement an `admin_auth` middleware layer
//!    that validates admin API keys or JWT tokens.
//!
//! Failing to protect these endpoints allows anyone to reset circuit
//! breakers, potentially allowing misbehaving agents to continue attacking.

use crate::{
    dto::{
        CircuitBreakerStatusResponse, ErrorResponse, ResetCircuitRequest, ResetCircuitResponse,
        TrippedCircuitsParams, TrippedCircuitsResponse,
    },
    AppState,
};
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    Json,
};
use stemedb_storage::CircuitBreakerStore;
use tracing::instrument;

/// GET /v1/admin/circuit-breaker/{agent_id}
///
/// Get the circuit breaker status for a specific agent.
#[utoipa::path(
    get,
    path = "/v1/admin/circuit-breaker/{agent_id}",
    params(
        ("agent_id" = String, Path, description = "Hex-encoded agent ID (64 characters)")
    ),
    responses(
        (status = 200, description = "Circuit breaker status retrieved", body = CircuitBreakerStatusResponse),
        (status = 400, description = "Invalid agent ID format", body = ErrorResponse),
        (status = 500, description = "Internal server error", body = ErrorResponse)
    ),
    tag = "admin"
)]
#[instrument(skip(state))]
pub async fn get_circuit_status(
    State(state): State<AppState>,
    Path(agent_id_hex): Path<String>,
) -> std::result::Result<Json<CircuitBreakerStatusResponse>, (StatusCode, Json<ErrorResponse>)> {
    let agent_id = parse_agent_id(&agent_id_hex)?;
    let store = &state.circuit_breaker_store;

    let current_time = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);

    let record = store.get_circuit(&agent_id).await.map_err(|e| {
        tracing::error!(error = %e, agent_id = %agent_id_hex, "Failed to get circuit breaker status");
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to retrieve circuit breaker status".to_string(),
                code: "CIRCUIT_BREAKER_RETRIEVAL_ERROR".to_string(),
            }),
        )
    })?;

    let response = match record {
        Some(record) => {
            let retry_after = store.retry_after(&agent_id, current_time).await.map_err(|e| {
                tracing::error!(error = %e, "Failed to get retry_after");
                (
                    StatusCode::INTERNAL_SERVER_ERROR,
                    Json(ErrorResponse {
                        error: "Failed to get retry information".to_string(),
                        code: "CIRCUIT_BREAKER_ERROR".to_string(),
                    }),
                )
            })?;
            CircuitBreakerStatusResponse::from_record(&record, retry_after)
        }
        None => CircuitBreakerStatusResponse::good_standing(&agent_id),
    };

    Ok(Json(response))
}

/// POST /v1/admin/circuit-breaker/reset
///
/// Manually reset a circuit breaker (admin operation).
#[utoipa::path(
    post,
    path = "/v1/admin/circuit-breaker/reset",
    request_body = ResetCircuitRequest,
    responses(
        (status = 200, description = "Circuit breaker reset successfully", body = ResetCircuitResponse),
        (status = 400, description = "Invalid agent ID format", body = ErrorResponse),
        (status = 500, description = "Internal server error", body = ErrorResponse)
    ),
    tag = "admin"
)]
#[instrument(skip(state))]
pub async fn reset_circuit(
    State(state): State<AppState>,
    Json(request): Json<ResetCircuitRequest>,
) -> std::result::Result<Json<ResetCircuitResponse>, (StatusCode, Json<ErrorResponse>)> {
    let agent_id = parse_agent_id(&request.agent_id)?;
    let store = &state.circuit_breaker_store;

    store.reset_circuit(&agent_id).await.map_err(|e| {
        tracing::error!(error = %e, agent_id = %request.agent_id, "Failed to reset circuit breaker");
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to reset circuit breaker".to_string(),
                code: "CIRCUIT_BREAKER_RESET_ERROR".to_string(),
            }),
        )
    })?;

    tracing::info!(agent_id = %request.agent_id, "Circuit breaker reset");

    Ok(Json(ResetCircuitResponse {
        agent_id: request.agent_id,
        message: "Circuit breaker reset successfully".to_string(),
    }))
}

/// GET /v1/admin/circuit-breakers/tripped
///
/// List all tripped (Open or HalfOpen) circuit breakers.
#[utoipa::path(
    get,
    path = "/v1/admin/circuit-breakers/tripped",
    params(
        ("limit" = Option<usize>, Query, description = "Maximum number of circuits to return (default: 100)")
    ),
    responses(
        (status = 200, description = "Tripped circuits retrieved", body = TrippedCircuitsResponse),
        (status = 500, description = "Internal server error", body = ErrorResponse)
    ),
    tag = "admin"
)]
#[instrument(skip(state))]
pub async fn list_tripped_circuits(
    State(state): State<AppState>,
    Query(params): Query<TrippedCircuitsParams>,
) -> std::result::Result<Json<TrippedCircuitsResponse>, (StatusCode, Json<ErrorResponse>)> {
    let store = &state.circuit_breaker_store;

    let current_time = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);

    let tripped = store.list_tripped(params.limit).await.map_err(|e| {
        tracing::error!(error = %e, "Failed to list tripped circuit breakers");
        (
            StatusCode::INTERNAL_SERVER_ERROR,
            Json(ErrorResponse {
                error: "Failed to list tripped circuit breakers".to_string(),
                code: "CIRCUIT_BREAKER_LIST_ERROR".to_string(),
            }),
        )
    })?;

    // Build responses with retry_after info
    let mut circuits = Vec::with_capacity(tripped.len());
    for record in &tripped {
        let retry_after = store.retry_after(&record.agent_id, current_time).await.ok().flatten();
        circuits.push(CircuitBreakerStatusResponse::from_record(record, retry_after));
    }

    let count = circuits.len();

    Ok(Json(TrippedCircuitsResponse { circuits, count }))
}

/// Parse and validate a hex-encoded agent ID.
fn parse_agent_id(
    agent_id_hex: &str,
) -> std::result::Result<[u8; 32], (StatusCode, Json<ErrorResponse>)> {
    let agent_bytes = hex::decode(agent_id_hex).map_err(|_| {
        (
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Invalid agent ID format (must be hex)".to_string(),
                code: "INVALID_AGENT_ID_FORMAT".to_string(),
            }),
        )
    })?;

    if agent_bytes.len() != 32 {
        return Err((
            StatusCode::BAD_REQUEST,
            Json(ErrorResponse {
                error: "Agent ID must be 32 bytes (64 hex characters)".to_string(),
                code: "INVALID_AGENT_ID_LENGTH".to_string(),
            }),
        ));
    }

    let mut agent_id = [0u8; 32];
    agent_id.copy_from_slice(&agent_bytes);
    Ok(agent_id)
}
