use local_ip_address::local_ip;
use rocket::{get, serde::json::Json};
use serde::{Deserialize, Serialize};
use std::{
    ops::Deref,
    time::{SystemTime, UNIX_EPOCH},
};

use crate::SharedCounter;

#[cfg(all(feature = "throughput", feature = "rtt"))]
#[derive(Debug, Deserialize)]
pub struct StartDemoRequest {
    endpoint_ip: Vec<String>,
    ping_ip: String,
    stream_url: String,
}

#[cfg(all(feature = "throughput", not(feature = "rtt")))]
#[derive(Debug, Deserialize)]
pub struct StartDemoRequest {
    endpoint_ip: Vec<String>,
    stream_url: String,
}

#[cfg(all(not(feature = "throughput"), feature = "rtt"))]
#[derive(Debug, Deserialize)]
pub struct StartDemoRequest {
    endpoint_ip: Vec<String>,
    ping_ip: String,
}

#[derive(Debug, Deserialize)]
pub struct DataCollectionStatsRequest {
    id: i32,
}

#[derive(Debug, Serialize)]
pub struct DataNode {
    id: i32,
    ip: String,
    dataset_size: f32,
    time_since_last_record: u64,
}

#[get("/demo/start", format = "json", data = "<data>")]
pub fn start_demo(
    state: &rocket::State<SharedCounter>,
    data: Json<StartDemoRequest>,
) -> &'static str {
    {
        let (local_state, cvar) = state.inner().deref();
        let mut local_state = local_state.lock().unwrap();
        local_state.started = true;
        local_state.endpoint_ip = Some(data.endpoint_ip.clone());

        #[cfg(feature = "rtt")]
        {
            local_state.ping_ip = Some(data.ping_ip.clone());
        }

        #[cfg(feature = "throughput")]
        {
            local_state.stream_url = Some(data.stream_url.clone());
        }

        cvar.notify_one();
    }
    "Ok"
}

#[get("/data_collection/get_data_stats", format = "json", data = "<data>")]
pub fn get_counter(
    state: &rocket::State<SharedCounter>,
    data: Json<DataCollectionStatsRequest>,
) -> Json<DataNode> {
    // Get counter value
    let (counter_val, last_visited): (f32, u64) = {
        let (local_state, _) = state.inner().deref();
        let local_state = local_state.lock().unwrap();
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();

        (local_state.counter as f32, now - local_state.last_check)
    };

    // Reset counter now that it has been seen
    {
        let (local_state, _) = state.inner().deref();
        let mut local_state = local_state.lock().unwrap();
        local_state.counter = 0;
        local_state.last_check = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
    }

    // Response
    Json(DataNode {
        id: data.id,
        ip: local_ip().unwrap().to_string(),
        dataset_size: counter_val,
        time_since_last_record: last_visited,
    })
}
