#[cfg(feature = "debug_telemetry")]
use bstr::ByteSlice;
use crate::common::git;
use std::{cell::RefCell, path::Path, rc::Rc, str};
use super::{frontend::Frontend, http};

#[derive(Clone, Debug)]
pub struct Telemetry {
    data: Rc<RefCell<TelemetryData>>,
}

#[derive(Debug)]
pub(crate) struct TelemetryData {
    session_id: uuid::Uuid,
    data: serde_json::Map<String, serde_json::Value>,
}

impl Telemetry {
    pub(crate) fn new() -> Self {
        Self {
            data: Rc::new(RefCell::new(TelemetryData::new(uuid::Uuid::new_v4()))),
        }
    }

    /// Take the data out of this Telemetry and return them, leaving empty data in place.
    /// This affects all clones. After calling this, the Telemetry can be reused for new
    /// submission.
    pub(crate) fn take(&self) -> TelemetryData {
        let new_data = TelemetryData::new(self.data.borrow().session_id);
        self.data.replace(new_data)
    }

    pub fn tele_set<T: serde::Serialize>(&self, key: &str, value: T) {
        self.data.borrow_mut().set(key, value);
    }

    pub(crate) fn set_repo_type(&self, repo: &git::Git) {
        let repo_type = if Path::new("kernel/panic.c").exists() {
            "kernel"
        } else if matches!(
            repo.current_branch().ok().flatten(),
            Some(branch) if branch == b"working-tree-next"
        ) {
            "dist-git"
        } else {
            "other"
        };
        self.tele_set("repo_type", repo_type);
    }
}

impl TelemetryData {
    fn new(session_id: uuid::Uuid) -> Self {
        let mut telemetry = Self {
            session_id,
            data: serde_json::Map::new(),
        };
        telemetry.set("version", env!("CARGO_PKG_VERSION"));
        telemetry.set("session_id", session_id.hyphenated());
        telemetry
    }

    fn set<T: serde::Serialize>(&mut self, key: &str, value: T) {
        self.data.insert(key.to_string(), serde_json::to_value(value).unwrap());
    }

    fn to_json(self, id: uuid::Uuid) -> String {
        let mut payload = serde_json::Value::Object(self.data);
        payload["id"] = serde_json::Value::String(id.hyphenated().to_string());
        let data = serde_json::json!({
            "jsonrpc": "2.0",
            "method": "telemetry",
            "id": 0,
            "params": { "json": serde_json::to_string(&payload).unwrap() },
        });
        serde_json::to_string(&data).unwrap()
    }

    pub(crate) async fn submit(self, id: uuid::Uuid, frontend: impl Frontend) {
        let data = self.to_json(id);
        #[cfg(feature = "debug_telemetry")]
        println!("telemetry: submitting {data}");
        #[allow(unused_variables)]
        let result = http::post(
            "http://patchpal.usersys.redhat.com:9080/v1",
            data,
            &frontend,
        ).await;
        #[cfg(feature = "debug_telemetry")]
        match result {
            Ok(msg) => println!("telemetry: received {}", msg.as_bstr()),
            Err(msg) => println!("telemetry: error {msg}"),
        }
    }

    #[cfg(feature = "debug_telemetry")]
    pub(crate) fn print_data(self) {
        let data = self.to_json(uuid::Uuid::nil());
        println!("telemetry: would submit {data}");
    }
}
