//! This code was AUTOGENERATED using the codama library.
//! Please DO NOT EDIT THIS FILE, instead use visitors
//! to add features, then rerun codama to update it.
//!
//! <https://github.com/codama-idl/codama>
//!

use crate::accounts::AmmConfig;
use crate::accounts::AmmInfo;
use crate::accounts::TargetOrders;
use crate::ID;

/// RaydiumAmm Program State
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
#[cfg_attr(feature = "tracing", derive(strum_macros::Display))]
pub enum RaydiumAmmProgramState {
    TargetOrders(TargetOrders),
    AmmInfo(AmmInfo),
    AmmConfig(AmmConfig),
}

impl RaydiumAmmProgramState {
    pub fn try_unpack(data_bytes: &[u8]) -> yellowstone_vixen_core::ParseResult<Self> {
        let data_len = data_bytes.len();
        const TARGETORDERS_LEN: usize = std::mem::size_of::<TargetOrders>();
        const AMMINFO_LEN: usize = std::mem::size_of::<AmmInfo>();
        const AMMCONFIG_LEN: usize = std::mem::size_of::<AmmConfig>();

        let acc = match data_len {
            TARGETORDERS_LEN => Ok(RaydiumAmmProgramState::TargetOrders(
                TargetOrders::from_bytes(data_bytes)?,
            )),
            AMMINFO_LEN => Ok(RaydiumAmmProgramState::AmmInfo(AmmInfo::from_bytes(
                data_bytes,
            )?)),
            AMMCONFIG_LEN => Ok(RaydiumAmmProgramState::AmmConfig(AmmConfig::from_bytes(
                data_bytes,
            )?)),
            _ => Err(yellowstone_vixen_core::ParseError::from(
                "Invalid Account data length".to_owned(),
            )),
        };

        #[cfg(feature = "tracing")]
        match &acc {
            Ok(acc) => {
                tracing::info!(
                    name: "correctly_parsed_account",
                    name = "account_update",
                    program = ID.to_string(),
                    account = acc.to_string()
                );
            }
            Err(e) => {
                tracing::info!(
                    name: "incorrectly_parsed_account",
                    name = "account_update",
                    program = ID.to_string(),
                    account = "error",
                    data_len = ?data_len,
                    error = ?e
                );
            }
        }

        acc
    }
}

#[derive(Debug, Copy, Clone)]
pub struct AccountParser;

impl yellowstone_vixen_core::Parser for AccountParser {
    type Input = yellowstone_vixen_core::AccountUpdate;
    type Output = RaydiumAmmProgramState;

    fn id(&self) -> std::borrow::Cow<str> {
        "raydium_amm::AccountParser".into()
    }

    fn prefilter(&self) -> yellowstone_vixen_core::Prefilter {
        yellowstone_vixen_core::Prefilter::builder()
            .account_owners([ID])
            .build()
            .unwrap()
    }

    async fn parse(
        &self,
        acct: &yellowstone_vixen_core::AccountUpdate,
    ) -> yellowstone_vixen_core::ParseResult<Self::Output> {
        let inner = acct
            .account
            .as_ref()
            .ok_or(solana_program_error::ProgramError::InvalidArgument)?;
        RaydiumAmmProgramState::try_unpack(&inner.data)
    }
}

impl yellowstone_vixen_core::ProgramParser for AccountParser {
    #[inline]
    fn program_id(&self) -> yellowstone_vixen_core::Pubkey {
        ID.to_bytes().into()
    }
}

// #[cfg(feature = "proto")]
mod proto_parser {
    use super::{AccountParser, RaydiumAmmProgramState};
    use crate::{proto_def, proto_helpers::proto_types_parsers::IntoProto};
    use yellowstone_vixen_core::proto::ParseProto;

