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

//! Normalized token DTO.

/// Application-facing normalized token DTO.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TokenDto {
    /// Optional numeric primary key.
    pub id: std::option::Option<i64>,
    /// Mint address.
    pub mint: std::string::String,
    /// Optional token symbol.
    pub symbol: std::option::Option<std::string::String>,
    /// Optional token display name.
    pub name: std::option::Option<std::string::String>,
    /// Optional decimals value.
    pub decimals: std::option::Option<u8>,
    /// Token program id.
    pub token_program: std::string::String,
    /// Whether this token is typically used as quote token.
    pub is_quote_token: bool,
    /// First seen timestamp.
    pub first_seen_at: chrono::DateTime<chrono::Utc>,
    /// Update timestamp.
    pub updated_at: chrono::DateTime<chrono::Utc>,
}

impl TokenDto {
    /// Creates a new token DTO.
    pub fn new(
        mint: std::string::String,
        symbol: std::option::Option<std::string::String>,
        name: std::option::Option<std::string::String>,
        decimals: std::option::Option<u8>,
        token_program: std::string::String,
        is_quote_token: bool,
    ) -> Self {
        let now = chrono::Utc::now();
        return Self {
            id: None,
            mint,
            symbol,
            name,
            decimals,
            token_program,
            is_quote_token,
            first_seen_at: now,
            updated_at: now,
        };
    }
}

impl TryFrom<crate::TokenEntity> for TokenDto {
    type Error = crate::Error;

    fn try_from(entity: crate::TokenEntity) -> Result<Self, Self::Error> {
        let first_seen_at_result = chrono::DateTime::parse_from_rfc3339(&entity.first_seen_at);
        let first_seen_at = match first_seen_at_result {
            Ok(first_seen_at) => first_seen_at.with_timezone(&chrono::Utc),
            Err(error) => {
                return Err(crate::Error::Db(format!(
                    "cannot parse token first_seen_at '{}': {}",
                    entity.first_seen_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 token updated_at '{}': {}",
                    entity.updated_at, error
                )));
            },
        };
        let decimals = match entity.decimals {
            Some(decimals) => {
                let decimals_result = u8::try_from(decimals);
                match decimals_result {
                    Ok(decimals) => Some(decimals),
                    Err(error) => {
                        return Err(crate::Error::Db(format!(
                            "cannot convert token decimals '{}' to u8: {}",
                            decimals, error
                        )));
                    },
                }
            },
            None => None,
        };
        return Ok(Self {
            id: Some(entity.id),
            mint: entity.mint,
            symbol: entity.symbol,
            name: entity.name,
            decimals,
            token_program: entity.token_program,
            is_quote_token: entity.is_quote_token != 0,
            first_seen_at,
            updated_at,
        });
    }
}
