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

//! Application-facing normalized chain instruction DTO.

/// Application-facing normalized chain instruction DTO.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ChainInstructionDto {
    /// Optional numeric primary key.
    pub id: std::option::Option<i64>,
    /// Parent transaction id.
    pub transaction_id: i64,
    /// Optional parent instruction id for inner instructions.
    pub parent_instruction_id: std::option::Option<i64>,
    /// Outer instruction index.
    pub instruction_index: u32,
    /// Optional inner instruction index.
    pub inner_instruction_index: std::option::Option<u32>,
    /// Optional program id.
    pub program_id: std::option::Option<std::string::String>,
    /// Optional program name.
    pub program_name: std::option::Option<std::string::String>,
    /// Optional stack height.
    pub stack_height: std::option::Option<u32>,
    /// Serialized accounts JSON array.
    pub accounts_json: std::string::String,
    /// Optional serialized data JSON.
    pub data_json: std::option::Option<std::string::String>,
    /// Optional parsed type.
    pub parsed_type: std::option::Option<std::string::String>,
    /// Optional serialized parsed JSON.
    pub parsed_json: std::option::Option<std::string::String>,
    /// Creation timestamp.
    pub created_at: chrono::DateTime<chrono::Utc>,
}

impl ChainInstructionDto {
    /// Creates a new chain instruction DTO.
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        transaction_id: i64,
        parent_instruction_id: std::option::Option<i64>,
        instruction_index: u32,
        inner_instruction_index: std::option::Option<u32>,
        program_id: std::option::Option<std::string::String>,
        program_name: std::option::Option<std::string::String>,
        stack_height: std::option::Option<u32>,
        accounts_json: std::string::String,
        data_json: std::option::Option<std::string::String>,
        parsed_type: std::option::Option<std::string::String>,
        parsed_json: std::option::Option<std::string::String>,
    ) -> Self {
        return Self {
            id: None,
            transaction_id,
            parent_instruction_id,
            instruction_index,
            inner_instruction_index,
            program_id,
            program_name,
            stack_height,
            accounts_json,
            data_json,
            parsed_type,
            parsed_json,
            created_at: chrono::Utc::now(),
        };
    }
}

impl TryFrom<crate::ChainInstructionEntity> for ChainInstructionDto {
    type Error = crate::Error;

    fn try_from(entity: crate::ChainInstructionEntity) -> Result<Self, Self::Error> {
        let instruction_index_result = u32::try_from(entity.instruction_index);
        let instruction_index = match instruction_index_result {
            Ok(instruction_index) => instruction_index,
            Err(error) => {
                return Err(crate::Error::Db(format!(
                    "cannot convert chain instruction instruction_index '{}' to u32: {}",
                    entity.instruction_index, error
                )));
            },
        };
        let inner_instruction_index = match entity.inner_instruction_index {
            Some(inner_instruction_index) => {
                let inner_instruction_index_result = u32::try_from(inner_instruction_index);
                match inner_instruction_index_result {
                    Ok(inner_instruction_index) => Some(inner_instruction_index),
                    Err(error) => {
                        return Err(crate::Error::Db(format!(
                            "cannot convert chain instruction inner_instruction_index '{}' to u32: {}",
                            inner_instruction_index, error
                        )));
                    },
                }
            },
            None => None,
        };
        let stack_height = match entity.stack_height {
            Some(stack_height) => {
                let stack_height_result = u32::try_from(stack_height);
                match stack_height_result {
                    Ok(stack_height) => Some(stack_height),
                    Err(error) => {
                        return Err(crate::Error::Db(format!(
                            "cannot convert chain instruction stack_height '{}' to u32: {}",
                            stack_height, error
                        )));
                    },
                }
            },
            None => None,
        };
        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 chain instruction created_at '{}': {}",
                    entity.created_at, error
                )));
            },
        };
        return Ok(Self {
            id: Some(entity.id),
            transaction_id: entity.transaction_id,
            parent_instruction_id: entity.parent_instruction_id,
            instruction_index,
            inner_instruction_index,
            program_id: entity.program_id,
            program_name: entity.program_name,
            stack_height,
            accounts_json: entity.accounts_json,
            data_json: entity.data_json,
            parsed_type: entity.parsed_type,
            parsed_json: entity.parsed_json,
            created_at,
        });
    }
}
