//! Skeptic View DTOs ("Trust but Verify" response showing all competing claims).

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

use super::enums::ObjectValueDto;
use super::responses::SourceWarningDto;
use super::source_registry::SourceMetadataDto;

// ============================================================================
// Skeptic View DTOs ("Trust but Verify")
// ============================================================================

/// Query parameters for the skeptic endpoint.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
pub struct SkepticQueryParams {
    /// Subject entity to analyze (required)
    pub subject: String,

    /// Predicate/relation to analyze (required)
    pub predicate: String,

    /// Include human-readable source metadata in the response.
    /// When true, each source will include label, tier, tier_label, and url.
    #[serde(default)]
    pub include_source_metadata: bool,
}

/// Resolution status indicating level of agreement.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, ToSchema)]
pub enum ResolutionStatusDto {
    /// All assertions agree on the same value
    Unanimous,
    /// Strong majority (>75% weight) agrees
    Agreed,
    /// No clear consensus; significant disagreement exists
    Contested,
}

impl From<stemedb_core::types::ResolutionStatus> for ResolutionStatusDto {
    fn from(status: stemedb_core::types::ResolutionStatus) -> Self {
        match status {
            stemedb_core::types::ResolutionStatus::Unanimous => ResolutionStatusDto::Unanimous,
            stemedb_core::types::ResolutionStatus::Agreed => ResolutionStatusDto::Agreed,
            stemedb_core::types::ResolutionStatus::Contested => ResolutionStatusDto::Contested,
        }
    }
}

/// Summary of a single claim in the conflict analysis.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ClaimSummaryDto {
    /// The claimed value
    pub value: ObjectValueDto,

    /// Normalized weight (0.0 to 1.0) relative to all claims
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub weight_share: f32,

    /// Number of assertions making this exact claim
    pub assertion_count: u32,

    /// Hash of the representative assertion (hex-encoded)
    pub representative_hash: String,

    /// Source provenance
    pub source: SourceSummaryDto,

    /// Agents supporting this claim
    pub supporting_agents: Vec<AgentSummaryDto>,

    /// Warning if this claim cites a quarantined or deprecated source (P3.2).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_warning: Option<SourceWarningDto>,
}

/// Source provenance summary.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SourceSummaryDto {
    /// Hash of the source document (hex-encoded)
    pub source_hash: String,

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

    /// Human-readable source metadata (only present when include_source_metadata=true)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub source_metadata: Option<SourceMetadataDto>,
}

/// Agent summary with trust score.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct AgentSummaryDto {
    /// Agent's public key (hex-encoded)
    pub agent_id: String,

    /// Trust score at query time (0.0 to 1.0)
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub trust_score: f32,
}

/// Response from the skeptic endpoint.
///
/// This is the "Trust but Verify" response that shows all competing claims
/// instead of hiding conflict behind a single winner.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SkepticResponse {
    /// The subject that was queried
    pub subject: String,

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

    /// Overall resolution status
    pub status: ResolutionStatusDto,

    /// Conflict score (0.0 = unanimous, 1.0 = chaos)
    #[schema(minimum = 0.0, maximum = 1.0)]
    pub conflict_score: f32,

    /// All distinct claims, ranked by weight_share descending
    pub claims: Vec<ClaimSummaryDto>,

    /// Total number of candidate assertions considered
    pub candidates_count: usize,

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

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

// ============================================================================
// Conversions from internal types
// ============================================================================

impl From<stemedb_query::SkepticView> for SkepticResponse {
    fn from(view: stemedb_query::SkepticView) -> Self {
        SkepticResponse {
            subject: view.subject,
            predicate: view.predicate,
            status: view.analysis.status.into(),
            conflict_score: view.analysis.conflict_score,
            claims: view.analysis.claims.into_iter().map(Into::into).collect(),
            candidates_count: view.analysis.candidates_count,
            computed_at: view.computed_at,
            lens_name: view.lens_name,
        }
    }
}

impl From<stemedb_core::types::ClaimSummary> for ClaimSummaryDto {
    fn from(claim: stemedb_core::types::ClaimSummary) -> Self {
        ClaimSummaryDto {
            value: claim.value.into(),
            weight_share: claim.weight_share,
            assertion_count: claim.assertion_count,
            representative_hash: hex::encode(claim.representative_hash),
            source: SourceSummaryDto {
                source_hash: hex::encode(claim.source.source_hash),
                visual_hash: claim.source.visual_hash.map(hex::encode),
                source_metadata: None, // Enriched by handler when include_source_metadata=true
            },
            supporting_agents: claim.supporting_agents.into_iter().map(Into::into).collect(),
            source_warning: None, // Enriched by handler (P3.2)
        }
    }
}

impl From<stemedb_core::types::AgentSummary> for AgentSummaryDto {
    fn from(agent: stemedb_core::types::AgentSummary) -> Self {
        AgentSummaryDto { agent_id: hex::encode(agent.agent_id), trust_score: agent.trust_score }
    }
}
