//! Request DTOs for creating assertions, votes, epochs, and supersessions.

use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use super::enums::{
    LifecycleDto, ObjectValueDto, SignatureDto, SourceClassDto, SupersessionTypeDto,
};

// ============================================================================
// Request DTOs
// ============================================================================

/// Request to create a new assertion.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CreateAssertionRequest {
    /// The subject entity (e.g., "Tesla_Inc")
    pub subject: String,

    /// The predicate/relation (e.g., "has_revenue")
    pub predicate: String,

    /// The object value
    pub object: ObjectValueDto,

    /// Confidence score (0.0 to 1.0)
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub confidence: f32,

    /// Agent signatures vouching for this assertion
    pub signatures: Vec<SignatureDto>,

    /// Hash of parent assertion (hex-encoded, optional for forks)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parent_hash: Option<String>,

    /// Hash of source evidence (hex-encoded)
    pub source_hash: String,

    /// Source authority tier (defaults to Expert if not specified)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_class: Option<SourceClassDto>,

    /// Perceptual hash for visual anchoring (hex-encoded, optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub visual_hash: Option<String>,

    /// Epoch ID (hex-encoded, optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub epoch: Option<String>,

    /// Lifecycle stage (defaults to Proposed if not specified)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lifecycle: Option<LifecycleDto>,

    /// Semantic embedding vector (optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub vector: Option<Vec<f32>>,

    /// Structured source metadata as a JSON string.
    /// Stored as bytes internally for rkyv compatibility.
    /// Schema is domain-specific (journal info, social metrics, etc.).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_metadata: Option<String>,

    /// Unix timestamp when the assertion was created.
    /// If not provided, defaults to the current time.
    /// **Important for v2 signatures:** Provide this field to preserve the
    /// timestamp that was signed. If omitted and the assertion has v2
    /// signatures, signature verification will fail.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp: Option<u64>,

    /// HLC (Hybrid Logical Clock) timestamp for distributed causal ordering.
    /// If not provided, defaults to a zero timestamp.
    /// **Important for v2 signatures:** Provide this field to preserve the
    /// HLC timestamp that was signed.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub hlc_timestamp: Option<HlcTimestampDto>,
}

/// HLC timestamp DTO for HTTP API.
///
/// Represents a Hybrid Logical Clock timestamp with NTP64 time
/// and node ID for distributed causal ordering.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct HlcTimestampDto {
    /// NTP64-encoded time (u64)
    pub time_ntp64: u64,

    /// Node ID (hex-encoded, 16 bytes)
    pub node_id: String,
}

/// Request to create a new vote on an assertion.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CreateVoteRequest {
    /// Hash of the assertion being voted on (hex-encoded)
    pub assertion_hash: String,

    /// Public key of the voting agent (hex-encoded, 32 bytes)
    pub agent_id: String,

    /// Weight of the vote (0.0 to 1.0)
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub weight: f32,

    /// Signature over the assertion hash (hex-encoded, 64 bytes)
    pub signature: String,

    /// The URL where the claim was observed (optional).
    /// Enables votes as cryptographic witnesses: "I saw this at this URL at this time."
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_url: Option<String>,

    /// Optional context (page snippet, etc.) as a JSON string.
    /// Stored as bytes internally for rkyv compatibility (same pattern as Assertion's source_metadata).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub observed_context: Option<String>,
}

/// Request to create a new epoch.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CreateEpochRequest {
    /// Human-readable name (e.g., "Pre-2024", "Newtonian")
    pub name: String,

    /// ID of the epoch this supersedes (hex-encoded, optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub supersedes: Option<String>,

    /// How this epoch supersedes the previous one (optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub supersession_type: Option<SupersessionTypeDto>,

    /// Start timestamp (Unix epoch, optional - defaults to now)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub start_timestamp: Option<u64>,
}

/// Request to supersede an assertion (error correction).
///
/// Supersession 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.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SupersedeRequest {
    /// Hash of the assertion being superseded (hex-encoded, 32 bytes)
    pub target_hash: String,

    /// How the target is being superseded
    pub supersession_type: SupersessionTypeDto,

    /// Human-readable explanation (for audit trail)
    pub reason: String,

    /// Hash of the replacement assertion (hex-encoded, 32 bytes, optional)
    /// None for RequiresReview (flagging, not replacing) or pure invalidation.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub new_hash: Option<String>,

    /// Public key of the agent creating the supersession (hex-encoded, 32 bytes)
    pub agent_id: String,

    /// Signature over the supersession content (hex-encoded, 64 bytes)
    /// Signs: BLAKE3(target_hash || type || reason || new_hash || timestamp)
    pub signature: String,
}

/// Response from creating a supersession.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SupersedeResponse {
    /// Status message
    pub status: String,

    /// Hash of the target assertion that was superseded (hex-encoded)
    pub target_hash: String,

    /// Type of supersession applied
    pub supersession_type: SupersessionTypeDto,

    /// Unix timestamp when the supersession was recorded
    pub timestamp: u64,
}

/// Response from a create operation.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CreateResponse {
    /// Content-addressed hash of the created resource (hex-encoded)
    pub hash: String,

    /// Status message
    pub status: String,
}

/// Request to store a source document.
///
/// Source documents are content-addressed: the same content always produces
/// the same hash. Re-uploading identical content is idempotent.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct StoreSourceRequest {
    /// The source document content (base64-encoded for binary safety).
    /// Maximum size: 10MB after decoding.
    #[schema(example = "VGhpcyBpcyBhIHNvdXJjZSBkb2N1bWVudC4=")]
    pub content: String,

    /// MIME type of the content (e.g., "application/pdf", "text/plain").
    #[schema(example = "text/plain")]
    pub content_type: String,
}

/// Response from storing a source document.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct StoreSourceResponse {
    /// BLAKE3 hash of the content (hex-encoded, 64 chars = 32 bytes).
    /// Use this as `source_hash` when creating assertions.
    pub hash: String,

    /// Size of the stored content in bytes.
    pub size: usize,

    /// Status message ("stored").
    pub status: String,
}

/// Request to trigger trust rank decay.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct DecayTrustRanksRequest {
    /// Unix timestamp to use as "now" for decay calculation.
    /// If not provided, uses the current system time.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub now: Option<u64>,

    /// Half-life duration in seconds for confidence decay.
    /// If not provided, uses the default of 30 days (2,592,000 seconds).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub half_life_seconds: Option<u64>,
}

/// Response from the decay trust ranks operation.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct DecayTrustRanksResponse {
    /// Number of TrustRanks that were actually decayed (score changed).
    pub decayed_count: usize,

    /// Unix timestamp used for decay calculation.
    pub timestamp_used: u64,

    /// Half-life used for decay (in seconds).
    pub half_life_used: u64,

    /// Status message.
    pub status: String,
}
