use {
    crate::{
        api::GetRandomValueResponse,
        chain::ethereum::SignablePythContract,
        config::{
            Config,
            GenerateOptions,
        },
    },
    anyhow::Result,
    base64::{
        engine::general_purpose::STANDARD as base64_standard_engine,
        Engine as _,
    },
    std::sync::Arc,
};

/// Run the entire random number generation protocol to produce a random number.
pub async fn generate(opts: &GenerateOptions) -> Result<()> {
    let contract = Arc::new(
        SignablePythContract::from_config(
            &Config::load(&opts.config.config)?.get_chain_config(&opts.chain_id)?,
            &opts.private_key,
        )
        .await?,
    );

    let user_randomness = rand::random::<[u8; 32]>();
    let provider = opts.provider;

    // Request a random number on the contract
    let sequence_number = contract
        .request_wrapper(&provider, &user_randomness, opts.blockhash)
        .await?;

    tracing::info!(sequence_number = sequence_number, "random number requested",);

    // Get the committed value from the provider
    let resp = reqwest::get(opts.url.join(&format!(
        "/v1/chains/{}/revelations/{}",
        opts.chain_id, sequence_number
    ))?)
    .await?
    .json::<GetRandomValueResponse>()
    .await?;

    tracing::info!(
        response = base64_standard_engine.encode(resp.value.data()),
        "Retrieved the provider's random value.",
    );
    let provider_randomness = resp.value.data();

    // Submit the provider's and our values to the contract to reveal the random number.
    let random_value = contract
        .reveal_wrapper(
            &provider,
            sequence_number,
            &user_randomness,
            &provider_randomness,
        )
        .await?;

    tracing::info!(
        number = base64_standard_engine.encode(random_value),
        "Random number generated."
    );

    Ok(())
}
