use solana_sdk::pubkey::Pubkey;

// ============================================================================
// Enums (previously re-exported from hadron-quote)
// ============================================================================

/// Interpolation mode for curve segments.
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Interpolation {
    /// Step function: output stays constant until next point
    Step = 0,
    /// Linear interpolation between points
    Linear = 1,
    /// Marginal-step: orderbook-like behavior where each size tier has a flat marginal price
    MarginalStep = 2,
    /// Hyperbolic easing with shape parameter k (Q16.16 in params[0..4])
    Hyperbolic = 3,
    /// Quadratic easing with shape parameter k (Q16.16 signed i32 in params[0..4])
    Quadratic = 4,
    /// Cubic easing with 2 shape parameters (Q8.8 signed i16 each in params[0..4])
    Cubic = 5,
}

impl Default for Interpolation {
    fn default() -> Self {
        Interpolation::Linear
    }
}

impl TryFrom<u8> for Interpolation {
    type Error = HadronSdkError;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Interpolation::Step),
            1 => Ok(Interpolation::Linear),
            2 => Ok(Interpolation::MarginalStep),
            3 => Ok(Interpolation::Hyperbolic),
            4 => Ok(Interpolation::Quadratic),
            5 => Ok(Interpolation::Cubic),
            _ => Err(HadronSdkError::InvalidEnum("Interpolation", value)),
        }
    }
}

/// X-axis interpretation mode for curves.
///
/// - **Price curves**: Native = absolute token atoms, Alternate = percent of vault (Q32)
/// - **Risk curves**: Native = pct_base percent (Q32), Alternate = absolute token atoms
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum CurveXMode {
    Native = 0,
    Alternate = 1,
}

impl Default for CurveXMode {
    fn default() -> Self {
        CurveXMode::Native
    }
}

impl TryFrom<u8> for CurveXMode {
    type Error = HadronSdkError;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(CurveXMode::Native),
            1 => Ok(CurveXMode::Alternate),
            _ => Err(HadronSdkError::InvalidEnum("CurveXMode", value)),
        }
    }
}

/// Risk evaluation mode for risk curves.
///
/// - **Virtual**: Evaluate at a single point (pre-trade vault balance). Path-dependent.
/// - **Integrated**: Walk curve segments, return weighted-average factor. Path-independent.
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum RiskMode {
    Virtual = 0,
    Integrated = 1,
}

impl Default for RiskMode {
    fn default() -> Self {
        RiskMode::Virtual
    }
}

impl TryFrom<u8> for RiskMode {
    type Error = HadronSdkError;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(RiskMode::Virtual),
            1 => Ok(RiskMode::Integrated),
            _ => Err(HadronSdkError::InvalidEnum("RiskMode", value)),
        }
    }
}

/// A single on-chain curve point (24 bytes, repr(C, packed)).
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CurvePoint {
    pub x_in: u64,
    pub price_factor_q32: u64,
    pub interpolation: u8,
    pub params: [u8; 4],
    pub _reserved: [u8; 3],
}

impl CurvePoint {
    pub const LEN: usize = 24;
}

// ============================================================================
// Enums
// ============================================================================

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum PoolState {
    Uninitialized = 0,
    Initialized = 1,
    Paused = 2,
    WithdrawOnly = 3,
}

