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

//! Application-facing DEX decode replay ledger DTO.

/// Application-facing replay ledger row for one transaction and decoder version.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct DexDecodeReplayLedgerDto {
    /// Optional numeric primary key.
    pub id: std::option::Option<i64>,
    /// Related chain transaction id.
    pub transaction_id: i64,
    /// Transaction signature.
    pub signature: std::string::String,
    /// Logical decoder scope.
    pub decoder_scope: std::string::String,
    /// Logical decoder version.
    pub decoder_version: std::string::String,
    /// Decode status.
    pub decode_status: std::string::String,
    /// Certainty level used by replay skip logic.
    pub certainty: std::string::String,
    /// Number of decoded events produced by this decoder pass.
    pub event_count: i64,
    /// Number of distinct token mints observed in decoded event mint fields.
    pub distinct_token_mint_count: i64,
    /// Whether replay must force decode for safety.
    pub force_replay_required: bool,
    /// Optional status reason.
    pub status_reason: std::option::Option<std::string::String>,
    /// Creation timestamp.
    pub created_at: chrono::DateTime<chrono::Utc>,
    /// Update timestamp.
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

impl DexDecodeReplayLedgerDto {
    /// Status used when a decoder pass produced one or more events.
    pub const STATUS_DECODED: &'static str = "decoded";
    /// Status used when a decoder pass completed but produced no event.
    pub const STATUS_NO_EVENTS: &'static str = "no_events";
    /// Status used when a decoder pass failed.
    pub const STATUS_FAILED: &'static str = "failed";
    /// Certainty used when the ledger row may safely skip a future decode pass.
    pub const CERTAINTY_SURE: &'static str = "sure";
    /// Certainty used when the ledger row must not skip a future decode pass.
    pub const CERTAINTY_UNSAFE: &'static str = "unsafe";

    /// Creates a new DEX decode replay ledger DTO.
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        transaction_id: i64,
        signature: std::string::String,
        decoder_scope: std::string::String,
        decoder_version: std::string::String,
        decode_status: std::string::String,
        certainty: std::string::String,
        event_count: i64,
        distinct_token_mint_count: i64,
        force_replay_required: bool,
        status_reason: std::option::Option<std::string::String>,
    ) -> Self {
        let now = chrono::Utc::now();
        return Self {
            id: None,
            transaction_id,
            signature,
            decoder_scope,
            decoder_version,
            decode_status,
            certainty,
            event_count,
            distinct_token_mint_count,
            force_replay_required,
            status_reason,
            created_at: now,
            updated_at: now,
        };
    }

    /// Returns whether this ledger row certifies that DEX decoding can be skipped.
    pub fn can_skip_decode(&self) -> bool {
        let status_allows_skip = self.decode_status == Self::STATUS_DECODED
            || self.decode_status == Self::STATUS_NO_EVENTS;
        return status_allows_skip
            && self.certainty == Self::CERTAINTY_SURE
            && !self.force_replay_required;
    }
}

impl TryFrom<crate::DexDecodeReplayLedgerEntity> for DexDecodeReplayLedgerDto {
    type Error = crate::Error;

    fn try_from(entity: crate::DexDecodeReplayLedgerEntity) -> Result<Self, Self::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 dex decode replay ledger created_at '{}': {}",
                    entity.created_at, error
                )));
            },
        };
        let updated_at_result = chrono::DateTime::parse_from_rfc3339(&entity.updated_at);
        let updated_at = match updated_at_result {
            Ok(updated_at) => updated_at.with_timezone(&chrono::Utc),
            Err(error) => {
                return Err(crate::Error::Db(format!(
                    "cannot parse dex decode replay ledger updated_at '{}': {}",
                    entity.updated_at, error
                )));
            },
        };
        return Ok(Self {
            id: Some(entity.id),
            transaction_id: entity.transaction_id,
            signature: entity.signature,
            decoder_scope: entity.decoder_scope,
            decoder_version: entity.decoder_version,
            decode_status: entity.decode_status,
            certainty: entity.certainty,
            event_count: entity.event_count,
            distinct_token_mint_count: entity.distinct_token_mint_count,
            force_replay_required: entity.force_replay_required != 0,
            status_reason: entity.status_reason,
            created_at,
            updated_at,
        });
    }
}
