//
// Copyright (c) Cryptic Dot
//
// Modification based on Orca Whirlpools (https://github.com/orca-so/whirlpools),
// originally licensed under the Apache License, Version 2.0, prior to February 26, 2025.
//
// Modifications licensed under FusionAMM SDK Source-Available License v1.0
// See the LICENSE file in the project root for license information.
//

use serde::Deserialize;
use serde_json::from_value;
use solana_account_decoder::UiAccountData;
use solana_client::{nonblocking::rpc_client::RpcClient, rpc_request::TokenAccountsFilter};
use solana_program::pubkey::Pubkey;
use solana_program::rent::Rent;
use solana_program::sysvar::SysvarId;
use std::{error::Error, str::FromStr};

#[derive(Debug, Clone)]
pub struct ParsedTokenAccount {
    pub pubkey: Pubkey,
    pub token_program: Pubkey,
    pub mint: Pubkey,
    pub amount: u64,
}

#[derive(Deserialize, Clone, Debug)]
struct Parsed {
    info: SplToken,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
struct SplToken {
    mint: String,
    token_amount: Amount,
}

#[derive(Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
#[allow(dead_code)]
struct Amount {
    amount: String,
    ui_amount_string: String,
    ui_amount: f64,
    decimals: u8,
}

pub(crate) async fn get_token_accounts_for_owner(
    rpc: &RpcClient,
    owner: Pubkey,
    filter: TokenAccountsFilter,
) -> Result<Vec<ParsedTokenAccount>, Box<dyn Error>> {
    let accounts = rpc.get_token_accounts_by_owner(&owner, filter).await?;

    let mut token_accounts: Vec<ParsedTokenAccount> = Vec::new();
    for account in accounts {
        if let UiAccountData::Json(data) = account.account.data {
            let token_program = match data.program.as_str() {
                "spl-token" => &spl_token::ID.to_string(),
                "spl-token-2022" => &spl_token_2022::ID.to_string(),
                pubkey => pubkey,
            };
            let token: Parsed = from_value(data.parsed)?;
            token_accounts.push(ParsedTokenAccount {
                pubkey: Pubkey::from_str(&account.pubkey)?,
                token_program: Pubkey::from_str(token_program)?,
                mint: Pubkey::from_str(&token.info.mint)?,
                amount: token.info.token_amount.amount.parse::<u64>()?,
            });
        }
    }
    Ok(token_accounts)
}

pub(crate) async fn get_rent(rpc: &RpcClient) -> Result<Rent, Box<dyn Error>> {
    let rent = rpc.get_account(&Rent::id()).await?;
    let rent: Rent = bincode::deserialize(&rent.data)?;
    Ok(rent)
}
