use crate::prelude::*;
use crate::services::get_base_directory;
#[allow(non_snake_case)]
#[derive(Deserialize, Debug)]
pub struct TelemetryConfig {
    pub TELEMETRY: bool,
    pub TELEM_URL: Option<String>,
}

impl TelemetryConfig {
    pub fn init_tracer() -> opentelemetry_sdk::trace::TracerProvider {
        let cfg: TelemetryConfig = TelemetryConfig::load();
        if !cfg.TELEMETRY {
            return TracerProvider::builder().build();
        }
        let endpoint = cfg.TELEM_URL.unwrap_or_default();
        let channel = Channel::from_shared(endpoint.clone())
            .unwrap()
            .tls_config(ClientTlsConfig::new().with_native_roots())
            .unwrap()
            .connect_lazy();
        let exporter = opentelemetry_otlp::SpanExporter::builder()
            .with_tonic()
            .with_endpoint(endpoint.clone())
            .with_channel(channel)
            .build()
            .expect("Failed to build exporter");
        opentelemetry_sdk::trace::TracerProvider::builder()
            .with_simple_exporter(exporter)
            .with_resource(Resource::new(vec![KeyValue::new(
                "service.name",
                "LoxideLibs",
            )]))
            .build()
    }
    fn load() -> Self {
        let cfg_path = get_base_directory().join("config\\user_config.json");
        if !cfg_path.exists() {
            return Self {
                TELEMETRY: false,
                TELEM_URL: None,
            };
        }
        match fs::read_to_string(&cfg_path) {
            Ok(contents) => serde_json::from_str::<Self>(&contents).unwrap_or(Self {
                TELEMETRY: false,
                TELEM_URL: None,
            }),
            Err(_) => Self {
                TELEMETRY: false,
                TELEM_URL: None,
            },
        }
    }
}

#[derive(Debug, Deserialize, Serialize)]
pub struct ApiResponse {
    pub(crate) error: String,
    pub(crate) response: ExecHistories,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ExecHistories {
    pub(crate) exechistories: Vec<Group>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Group {
    pub(crate) checkpoint: String,
    #[serde(rename = "type")]
    pub(crate) exectype: u8,
    pub(crate) username: String,
    pub(crate) hostname: String,
    pub(crate) netdomain: String,
    pub(crate) filename: String,
    pub(crate) ppolicy: String,
    pub(crate) policyname: String,
    pub(crate) policyver: String,
    pub(crate) commandline: String,
    pub(crate) publisher: String,
    pub(crate) pprocess: String,
    pub(crate) gprocess: String,
    pub(crate) sha256: String,
    pub(crate) datetime: String,
    pub(crate) md5: String,
    pub(crate) sha128: String,
    pub(crate) sha384: String,
    pub(crate) sha512: String,
    pub(crate) ip: String,
    pub(crate) localip: String,
}

pub struct PyData {
    pub headers: reqwest::header::HeaderMap,
    pub base_url: String,
}

impl PyData {
    pub fn extract_data(py: Python<'_>, obj: &Py<PyAny>) -> Self {
        let headers_raw = obj.getattr(py, "headers").unwrap().to_string();
        let headers_json = headers_raw.replace('\'', "\"");
        let parsed: Value = serde_json::from_str(&headers_json).unwrap();
        let mut header_map = HeaderMap::new();
        if let Some(obj) = parsed.as_object() {
            for (key, val) in obj {
                if let Some(v) = val.as_str() {
                    let header_name = HeaderName::from_str(key).unwrap();
                    let header_value: HeaderValue = HeaderValue::from_str(v).unwrap();
                    header_map.insert(header_name, header_value);
                }
            }
        }
        let base_url = obj.getattr(py, "base_url").unwrap().to_string();
        Self {
            headers: header_map,
            base_url,
        }
    }
}

pub struct SkipBack;

impl SkipBack {
    pub fn find_checkpoint(days: i64) -> ObjectId {
        let date_days_ago = Local::now() - Duration::days(days);
        let timestamp = date_days_ago.timestamp() as u32;
        let mut hex_timestamp = String::new();
        write!(&mut hex_timestamp, "{:08x}", timestamp).unwrap();
        let objectid_hex = format!("{}0000000000000000", hex_timestamp);
        ObjectId::parse_str(&objectid_hex).expect("Invalid ObjectId hex")
    }
}
