//! Audit trail DTOs for query logging and tracing.

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

use super::enums::LifecycleDto;

// ============================================================================
// Query Audit DTOs
// ============================================================================

/// Query parameters for listing query audits.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
#[serde(default)]
pub struct AuditQueryParams {
    /// Filter by agent public key (hex-encoded, 32 bytes)
    pub agent_id: Option<String>,

    /// Filter by subject (case-insensitive substring match)
    pub subject: Option<String>,

    /// Filter by predicate (case-insensitive substring match)
    pub predicate: Option<String>,

    /// Start of time range (Unix timestamp)
    pub from: Option<u64>,

    /// End of time range (Unix timestamp)
    pub to: Option<u64>,

    /// Maximum number of results to return (defaults to 100)
    #[serde(default = "default_limit")]
    pub limit: usize,
}

fn default_limit() -> usize {
    100
}

impl Default for AuditQueryParams {
    fn default() -> Self {
        Self { agent_id: None, subject: None, predicate: None, from: None, to: None, limit: 100 }
    }
}

/// Response containing a query audit record.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct QueryAuditResponse {
    /// Unique identifier for this query (hex-encoded, 32 bytes)
    pub query_id: String,

    /// Agent public key who made the query (hex-encoded, optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub agent_id: Option<String>,

    /// Unix timestamp when the query was executed
    pub timestamp: u64,

    /// Query parameters that were used
    pub params: QueryParamsAuditDto,

    /// Hash of the winning assertion (hex-encoded, optional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub result_hash: Option<String>,

    /// Confidence score of the resolution (0.0 to 1.0)
    pub result_confidence: f32,

    /// Assertions that contributed to this result
    pub contributing_assertions: Vec<ContributingAssertionDto>,
}

/// Query parameters captured for audit purposes.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct QueryParamsAuditDto {
    /// Filter by subject entity
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subject: Option<String>,

    /// Filter by predicate/relation
    #[serde(skip_serializing_if = "Option::is_none")]
    pub predicate: Option<String>,

    /// Filter by lifecycle stage
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lifecycle: Option<LifecycleDto>,

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

    /// Lens used for conflict resolution
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lens: Option<String>,
}

/// An assertion that contributed to a query result.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ContributingAssertionDto {
    /// Hash of the assertion (hex-encoded)
    pub assertion_hash: String,

    /// How much this assertion influenced the result (0.0 to 1.0)
    pub weight: f32,

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

    /// Lifecycle stage of this assertion
    pub lifecycle: LifecycleDto,
}

/// Response from listing query audits.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct QueryAuditListResponse {
    /// List of query audit records
    pub audits: Vec<QueryAuditResponse>,

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

/// Query parameters for the trace endpoint.
///
/// Returns a filtered list of query audits for debugging agent decision-making.
/// All parameters are optional filters that narrow down results.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
#[serde(default)]
pub struct TraceParams {
    /// Filter by agent public key (hex-encoded, 32 bytes) - required
    pub agent_id: String,

    /// Start of time range (Unix timestamp or ISO8601) - required
    pub from: String,

    /// End of time range (Unix timestamp or ISO8601, defaults to now)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub to: Option<String>,

    /// Filter by subject pattern (e.g., "Tesla*", "auth/*")
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subject: Option<String>,

    /// Maximum number of results (default: 100)
    #[serde(default = "default_limit")]
    pub limit: usize,
}

impl Default for TraceParams {
    fn default() -> Self {
        Self { agent_id: String::new(), from: String::new(), to: None, subject: None, limit: 100 }
    }
}

/// Response from the trace endpoint.
///
/// Returns query audits matching the trace filters, useful for debugging
/// why an agent made specific decisions.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct TraceResponse {
    /// Matching query audit records
    pub audits: Vec<QueryAuditResponse>,

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

    /// Agent ID that was traced (hex-encoded)
    pub agent_id: String,

    /// Time range start (Unix timestamp)
    pub from_timestamp: u64,

    /// Time range end (Unix timestamp)
    pub to_timestamp: u64,
}

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

impl From<stemedb_core::types::QueryAudit> for QueryAuditResponse {
    fn from(audit: stemedb_core::types::QueryAudit) -> Self {
        QueryAuditResponse {
            query_id: hex::encode(audit.query_id),
            agent_id: audit.agent_id.map(hex::encode),
            timestamp: audit.timestamp,
            params: QueryParamsAuditDto {
                subject: audit.params.subject,
                predicate: audit.params.predicate,
                lifecycle: audit.params.lifecycle.map(Into::into),
                epoch: audit.params.epoch.map(hex::encode),
                lens: audit.params.lens,
            },
            result_hash: audit.result_hash.map(hex::encode),
            result_confidence: audit.result_confidence,
            contributing_assertions: audit
                .contributing_assertions
                .into_iter()
                .map(Into::into)
                .collect(),
        }
    }
}

impl From<stemedb_core::types::ContributingAssertion> for ContributingAssertionDto {
    fn from(ca: stemedb_core::types::ContributingAssertion) -> Self {
        ContributingAssertionDto {
            assertion_hash: hex::encode(ca.assertion_hash),
            weight: ca.weight,
            source_hash: hex::encode(ca.source_hash),
            lifecycle: ca.lifecycle.into(),
        }
    }
}
