use crate::api::MyceliumClient;
use crate::error::AppResult;
use serde::{Deserialize, Serialize};

/// The daemon serializes the metric as either an unsigned integer for
/// reachable routes, or the literal string "infinite" for poisoned ones.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Metric {
    Value(u64),
    Infinite(String),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Route {
    pub subnet: String,
    pub next_hop: String,
    pub metric: Metric,
    pub seqno: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct QueriedSubnet {
    pub subnet: String,
    pub expiration: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct RoutesSnapshot {
    pub selected: Vec<Route>,
    pub fallback: Vec<Route>,
    pub queried: Vec<QueriedSubnet>,
}

impl MyceliumClient {
    pub async fn routes_selected(&self) -> AppResult<Vec<Route>> {
        let r = self
            .http()
            .get(self.url("/admin/routes/selected"))
            .send()
            .await?;
        Self::parse(r).await
    }

    pub async fn routes_fallback(&self) -> AppResult<Vec<Route>> {
        let r = self
            .http()
            .get(self.url("/admin/routes/fallback"))
            .send()
            .await?;
        Self::parse(r).await
    }

    pub async fn routes_queried(&self) -> AppResult<Vec<QueriedSubnet>> {
        let r = self
            .http()
            .get(self.url("/admin/routes/queried"))
            .send()
            .await?;
        Self::parse(r).await
    }

    pub async fn routes_snapshot(&self) -> AppResult<RoutesSnapshot> {
        // Issue the three calls concurrently so the snapshot reflects a
        // near-coincident view of the routing table.
        let (sel, fb, q) = tokio::try_join!(
            self.routes_selected(),
            self.routes_fallback(),
            self.routes_queried(),
        )?;
        Ok(RoutesSnapshot {
            selected: sel,
            fallback: fb,
            queried: q,
        })
    }
}
