use ghost_test_service::{node_config, run_validator_node, test_prometheus_config};
use hyper::{Client, Uri};
use keyring::AccountKeyring::*;
use std::collections::HashMap;

const DEFAULT_PROMETHEUS_PORT: u16 = 9616;

#[tokio::test(flavor = "multi_thread")]
async fn runtime_can_publish_metrics() {
    let mut alice_config = node_config(
        || {},
        tokio::runtime::Handle::current(),
        Alice,
        Vec::new(),
        true,
    );

    // Enable prometheus metrics for Alice.
    alice_config.prometheus_config = Some(test_prometheus_config(DEFAULT_PROMETHEUS_PORT));

    let mut builder = sc_cli::LoggerBuilder::new("");

    // Enable profiling with `wasm_tracing` target.
    builder.with_profiling(Default::default(), String::from("wasm_tracing=trace"));

    // Setup the runtime metrics provider.
    crate::logger_hook()(&mut builder, &alice_config);

    builder.init().expect("Failed to set up logger");

    // Start validator Alice
    let alice = run_validator_node(alice_config, None);

    let bob_config = node_config(
        || {},
        tokio::runtime::Handle::current(),
        Bob,
        vec![alice.addr.clone()],
        true,
    );

    // Start validator Bob
    let _bob = run_validator_node(bob_config, None);

    // Wait for Alice to see finalized blocks.
    alice.wait_for_finalized_blocks(2).await;

    let metrics_uri = format!("http://localhost:{}/metrics", DEFAULT_PROMETHEUS_PORT);
    let metrics = scrape_prometheus_metrics(&metrics_uri).await;

    // TODO: which assert
}

async fn scrape_prometheus_metrics(metrics_uri: &str) -> HashMap<String, u64> {
    let res = Client::new()
        .get(Uri::try_from(metrics_uri).expect("bad URI"))
        .await
        .expect("GET request failed");

    let body = String::from_utf8(
        hyper::body::to_bytes(res)
            .await
            .expect("can't get body as bytes")
            .to_vec(),
    )
    .expect("body is not an UTF8 string");

    let lines: Vec<_> = body.lines().map(|s| Ok(s.to_owned())).collect();
    prometheus_parse::Scrape::parse(lines.into_iter())
        .expect("Scraper failed to parse Prometheus metrics")
        .samples
        .into_iter()
        .filter_map(
            |prometheus_parse::Sample { metric, value, .. }| match value {
                prometheus_parse::Value::Counter(value) => Some((metric, value as u64)),
                prometheus_parse::Value::Gauge(value) => Some((metric, value as u64)),
                prometheus_parse::Value::Untyped(value) => Some((metric, value as u64)),
                _ => None,
            },
        )
        .collect()
}