impl TryFrom<u8> for PoolState {
    type Error = crate::HadronSdkError;
    fn try_from(v: u8) -> Result<Self, Self::Error> {
        match v {
            0 => Ok(Self::Uninitialized),
            1 => Ok(Self::Initialized),
            2 => Ok(Self::Paused),
            3 => Ok(Self::WithdrawOnly),
            _ => Err(crate::HadronSdkError::InvalidEnum("PoolState", v)),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum OracleMode {
    Authority = 0,
    Relative = 1,
}

impl TryFrom<u8> for OracleMode {
    type Error = crate::HadronSdkError;
    fn try_from(v: u8) -> Result<Self, Self::Error> {
        match v {
            0 => Ok(Self::Authority),
            1 => Ok(Self::Relative),
            _ => Err(crate::HadronSdkError::InvalidEnum("OracleMode", v)),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CurveType {
    PriceBid = 0,
    PriceAsk = 1,
    RiskBid = 2,
    RiskAsk = 3,
}

impl TryFrom<u8> for CurveType {
    type Error = crate::HadronSdkError;
    fn try_from(v: u8) -> Result<Self, Self::Error> {
        match v {
            0 => Ok(Self::PriceBid),
            1 => Ok(Self::PriceAsk),
            2 => Ok(Self::RiskBid),
            3 => Ok(Self::RiskAsk),
            _ => Err(crate::HadronSdkError::InvalidEnum("CurveType", v)),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Side {
    Bid = 0,
    Ask = 1,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum CurveUpdateOpKind {
    Edit = 0,
    Add = 1,
    Remove = 2,
}

impl TryFrom<u8> for CurveUpdateOpKind {
    type Error = crate::HadronSdkError;
    fn try_from(v: u8) -> Result<Self, Self::Error> {
        match v {
            0 => Ok(Self::Edit),
            1 => Ok(Self::Add),
            2 => Ok(Self::Remove),
            _ => Err(crate::HadronSdkError::InvalidEnum("CurveUpdateOpKind", v)),
        }
    }
}

// ============================================================================
// Error type
// ============================================================================

#[derive(Debug, thiserror::Error)]
pub enum HadronSdkError {
    #[error("Account data too short: expected at least {expected}, got {actual}")]
    DataTooShort { expected: usize, actual: usize },

    #[error("Invalid {0} enum value: {1}")]
    InvalidEnum(&'static str, u8),

    #[error("Account not found: {0}")]
    AccountNotFound(&'static str),

    #[cfg(feature = "rpc")]
    #[error("RPC error: {0}")]
    Rpc(#[from] solana_client::client_error::ClientError),
}

// ============================================================================
// Decoded Account Structs
// ============================================================================

#[derive(Debug, Clone)]
pub struct DecodedConfig {
    pub state: PoolState,
    pub seed: u64,
    pub authority: Pubkey,
    pub mint_x: Pubkey,
    pub mint_y: Pubkey,
    pub config_bump: u8,
    pub curve_meta: Pubkey,
    pub spread_config_initialized: bool,
    pub delta_staleness: u8,
    pub oracle_mode: OracleMode,
    pub has_pool_fee: bool,
    pub pending_authority: Pubkey,
    pub nomination_expiry: u64,
    pub token_program_x: Pubkey,
    pub token_program_y: Pubkey,
}

#[derive(Debug, Clone)]
pub struct DecodedMidpriceOracle {
    pub authority: Pubkey,
    pub sequence: u64,
    pub midprice_q32: u64,
    pub spread_factor_q32: u64,
    pub last_update_slot: u64,
}

#[derive(Debug, Clone)]
pub struct DecodedCurveMeta {
    pub authority: Pubkey,
    pub active_price_bid_slot: u8,
    pub active_price_ask_slot: u8,
    pub active_risk_bid_slot: u8,
    pub active_risk_ask_slot: u8,
    pub initialized_slots: [u8; 8],
    pub max_prefab_slots: u8,
    pub max_curve_points: u8,
}

#[derive(Debug, Clone)]
pub struct DecodedFeeConfig {
    pub initialized: bool,
    pub fee_ppm: u32,
    pub bump: u8,
    pub fee_admin: Pubkey,
    pub fee_recipient: Pubkey,
}

#[derive(Debug, Clone)]
pub struct DecodedSpreadConfig {
    pub initialized: bool,
    pub bump: u8,
    pub num_triggers: u8,
    pub admin: Pubkey,
    pub config: Pubkey,
    pub triggers: Vec<SpreadTriggerInput>,
}

#[derive(Debug, Clone)]
pub struct DecodedCurveUpdates {
    pub authority: Pubkey,
    pub curve_meta: Pubkey,
    pub num_ops: u8,
    pub ops: Vec<CurveUpdateOp>,
}

// ============================================================================
// Curve Data Types
// ============================================================================

#[derive(Debug, Clone)]
pub struct CurveSide {
    pub num_points: u8,
    pub default_interpolation: Interpolation,
    pub x_mode: CurveXMode,
    pub risk_mode: RiskMode,
    pub points: Vec<DecodedCurvePoint>,
}

#[derive(Debug, Clone)]
pub struct DecodedCurvePoint {
    pub amount_in: u64,
    pub price_factor_q32: u64,
    pub interpolation: Interpolation,
    pub params: [u8; 4],
}

#[derive(Debug, Clone)]
pub struct CurveUpdateOp {
    pub curve_type: CurveType,
    pub op_kind: CurveUpdateOpKind,
    pub point_index: u8,
    pub interpolation: Interpolation,
    pub amount_in: u64,
    pub price_factor_q32: u64,
    pub params: [u8; 4],
}

#[derive(Debug, Clone)]
pub struct ActiveCurves {
    pub price_bid: CurveSide,
    pub price_ask: CurveSide,
    pub risk_bid: CurveSide,
    pub risk_ask: CurveSide,
}

// ============================================================================
// Pool Addresses
// ============================================================================

#[derive(Debug, Clone)]
pub struct PoolAddresses {
    pub config: Pubkey,
    pub config_bump: u8,
    pub midprice_oracle: Pubkey,
    pub curve_meta: Pubkey,
    pub curve_prefabs: Pubkey,
    pub curve_updates: Pubkey,
    pub vault_x: Pubkey,
    pub vault_y: Pubkey,
}

// ============================================================================
// Instruction Parameter Structs
// ============================================================================

pub struct InitializeParams {
    pub seed: Option<u64>,
    pub mint_x: Pubkey,
    pub mint_y: Pubkey,
    pub authority: Pubkey,
    pub initial_midprice_q32: u64,
    pub oracle_mode: Option<OracleMode>,
    pub max_prefab_slots: Option<u8>,
    pub max_curve_points: Option<u8>,
    pub token_program_x: Option<Pubkey>,
    pub token_program_y: Option<Pubkey>,
}

pub struct DepositParams {
    pub amount_x: u64,
    pub amount_y: u64,
    pub expiration: Option<i64>,
}

pub struct WithdrawParams {
    pub amount_x: u64,
    pub amount_y: u64,
    pub expiration: Option<i64>,
}

pub struct SwapParams {
    pub is_x: bool,
    pub amount_in: u64,
    pub min_out: u64,
    pub fee_recipient: Pubkey,
    pub expiration: Option<i64>,
}

pub struct SetCurvePointInput {
    pub amount_in: u64,
    pub price_factor_q32: u64,
    pub interpolation: Option<Interpolation>,
    pub params: Option<[u8; 4]>,
}

pub struct SetCurveParams {
    pub side: Side,
    pub default_interpolation: Interpolation,
    pub points: Vec<SetCurvePointInput>,
    pub slot: Option<u8>,
    pub x_mode: Option<CurveXMode>,
}

pub struct SetCurveBothParams {
    pub bid: SetCurveHalfParams,
    pub ask: SetCurveHalfParams,
}

pub struct SetCurveHalfParams {
    pub default_interpolation: Interpolation,
    pub points: Vec<SetCurvePointInput>,
    pub slot: Option<u8>,
    pub x_mode: Option<CurveXMode>,
}

pub struct SetRiskCurvePointInput {
    pub pct_base_q32: u64,
    pub price_factor_q32: u64,
    pub interpolation: Option<Interpolation>,
    pub params: Option<[u8; 4]>,
}

pub struct SetRiskCurveParams {
    pub side: Side,
    pub default_interpolation: Interpolation,
    pub points: Vec<SetRiskCurvePointInput>,
    pub slot: Option<u8>,
    pub x_mode: Option<CurveXMode>,
    pub risk_mode: Option<RiskMode>,
}

pub struct SetRiskCurveBothParams {
    pub bid: SetRiskCurveHalfParams,
    pub ask: SetRiskCurveHalfParams,
}

pub struct SetRiskCurveHalfParams {
    pub default_interpolation: Interpolation,
    pub points: Vec<SetRiskCurvePointInput>,
    pub slot: Option<u8>,
    pub x_mode: Option<CurveXMode>,
    pub risk_mode: Option<RiskMode>,
}

pub struct SetRiskCurveAbsolutePointInput {
    pub vault_balance: u64,
    pub price_factor_q32: u64,
    pub interpolation: Option<Interpolation>,
    pub params: Option<[u8; 4]>,
}

pub struct SetRiskCurveAbsoluteParams {
    pub side: Side,
    pub default_interpolation: Interpolation,
    pub points: Vec<SetRiskCurveAbsolutePointInput>,
    pub slot: Option<u8>,
    pub risk_mode: Option<RiskMode>,
}

pub struct SetRiskCurveAbsoluteBothParams {
    pub bid: SetRiskCurveAbsoluteHalfParams,
    pub ask: SetRiskCurveAbsoluteHalfParams,
}

pub struct SetRiskCurveAbsoluteHalfParams {
    pub default_interpolation: Interpolation,
    pub points: Vec<SetRiskCurveAbsolutePointInput>,
    pub slot: Option<u8>,
    pub risk_mode: Option<RiskMode>,
}

pub struct UpdateMidpriceParams {
    pub midprice_q32: u64,
    pub sequence: Option<u64>,
}

pub struct UpdateBaseSpreadParams {
    pub spread_factor_q32: u64,
    pub sequence: Option<u64>,
}

pub struct UpdateMidpriceAndBaseSpreadParams {
    pub midprice_q32: u64,
    pub spread_factor_q32: u64,
    pub sequence: Option<u64>,
}

pub struct SwitchCurveParams {
    pub side: Side,
    pub slot: u8,
}

pub struct NominateAuthorityParams {
    pub new_authority: Pubkey,
    pub expiry_slot: u64,
}

pub struct SetQuotingAuthorityParams {
    pub new_quoting_authority: Pubkey,
    pub spread_config_pda: Option<Pubkey>,
}

pub struct InitializeFeeConfigParams {
    pub fee_ppm: u32,
    pub fee_admin: Pubkey,
    pub fee_recipient: Pubkey,
}

pub struct UpdateFeeConfigParams {
    /// None = no change (sends u32::MAX)
    pub fee_ppm: Option<u32>,
    /// None = no change (sends zeros)
    pub fee_recipient: Option<Pubkey>,
}

pub struct RotateFeeAdminParams {
    pub new_fee_admin: Pubkey,
}

pub struct InitializePoolFeeConfigParams {
    pub fee_ppm: u32,
    pub fee_admin: Pubkey,
    pub fee_recipient: Pubkey,
}

pub struct UpdatePoolFeeConfigParams {
    /// None = no change (sends u32::MAX)
    pub fee_ppm: Option<u32>,
    /// None = no change (sends zeros)
    pub fee_recipient: Option<Pubkey>,
}

pub struct SetPoolStateParams {
    pub new_state: PoolState,
}

pub struct UpdateDeltaStalenessParams {
    pub delta_staleness: u8,
}

pub struct InitializeSpreadConfigParams {
    pub admin: Pubkey,
}

#[derive(Debug, Clone)]
pub struct SpreadTriggerInput {
    pub account: Pubkey,
    pub spread_bps: u16,
}

pub struct UpdateSpreadConfigParams {
    pub triggers: Vec<SpreadTriggerInput>,
}

pub struct AllocateCurvePrefabsParams {
    pub seed: u64,
    pub mint_x: Pubkey,
    pub mint_y: Pubkey,
    pub max_prefab_slots: Option<u8>,
    pub max_curve_points: Option<u8>,
    pub authority: Pubkey,
}
