//! Handler for assertion supersession endpoint.
//!
//! The supersede endpoint enables error correction without violating append-only
//! semantics. Instead of mutating an assertion, we create a supersession record
//! that points from the old (target) to the new (replacement) assertion.
//!
//! # Use Case: 3am Incident Investigation
//!
//! 1. SRE finds agent deployed bad config
//! 2. Trace shows agent queried assertion X with hash `abc123`
//! 3. Supervisor creates supersession: `abc123` → `new_hash` (Invalidate)
//! 4. Future queries skip `abc123` automatically
//! 5. Audit trail preserved: "Who fixed it? When? Why?"

use axum::{extract::State, http::StatusCode, Json};
use tracing::instrument;

use crate::{
    dto::{SupersedeRequest, SupersedeResponse},
    error::{ApiError, Result},
    hex,
    state::AppState,
};

use stemedb_core::types::{Supersession, SupersessionType};
use stemedb_storage::{GenericSupersessionStore, SupersessionStore};

/// Create a supersession record for error correction.
///
/// This endpoint accepts a supersession request, validates the fields,
/// and stores the supersession record. Future queries will automatically
/// filter out superseded assertions based on type.
///
/// # Request Body
///
/// - `target_hash`: Hash of the assertion being superseded (hex-encoded)
/// - `supersession_type`: How the target is being superseded (Invalidate, Temporal, etc.)
/// - `reason`: Human-readable explanation for audit trail
/// - `new_hash`: Hash of replacement assertion (optional)
/// - `agent_id`: Public key of agent creating the supersession
/// - `signature`: Signature over the supersession content
///
/// # Example
///
/// ```text
/// POST /v1/supersede
/// {
///   "target_hash": "abc123...",
///   "supersession_type": "Invalidate",
///   "reason": "Proposal treated as approved. See incident INC-2024-001",
///   "new_hash": "def456...",
///   "agent_id": "deadbeef...",
///   "signature": "..."
/// }
/// ```
#[utoipa::path(
    post,
    path = "/v1/supersede",
    request_body = SupersedeRequest,
    responses(
        (status = 201, description = "Supersession recorded successfully", body = SupersedeResponse),
        (status = 400, description = "Invalid request parameters", body = crate::dto::ErrorResponse),
        (status = 404, description = "Target assertion not found", body = crate::dto::ErrorResponse),
        (status = 409, description = "Assertion already superseded", body = crate::dto::ErrorResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse)
    ),
    tag = "supersession"
)]
#[instrument(skip(state), fields(
    target_hash = %req.target_hash,
    supersession_type = ?req.supersession_type,
    agent_id = %req.agent_id
))]
pub async fn supersede(
    State(state): State<AppState>,
    Json(req): Json<SupersedeRequest>,
) -> Result<(StatusCode, Json<SupersedeResponse>)> {
    // Decode and validate hex fields
    let target_hash = hex::decode_hash_32(&req.target_hash)?;
    let agent_id = hex::decode_agent_id(&req.agent_id)?;
    let signature = hex::decode_signature(&req.signature)?;

    let new_hash = req.new_hash.map(|h| hex::decode_hash_32(&h)).transpose()?;

    // Validate reason is not empty
    if req.reason.trim().is_empty() {
        return Err(ApiError::InvalidRequest(
            "Reason is required for supersession audit trail".to_string(),
        ));
    }

    // Get current timestamp
    let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map_err(|e| ApiError::InvalidRequest(format!("System clock error: {}", e)))?
        .as_secs();

    // Create supersession store
    let supersession_store = GenericSupersessionStore::new(state.store.clone());

    // Check if already superseded
    if supersession_store
        .is_superseded(&target_hash)
        .await
        .map_err(|e| ApiError::Internal(format!("Failed to check supersession status: {}", e)))?
    {
        return Err(ApiError::Conflict(format!(
            "Assertion {} is already superseded",
            req.target_hash
        )));
    }

    // Convert DTO type to core type
    let supersession_type: SupersessionType = req.supersession_type.into();

    // Create supersession record
    // NOTE: hlc_timestamp is None for API-created supersessions. In distributed mode,
    // supersessions flow through the IngestWorker which generates HLC timestamps.
    // Direct API creation is for single-node deployments or manual corrections.
    let supersession = Supersession {
        target_hash,
        supersession_type,
        reason: req.reason.clone(),
        new_hash,
        timestamp,
        hlc_timestamp: None, // Single-node mode; distributed mode uses IngestWorker
        agent_id,
        signature,
    };

    // Store the supersession
    supersession_store
        .put_supersession(&supersession)
        .await
        .map_err(|e| ApiError::Internal(format!("Failed to store supersession: {}", e)))?;

    // Build response
    let response = SupersedeResponse {
        status: "superseded".to_string(),
        target_hash: req.target_hash,
        supersession_type: req.supersession_type,
        timestamp,
    };

    Ok((StatusCode::CREATED, Json(response)))
}

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

    #[test]
    fn test_supersession_type_conversion() {
        // Test all DTO types convert correctly
        assert_eq!(
            SupersessionType::from(SupersessionTypeDto::Invalidate),
            SupersessionType::Invalidate
        );
        assert_eq!(
            SupersessionType::from(SupersessionTypeDto::Temporal),
            SupersessionType::Temporal
        );
        assert_eq!(
            SupersessionType::from(SupersessionTypeDto::Refinement),
            SupersessionType::Refinement
        );
        assert_eq!(
            SupersessionType::from(SupersessionTypeDto::RequiresReview),
            SupersessionType::RequiresReview
        );
        assert_eq!(
            SupersessionType::from(SupersessionTypeDto::Additive),
            SupersessionType::Additive
        );
    }
}
