//! Response DTOs for query results and API operations.

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

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

// ============================================================================
// Source Status Warning (P3.2 Cascade Flagging)
// ============================================================================

/// Warning attached to assertions citing non-Active sources.
///
/// Enables "show with warning" behavior per P3.2 requirements.
/// When a source is deprecated or quarantined, assertions citing it
/// receive this warning instead of being silently filtered.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SourceWarningDto {
    /// Warning type: "deprecated" or "quarantined"
    pub warning_type: String,

    /// Human-readable explanation
    pub message: String,

    /// Source label from registry (if available)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_label: Option<String>,

    /// When the source status was last updated (Unix timestamp)
    pub status_updated_at: u64,
}

// ============================================================================
// Response DTOs
// ============================================================================

/// Response containing a single assertion.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct AssertionResponse {
    /// Content-addressed hash of this assertion (hex-encoded)
    pub hash: String,

    /// The subject entity
    pub subject: String,

    /// The predicate/relation
    pub predicate: String,

    /// The object value
    pub object: ObjectValueDto,

    /// Hash of parent assertion (hex-encoded, optional)
    #[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
    pub source_class: 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
    pub lifecycle: LifecycleDto,

    /// Agent signatures
    pub signatures: Vec<SignatureDto>,

    /// Confidence score (0.0 to 1.0)
    pub confidence: f32,

    /// Creation timestamp (Unix epoch)
    pub timestamp: u64,

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

    /// Structured source metadata as a JSON string (optional).
    /// Domain-specific schema (journal, DOI, sample_size, etc.).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_metadata: Option<String>,

    /// Warning if this assertion cites a quarantined or deprecated source.
    ///
    /// Present when the assertion's source has a non-Active status in the
    /// Source Registry. Enables "show with warning" behavior for enterprise
    /// compliance workflows.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_warning: Option<SourceWarningDto>,
}

/// Response from a query operation.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct QueryResponse {
    /// Matching assertions
    pub assertions: Vec<AssertionResponse>,

    /// Total number of results returned
    pub total_count: usize,

    /// Whether there are more results beyond the limit
    pub has_more: bool,

    /// Degree of disagreement among candidates (0.0 = unanimous, 1.0 = max conflict).
    /// See `stemedb_lens::compute_conflict_score()` for the canonical algorithm.
    /// Only present when a lens is applied.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub conflict_score: Option<f32>,

    /// Confidence in the resolution (0.0 to 1.0).
    /// Only present when a lens is applied.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resolution_confidence: Option<f32>,

    /// Changelog entries for MV changes since the `since` parameter.
    /// Only present when `since` parameter was provided.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub changes_since: Option<Vec<ChangeEntryDto>>,
}

/// A changelog entry from a "since" query.
///
/// Represents a single change to a materialized view's winner.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ChangeEntryDto {
    /// Unix timestamp when the change occurred.
    pub timestamp: u64,

    /// Hash of the previous winner (hex-encoded, None if first MV).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub previous_winner_hash: Option<String>,

    /// Hash of the new winner (hex-encoded).
    pub new_winner_hash: String,

    /// Subject of the changed pair.
    pub subject: String,

    /// Predicate of the changed pair.
    pub predicate: String,

    /// Which lens was used for resolution.
    pub lens_name: String,
}

/// Error response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ErrorResponse {
    /// Human-readable error message
    pub error: String,

    /// Machine-readable error code
    pub code: String,
}

/// Health check response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct HealthResponse {
    /// Service status (e.g., "healthy")
    pub status: String,

    /// API version
    pub version: String,

    /// Total number of assertions in the database
    pub assertions_count: u64,
}

/// Response from retrieving a source document.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProvenanceResponse {
    /// BLAKE3 hash of the content (hex-encoded).
    pub hash: String,

    /// The source document content (base64-encoded).
    pub content: String,

    /// MIME type of the content.
    pub content_type: String,

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

/// Per-tier resolution result from LayeredConsensus lens.
///
/// Represents the consensus within a single source class tier.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct TierResolutionDto {
    /// The tier number (0-5). Lower = higher authority.
    pub tier: u8,

    /// The source class for this tier.
    pub source_class: SourceClassDto,

    /// The winning assertion from within-tier consensus, if any candidates.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub winner: Option<AssertionResponse>,

    /// Number of candidates in this tier.
    pub candidates_count: usize,

    /// Within-tier conflict score (0.0 = unanimous, 1.0 = max conflict).
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub conflict_score: f32,

    /// Within-tier resolution confidence (0.0 to 1.0).
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub resolution_confidence: f32,
}

/// Response from a LayeredConsensus query.
///
/// Provides per-tier resolution results plus an overall winner.
/// Use this to see "What does Tier 0 say? What does Tier 5 say?"
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct LayeredQueryResponse {
    /// The subject that was queried.
    pub subject: String,

    /// The predicate that was queried.
    pub predicate: String,

    /// Per-tier consensus results, ordered by tier (0 = highest authority first).
    /// Only tiers with at least one candidate are included.
    pub tiers: Vec<TierResolutionDto>,

    /// Overall winner: winner from the highest-authority tier that has candidates.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub overall_winner: Option<AssertionResponse>,

    /// Cross-tier disagreement score (0.0 = tiers agree, 1.0 = tiers disagree).
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub overall_conflict_score: f32,

    /// Total candidates considered across all tiers.
    pub total_candidates: usize,

    /// Unix timestamp when this view was computed.
    pub computed_at: u64,

    /// Which lens was used (always "LayeredConsensus").
    pub lens_name: String,
}
