//! 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::FeeTier;
use crate::accounts::LockConfig;
use crate::accounts::Position;
use crate::accounts::PositionBundle;
use crate::accounts::TickArray;
use crate::accounts::TokenBadge;
use crate::accounts::Whirlpool;
use crate::accounts::WhirlpoolsConfig;
use crate::accounts::WhirlpoolsConfigExtension;
use crate::ID;

/// Whirlpool Program State
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
#[cfg_attr(feature = "tracing", derive(strum_macros::Display))]
pub enum WhirlpoolProgramState {
    WhirlpoolsConfig(WhirlpoolsConfig),
    WhirlpoolsConfigExtension(WhirlpoolsConfigExtension),
    FeeTier(FeeTier),
    LockConfig(LockConfig),
    Position(Position),
    PositionBundle(PositionBundle),
    TickArray(TickArray),
    TokenBadge(TokenBadge),
    Whirlpool(Whirlpool),
}

impl WhirlpoolProgramState {
    pub fn try_unpack(data_bytes: &[u8]) -> yellowstone_vixen_core::ParseResult<Self> {
        let acc_discriminator: [u8; 8] = data_bytes[0..8].try_into()?;
        let acc = match acc_discriminator {
            [157, 20, 49, 224, 217, 87, 193, 254] => Ok(WhirlpoolProgramState::WhirlpoolsConfig(
                WhirlpoolsConfig::from_bytes(data_bytes)?,
            )),
            [2, 99, 215, 163, 240, 26, 153, 58] => {
                Ok(WhirlpoolProgramState::WhirlpoolsConfigExtension(
                    WhirlpoolsConfigExtension::from_bytes(data_bytes)?,
                ))
            }
            [56, 75, 159, 76, 142, 68, 190, 105] => Ok(WhirlpoolProgramState::FeeTier(
                FeeTier::from_bytes(data_bytes)?,
            )),
            [106, 47, 238, 159, 124, 12, 160, 192] => Ok(WhirlpoolProgramState::LockConfig(
                LockConfig::from_bytes(data_bytes)?,
            )),
            [170, 188, 143, 228, 122, 64, 247, 208] => Ok(WhirlpoolProgramState::Position(
                Position::from_bytes(data_bytes)?,
            )),
            [129, 169, 175, 65, 185, 95, 32, 100] => Ok(WhirlpoolProgramState::PositionBundle(
                PositionBundle::from_bytes(data_bytes)?,
            )),
            [69, 97, 189, 190, 110, 7, 66, 187] => Ok(WhirlpoolProgramState::TickArray(
                TickArray::from_bytes(data_bytes)?,
            )),
            [116, 219, 204, 229, 249, 116, 255, 150] => Ok(WhirlpoolProgramState::TokenBadge(
                TokenBadge::from_bytes(data_bytes)?,
            )),
            [63, 149, 209, 12, 225, 128, 99, 9] => Ok(WhirlpoolProgramState::Whirlpool(
                Whirlpool::from_bytes(data_bytes)?,
            )),
            _ => Err(yellowstone_vixen_core::ParseError::from(
                "Invalid Account discriminator".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",
                    discriminator = ?acc_discriminator,
                    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 = WhirlpoolProgramState;

    fn id(&self) -> std::borrow::Cow<str> {
        "whirlpool::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)?;
        WhirlpoolProgramState::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, WhirlpoolProgramState};
    use crate::{proto_def, proto_helpers::proto_types_parsers::IntoProto};
    use yellowstone_vixen_core::proto::ParseProto;

    use super::WhirlpoolsConfig;
    impl IntoProto<proto_def::WhirlpoolsConfig> for WhirlpoolsConfig {
        fn into_proto(self) -> proto_def::WhirlpoolsConfig {
            proto_def::WhirlpoolsConfig {
                fee_authority: self.fee_authority.to_string(),
                collect_protocol_fees_authority: self.collect_protocol_fees_authority.to_string(),
                reward_emissions_super_authority: self.reward_emissions_super_authority.to_string(),
                default_protocol_fee_rate: self.default_protocol_fee_rate.into(),
            }
        }
    }
    use super::WhirlpoolsConfigExtension;
    impl IntoProto<proto_def::WhirlpoolsConfigExtension> for WhirlpoolsConfigExtension {
        fn into_proto(self) -> proto_def::WhirlpoolsConfigExtension {
            proto_def::WhirlpoolsConfigExtension {
                whirlpools_config: self.whirlpools_config.to_string(),
                config_extension_authority: self.config_extension_authority.to_string(),
                token_badge_authority: self.token_badge_authority.to_string(),
            }
        }
    }
    use super::FeeTier;
    impl IntoProto<proto_def::FeeTier> for FeeTier {
        fn into_proto(self) -> proto_def::FeeTier {
            proto_def::FeeTier {
                whirlpools_config: self.whirlpools_config.to_string(),
                tick_spacing: self.tick_spacing.into(),
                default_fee_rate: self.default_fee_rate.into(),
            }
        }
    }
    use super::LockConfig;
    impl IntoProto<proto_def::LockConfig> for LockConfig {
        fn into_proto(self) -> proto_def::LockConfig {
            proto_def::LockConfig {
                position: self.position.to_string(),
                position_owner: self.position_owner.to_string(),
                whirlpool: self.whirlpool.to_string(),
                locked_timestamp: self.locked_timestamp,
                lock_type: self.lock_type as i32,
            }
        }
    }
    use super::Position;
    impl IntoProto<proto_def::Position> for Position {
        fn into_proto(self) -> proto_def::Position {
            proto_def::Position {
                whirlpool: self.whirlpool.to_string(),
                position_mint: self.position_mint.to_string(),
                liquidity: self.liquidity.to_string(),
                tick_lower_index: self.tick_lower_index,
                tick_upper_index: self.tick_upper_index,
                fee_growth_checkpoint_a: self.fee_growth_checkpoint_a.to_string(),
                fee_owed_a: self.fee_owed_a,
                fee_growth_checkpoint_b: self.fee_growth_checkpoint_b.to_string(),
                fee_owed_b: self.fee_owed_b,
                reward_infos: self
                    .reward_infos
                    .into_iter()
                    .map(|x| x.into_proto())
                    .collect(),
            }
        }
    }
    use super::PositionBundle;
    impl IntoProto<proto_def::PositionBundle> for PositionBundle {
        fn into_proto(self) -> proto_def::PositionBundle {
            proto_def::PositionBundle {
                position_bundle_mint: self.position_bundle_mint.to_string(),
                position_bitmap: self.position_bitmap.into_iter().map(|x| x.into()).collect(),
            }
        }
    }
    use super::TickArray;
    impl IntoProto<proto_def::TickArray> for TickArray {
        fn into_proto(self) -> proto_def::TickArray {
            proto_def::TickArray {
                start_tick_index: self.start_tick_index,
                ticks: self.ticks.into_iter().map(|x| x.into_proto()).collect(),
                whirlpool: self.whirlpool.to_string(),
            }
        }
    }
    use super::TokenBadge;
    impl IntoProto<proto_def::TokenBadge> for TokenBadge {
        fn into_proto(self) -> proto_def::TokenBadge {
            proto_def::TokenBadge {
                whirlpools_config: self.whirlpools_config.to_string(),
                token_mint: self.token_mint.to_string(),
            }
        }
    }
    use super::Whirlpool;
    impl IntoProto<proto_def::Whirlpool> for Whirlpool {
        fn into_proto(self) -> proto_def::Whirlpool {
            proto_def::Whirlpool {
                whirlpools_config: self.whirlpools_config.to_string(),
                whirlpool_bump: self.whirlpool_bump.into_iter().map(|x| x.into()).collect(),
                tick_spacing: self.tick_spacing.into(),
                tick_spacing_seed: self
                    .tick_spacing_seed
                    .into_iter()
                    .map(|x| x.into())
                    .collect(),
                fee_rate: self.fee_rate.into(),
                protocol_fee_rate: self.protocol_fee_rate.into(),
                liquidity: self.liquidity.to_string(),
                sqrt_price: self.sqrt_price.to_string(),
                tick_current_index: self.tick_current_index,
                protocol_fee_owed_a: self.protocol_fee_owed_a,
                protocol_fee_owed_b: self.protocol_fee_owed_b,
                token_mint_a: self.token_mint_a.to_string(),
                token_vault_a: self.token_vault_a.to_string(),
                fee_growth_global_a: self.fee_growth_global_a.to_string(),
                token_mint_b: self.token_mint_b.to_string(),
                token_vault_b: self.token_vault_b.to_string(),
                fee_growth_global_b: self.fee_growth_global_b.to_string(),
                reward_last_updated_timestamp: self.reward_last_updated_timestamp,
                reward_infos: self
                    .reward_infos
                    .into_iter()
                    .map(|x| x.into_proto())
                    .collect(),
            }
        }
    }

    impl IntoProto<proto_def::ProgramState> for WhirlpoolProgramState {
        fn into_proto(self) -> proto_def::ProgramState {
            let state_oneof = match self {
                WhirlpoolProgramState::WhirlpoolsConfig(data) => {
                    proto_def::program_state::StateOneof::WhirlpoolsConfig(data.into_proto())
                }
                WhirlpoolProgramState::WhirlpoolsConfigExtension(data) => {
                    proto_def::program_state::StateOneof::WhirlpoolsConfigExtension(
                        data.into_proto(),
                    )
                }
                WhirlpoolProgramState::FeeTier(data) => {
                    proto_def::program_state::StateOneof::FeeTier(data.into_proto())
                }
                WhirlpoolProgramState::LockConfig(data) => {
                    proto_def::program_state::StateOneof::LockConfig(data.into_proto())
                }
                WhirlpoolProgramState::Position(data) => {
                    proto_def::program_state::StateOneof::Position(data.into_proto())
                }
                WhirlpoolProgramState::PositionBundle(data) => {
                    proto_def::program_state::StateOneof::PositionBundle(data.into_proto())
                }
                WhirlpoolProgramState::TickArray(data) => {
                    proto_def::program_state::StateOneof::TickArray(data.into_proto())
                }
                WhirlpoolProgramState::TokenBadge(data) => {
                    proto_def::program_state::StateOneof::TokenBadge(data.into_proto())
                }
                WhirlpoolProgramState::Whirlpool(data) => {
                    proto_def::program_state::StateOneof::Whirlpool(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()
        }
    }
}