    use super::TargetOrders;
    impl IntoProto<proto_def::TargetOrders> for TargetOrders {
        fn into_proto(self) -> proto_def::TargetOrders {
            proto_def::TargetOrders {
                owner: self.owner.to_vec(),
                buy_orders: self
                    .buy_orders
                    .into_iter()
                    .map(|x| x.into_proto())
                    .collect(),
                padding1: self.padding1.to_vec(),
                target_x: self.target_x.to_string(),
                target_y: self.target_y.to_string(),
                plan_x_buy: self.plan_x_buy.to_string(),
                plan_y_buy: self.plan_y_buy.to_string(),
                plan_x_sell: self.plan_x_sell.to_string(),
                plan_y_sell: self.plan_y_sell.to_string(),
                placed_x: self.placed_x.to_string(),
                placed_y: self.placed_y.to_string(),
                calc_pnl_x: self.calc_pnl_x.to_string(),
                calc_pnl_y: self.calc_pnl_y.to_string(),
                sell_orders: self
                    .sell_orders
                    .into_iter()
                    .map(|x| x.into_proto())
                    .collect(),
                padding2: self.padding2.to_vec(),
                replace_buy_client_id: self.replace_buy_client_id.to_vec(),
                replace_sell_client_id: self.replace_sell_client_id.to_vec(),
                last_order_numerator: self.last_order_numerator,
                last_order_denominator: self.last_order_denominator,
                plan_orders_cur: self.plan_orders_cur,
                place_orders_cur: self.place_orders_cur,
                valid_buy_order_num: self.valid_buy_order_num,
                valid_sell_order_num: self.valid_sell_order_num,
                padding3: self.padding3.to_vec(),
                free_slot_bits: self.free_slot_bits.to_string(),
            }
        }
    }
    use super::AmmInfo;
    impl IntoProto<proto_def::AmmInfo> for AmmInfo {
        fn into_proto(self) -> proto_def::AmmInfo {
            proto_def::AmmInfo {
                status: self.status,
                nonce: self.nonce,
                order_num: self.order_num,
                depth: self.depth,
                coin_decimals: self.coin_decimals,
                pc_decimals: self.pc_decimals,
                state: self.state,
                reset_flag: self.reset_flag,
                min_size: self.min_size,
                vol_max_cut_ratio: self.vol_max_cut_ratio,
                amount_wave: self.amount_wave,
                coin_lot_size: self.coin_lot_size,
                pc_lot_size: self.pc_lot_size,
                min_price_multiplier: self.min_price_multiplier,
                max_price_multiplier: self.max_price_multiplier,
                sys_decimal_value: self.sys_decimal_value,
                fees: Some(self.fees.into_proto()),
                out_put: Some(self.out_put.into_proto()),
                token_coin: self.token_coin.to_string(),
                token_pc: self.token_pc.to_string(),
                coin_mint: self.coin_mint.to_string(),
                pc_mint: self.pc_mint.to_string(),
                lp_mint: self.lp_mint.to_string(),
                open_orders: self.open_orders.to_string(),
                market: self.market.to_string(),
                serum_dex: self.serum_dex.to_string(),
                target_orders: self.target_orders.to_string(),
                withdraw_queue: self.withdraw_queue.to_string(),
                token_temp_lp: self.token_temp_lp.to_string(),
                amm_owner: self.amm_owner.to_string(),
                lp_amount: self.lp_amount,
                client_order_id: self.client_order_id,
                padding: self.padding.to_vec(),
            }
        }
    }
    use super::AmmConfig;
    impl IntoProto<proto_def::AmmConfig> for AmmConfig {
        fn into_proto(self) -> proto_def::AmmConfig {
            proto_def::AmmConfig {
                pnl_owner: self.pnl_owner.to_string(),
                cancel_owner: self.cancel_owner.to_string(),
                pending1: self.pending1.to_vec(),
                pending2: self.pending2.to_vec(),
                create_pool_fee: self.create_pool_fee,
            }
        }
    }

    impl IntoProto<proto_def::ProgramState> for RaydiumAmmProgramState {
        fn into_proto(self) -> proto_def::ProgramState {
            let state_oneof = match self {
                RaydiumAmmProgramState::TargetOrders(data) => {
                    proto_def::program_state::StateOneof::TargetOrders(data.into_proto())
                }
                RaydiumAmmProgramState::AmmInfo(data) => {
                    proto_def::program_state::StateOneof::AmmInfo(data.into_proto())
                }
                RaydiumAmmProgramState::AmmConfig(data) => {
                    proto_def::program_state::StateOneof::AmmConfig(data.into_proto())
                }
            };

            proto_def::ProgramState {
                state_oneof: Some(state_oneof),
            }
        }
    }

    impl ParseProto for AccountParser {
        type Message = proto_def::ProgramState;

        fn output_into_message(value: Self::Output) -> Self::Message {
            value.into_proto()
        }
    }
}
