//! DTOs for circuit breaker management (Phase 7D).

use serde::{Deserialize, Serialize};
use stemedb_storage::{CircuitBreakerRecord, CircuitState, FailureType};
use utoipa::ToSchema;

/// Circuit state (DTO).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum CircuitStateDto {
    /// Normal operation - requests allowed.
    Closed,
    /// Circuit tripped - requests blocked.
    Open,
    /// Testing state after timeout - one request allowed.
    HalfOpen,
}

impl From<CircuitState> for CircuitStateDto {
    fn from(state: CircuitState) -> Self {
        match state {
            CircuitState::Closed => CircuitStateDto::Closed,
            CircuitState::Open => CircuitStateDto::Open,
            CircuitState::HalfOpen => CircuitStateDto::HalfOpen,
        }
    }
}

/// Failure type (DTO).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum FailureTypeDto {
    /// Invalid cryptographic signature.
    InvalidSignature,
    /// Malformed input or validation failure.
    InputValidation,
    /// Invalid proof-of-work solution.
    PowError,
    /// Quota limit exceeded.
    QuotaExceeded,
    /// General application error.
    ApplicationError,
}

impl From<FailureType> for FailureTypeDto {
    fn from(failure_type: FailureType) -> Self {
        match failure_type {
            FailureType::InvalidSignature => FailureTypeDto::InvalidSignature,
            FailureType::InputValidation => FailureTypeDto::InputValidation,
            FailureType::PowError => FailureTypeDto::PowError,
            FailureType::QuotaExceeded => FailureTypeDto::QuotaExceeded,
            FailureType::ApplicationError => FailureTypeDto::ApplicationError,
        }
    }
}

/// A single failure event (DTO).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct FailureEventDto {
    /// Type of failure.
    pub failure_type: FailureTypeDto,
    /// Unix timestamp (seconds) when the failure occurred.
    pub timestamp: u64,
}

/// Circuit breaker status response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CircuitBreakerStatusResponse {
    /// Hex-encoded agent ID.
    pub agent_id: String,
    /// Current circuit state.
    pub state: CircuitStateDto,
    /// Human-readable state name.
    pub state_name: String,
    /// Number of failures in the current window.
    pub failure_count: usize,
    /// Total number of times this circuit has tripped.
    pub trip_count: u64,
    /// Unix timestamp (seconds) when the circuit was last tripped.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub last_trip_time: Option<u64>,
    /// Unix timestamp (seconds) of the most recent failure.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub last_failure_time: Option<u64>,
    /// Seconds until the agent can retry (only when Open).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub retry_after_secs: Option<u64>,
    /// Recent failures within the window.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub recent_failures: Vec<FailureEventDto>,
    /// Failure counts by type.
    pub failure_counts_by_type: FailureCountsDto,
}

/// Failure counts by type.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct FailureCountsDto {
    /// Number of invalid signature failures.
    pub invalid_signature: usize,
    /// Number of input validation failures.
    pub input_validation: usize,
    /// Number of PoW failures.
    pub pow_error: usize,
    /// Number of quota exceeded failures.
    pub quota_exceeded: usize,
    /// Number of application error failures.
    pub application_error: usize,
}

impl CircuitBreakerStatusResponse {
    /// Create a response for a non-existent circuit (agent in good standing).
    pub fn good_standing(agent_id: &[u8; 32]) -> Self {
        Self {
            agent_id: hex::encode(agent_id),
            state: CircuitStateDto::Closed,
            state_name: "closed".to_string(),
            failure_count: 0,
            trip_count: 0,
            last_trip_time: None,
            last_failure_time: None,
            retry_after_secs: None,
            recent_failures: Vec::new(),
            failure_counts_by_type: FailureCountsDto {
                invalid_signature: 0,
                input_validation: 0,
                pow_error: 0,
                quota_exceeded: 0,
                application_error: 0,
            },
        }
    }

    /// Create a response from a circuit breaker record.
    pub fn from_record(record: &CircuitBreakerRecord, retry_after: Option<u64>) -> Self {
        let recent_failures = record
            .failures
            .iter()
            .map(|f| FailureEventDto {
                failure_type: f.failure_type.into(),
                timestamp: f.timestamp,
            })
            .collect();

        Self {
            agent_id: hex::encode(record.agent_id),
            state: record.state.into(),
            state_name: record.state.name().to_string(),
            failure_count: record.failure_count(),
            trip_count: record.trip_count,
            last_trip_time: record.last_trip_time,
            last_failure_time: record.last_failure_time,
            retry_after_secs: retry_after,
            recent_failures,
            failure_counts_by_type: FailureCountsDto {
                invalid_signature: record.count_failures_by_type(FailureType::InvalidSignature),
                input_validation: record.count_failures_by_type(FailureType::InputValidation),
                pow_error: record.count_failures_by_type(FailureType::PowError),
                quota_exceeded: record.count_failures_by_type(FailureType::QuotaExceeded),
                application_error: record.count_failures_by_type(FailureType::ApplicationError),
            },
        }
    }
}

/// Request to reset a circuit breaker.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ResetCircuitRequest {
    /// Hex-encoded agent ID to reset.
    pub agent_id: String,
}

/// Response for resetting a circuit breaker.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ResetCircuitResponse {
    /// Hex-encoded agent ID that was reset.
    pub agent_id: String,
    /// Success message.
    pub message: String,
}

/// Response for listing tripped circuit breakers.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct TrippedCircuitsResponse {
    /// List of tripped circuits.
    pub circuits: Vec<CircuitBreakerStatusResponse>,
    /// Total count of tripped circuits.
    pub count: usize,
}

/// Query parameters for listing tripped circuits.
#[derive(Debug, Deserialize, ToSchema)]
pub struct TrippedCircuitsParams {
    /// Maximum number of circuits to return (default: 100).
    #[serde(default = "default_limit")]
    pub limit: usize,
}

fn default_limit() -> usize {
    100
}
