// file: kb_lib/src/db/dtos/analysis_signal.rs

//! Analysis signal DTO.

/// Application-facing analysis signal DTO.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct AnalysisSignalDto {
    /// Optional numeric primary key.
    pub id: std::option::Option<i64>,
    /// Signal kind.
    pub signal_kind: std::string::String,
    /// Signal severity.
    pub severity: crate::AnalysisSignalSeverity,
    /// Logical object key, for example a mint, signature or pool address.
    pub object_key: std::string::String,
    /// Optional related on-chain observation id.
    pub related_observation_id: std::option::Option<i64>,
    /// Optional numeric score.
    pub score: std::option::Option<f64>,
    /// Signal payload.
    pub payload: serde_json::Value,
    /// Creation timestamp.
    pub created_at: chrono::DateTime<chrono::Utc>,
}

impl AnalysisSignalDto {
    /// Creates a new analysis signal DTO with the current timestamp.
    pub fn new(
        signal_kind: std::string::String,
        severity: crate::AnalysisSignalSeverity,
        object_key: std::string::String,
        related_observation_id: std::option::Option<i64>,
        score: std::option::Option<f64>,
        payload: serde_json::Value,
    ) -> Self {
        return Self {
            id: None,
            signal_kind,
            severity,
            object_key,
            related_observation_id,
            score,
            payload,
            created_at: chrono::Utc::now(),
        };
    }
}

impl TryFrom<crate::AnalysisSignalEntity> for AnalysisSignalDto {
    type Error = crate::Error;

    fn try_from(entity: crate::AnalysisSignalEntity) -> Result<Self, Self::Error> {
        let severity_result = crate::AnalysisSignalSeverity::from_i16(entity.severity);
        let severity = match severity_result {
            Ok(severity) => severity,
            Err(error) => return Err(error),
        };
        let created_at_result = chrono::DateTime::parse_from_rfc3339(&entity.created_at);
        let created_at = match created_at_result {
            Ok(created_at) => created_at.with_timezone(&chrono::Utc),
            Err(error) => {
                return Err(crate::Error::Db(format!(
                    "cannot parse analysis signal created_at '{}': {}",
                    entity.created_at, error
                )));
            },
        };
        let payload_result = serde_json::from_str::<serde_json::Value>(&entity.payload_json);
        let payload = match payload_result {
            Ok(payload) => payload,
            Err(error) => {
                return Err(crate::Error::Db(format!(
                    "cannot parse analysis signal payload_json '{}': {}",
                    entity.payload_json, error
                )));
            },
        };
        return Ok(Self {
            id: Some(entity.id),
            signal_kind: entity.signal_kind,
            severity,
            object_key: entity.object_key,
            related_observation_id: entity.related_observation_id,
            score: entity.score,
            payload,
            created_at,
        });
    }
}
