use solana_client::rpc_client::RpcClient;
use solana_sdk::pubkey::Pubkey;

use crate::accounts::*;
use crate::constants::HADRON_PROGRAM_ID;
use crate::helpers::derive::*;
use crate::types::HadronSdkError;
use crate::Hadron;

impl Hadron {
    /// Load a Hadron instance from an existing pool address via RPC.
    /// Fetches config first, then batch-fetches remaining accounts.
    pub fn load(rpc: &RpcClient, pool_address: &Pubkey) -> Result<Self, HadronSdkError> {
        let program_id = HADRON_PROGRAM_ID;

        // Fetch config first to get pool identity
        let config_account = rpc
            .get_account(pool_address)
            .map_err(HadronSdkError::Rpc)?;
        let config = decode_config(&config_account.data)?;

        // Derive addresses
        let addresses = derive_pool_addresses(
            config.seed,
            &config.mint_x,
            &config.mint_y,
            &config.token_program_x,
            &config.token_program_y,
            &program_id,
        );

        // Batch-fetch remaining accounts
        let accounts_to_fetch = &[
            addresses.midprice_oracle,
            addresses.curve_meta,
            addresses.curve_prefabs,
        ];
        let fetched = rpc
            .get_multiple_accounts(accounts_to_fetch)
            .map_err(HadronSdkError::Rpc)?;

        let oracle_account = fetched[0]
            .as_ref()
            .ok_or(HadronSdkError::AccountNotFound("MidpriceOracle"))?;
        let curve_meta_account = fetched[1]
            .as_ref()
            .ok_or(HadronSdkError::AccountNotFound("CurveMeta"))?;
        let curve_prefabs_account = fetched[2]
            .as_ref()
            .ok_or(HadronSdkError::AccountNotFound("CurvePrefabs"))?;

        let oracle = decode_midprice_oracle(&oracle_account.data)?;
        let curve_meta = decode_curve_meta(&curve_meta_account.data)?;

        Ok(Self {
            program_id,
            pool_address: *pool_address,
            addresses,
            config,
            oracle,
            curve_meta,
            curve_prefabs_data: curve_prefabs_account.data.clone(),
        })
    }

    /// Load a Hadron instance from pool identity (seed + mints) via RPC.
    pub fn load_from_seed(
        rpc: &RpcClient,
        seed: u64,
        mint_x: &Pubkey,
        mint_y: &Pubkey,
    ) -> Result<Self, HadronSdkError> {
        let program_id = HADRON_PROGRAM_ID;
        let (pool_address, _) = get_config_address(seed, mint_x, mint_y, &program_id);
        Self::load(rpc, &pool_address)
    }

    /// Re-fetch all account state from the chain.
    pub fn refetch_states(&mut self, rpc: &RpcClient) -> Result<(), HadronSdkError> {
        let accounts_to_fetch = &[
            self.pool_address,
            self.addresses.midprice_oracle,
            self.addresses.curve_meta,
            self.addresses.curve_prefabs,
        ];
        let fetched = rpc
            .get_multiple_accounts(accounts_to_fetch)
            .map_err(HadronSdkError::Rpc)?;

        if let Some(config_account) = &fetched[0] {
            self.config = decode_config(&config_account.data)?;
        }
        if let Some(oracle_account) = &fetched[1] {
            self.oracle = decode_midprice_oracle(&oracle_account.data)?;
        }
        if let Some(curve_meta_account) = &fetched[2] {
            self.curve_meta = decode_curve_meta(&curve_meta_account.data)?;
        }
        if let Some(curve_prefabs_account) = &fetched[3] {
            self.curve_prefabs_data = curve_prefabs_account.data.clone();
        }

        Ok(())
    }
}
