use near_api::{
    types::{tokens::FTBalance, AccountId},
    Contract, NetworkConfig, Signer, Tokens,
};
use near_sandbox::{config::DEFAULT_GENESIS_ACCOUNT, GenesisAccount, SandboxConfig};

use serde_json::json;
use testresult::TestResult;

#[tokio::main]
async fn main() -> TestResult {
    let token = GenesisAccount::generate_with_name("token".parse()?);
    let account: AccountId = DEFAULT_GENESIS_ACCOUNT.into();
    let token_signer = Signer::from_secret_key(token.private_key.clone().parse()?)?;

    let sandbox = near_sandbox::Sandbox::start_sandbox_with_config(SandboxConfig {
        additional_accounts: vec![token.clone()],
        ..Default::default()
    })
    .await?;
    let network = NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);

    // Deploying token contract
    Contract::deploy(token.account_id.clone())
        .use_code(include_bytes!("../resources/fungible_token.wasm").to_vec())
        .with_init_call(
            "new_default_meta",
            json!({
                    "owner_id": token.account_id.clone(),
                "total_supply": "1000000000000000000000000000"
            }),
        )?
        .with_signer(token_signer.clone())
        .send_to(&network)
        .await?
        .assert_success();

    // Verifying that user has 1000 tokens
    let tokens = Tokens::account(token.account_id.clone())
        .ft_balance(token.account_id.clone())
        .fetch_from(&network)
        .await?;

    println!("Owner has {tokens}");

    // Transfer 100 tokens to the account
    // We handle internally the storage deposit for the receiver account
    Tokens::account(token.account_id.clone())
        .send_to(account.clone())
        .ft(
            token.account_id.clone(),
            // Send 1.5 tokens
            FTBalance::with_decimals(24).with_whole_amount(100),
        )
        .with_signer(token_signer.clone())
        .send_to(&network)
        .await?
        .assert_success();

    let tokens = Tokens::account(account.clone())
        .ft_balance(token.account_id.clone())
        .fetch_from(&network)
        .await?;

    println!("Account has {tokens}");

    let tokens = Tokens::account(token.account_id.clone())
        .ft_balance(token.account_id.clone())
        .fetch_from(&network)
        .await?;

    println!("Owner has {tokens}");

    // We validate decimals at the network level so this should fail with a validation error
    let token = Tokens::account(token.account_id.clone())
        .send_to(account.clone())
        .ft(
            token.account_id.clone(),
            FTBalance::with_decimals(8).with_whole_amount(100),
        )
        .with_signer(token_signer)
        .send_to(&network)
        .await;

    assert!(token.is_err());
    println!(
        "Expected decimal validation error: {}",
        token.err().ok_or("Error is none")?
    );

    Ok(())
}
