use std::{path::PathBuf, process::Stdio};

// yt metadata
pub fn get_youtube_metadata(path: &PathBuf) -> Option<YouTubeMeta> {
    let cmd = std::process::Command::new("mkvextract")
        .arg("-q")
        .arg("attachments")
        .arg(path.to_str().unwrap())
        .arg("1:/proc/self/fd/1")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .unwrap();

    let out = cmd.wait_with_output().unwrap();

    let stdout = String::from_utf8_lossy(&out.stdout).to_string();
    //let stderr = String::from_utf8_lossy(&out.stderr).to_string();

    // println!("path {path:?} -- stdout {stdout} -- stderr {stderr}");

    let val = serde_json::from_str(&stdout).ok()?;
    log::info!("Extracted YouTube Metadata from {path:?}");
    Some(YouTubeMeta { inner: val })
}

pub struct YouTubeMeta {
    inner: serde_json::Value,
}

impl YouTubeMeta {
    pub fn title(&self) -> String {
        self.inner
            .as_object()
            .unwrap()
            .get("title")
            .unwrap()
            .as_str()
            .unwrap()
            .to_owned()
    }

    pub fn youtube_id(&self) -> Option<String> {
        self.inner
            .as_object()
            .unwrap()
            .get("id")
            .unwrap()
            .as_str()
            .map(std::borrow::ToOwned::to_owned)
    }

    pub fn description(&self) -> Option<String> {
        self.inner
            .as_object()
            .unwrap()
            .get("description")
            .unwrap()
            .as_str()
            .map(std::borrow::ToOwned::to_owned)
    }

    pub fn uploader_name(&self) -> Option<String> {
        self.inner
            .as_object()
            .unwrap()
            .get("uploader")
            .unwrap()
            .as_str()
            .map(std::borrow::ToOwned::to_owned)
    }

    pub fn uploader_id(&self) -> Option<String> {
        self.inner
            .as_object()
            .unwrap()
            .get("channel_id")
            .unwrap()
            .as_str()
            .map(std::borrow::ToOwned::to_owned)
    }

    pub fn duration(&self) -> Option<usize> {
        self.inner
            .as_object()
            .unwrap()
            .get("duration")
            .unwrap()
            .as_i64()
            .map(|x| (x as usize).to_owned())
    }

    pub fn views(&self) -> Option<usize> {
        self.inner
            .as_object()
            .unwrap()
            .get("view_count")
            .unwrap()
            .as_i64()
            .map(|x| (x as usize).to_owned())
    }

    pub fn categories(&self) -> Option<Vec<String>> {
        self.inner
            .as_object()
            .unwrap()
            .get("categories")
            .unwrap()
            .as_array()
            .unwrap()
            .iter()
            .map(|x| x.as_str().map(std::borrow::ToOwned::to_owned))
            .collect()
    }

    pub fn tags(&self) -> Option<Vec<String>> {
        if let Some(tags) = self.inner.as_object().unwrap().get("tags") {
            return tags
                .as_array()
                .unwrap()
                .iter()
                .map(|x| x.as_str().map(std::borrow::ToOwned::to_owned))
                .collect();
        }
        None
    }

    pub fn upload_date(&self) -> Option<String> {
        self.inner
            .as_object()
            .unwrap()
            .get("upload_date")
            .unwrap()
            .as_str()
            .map(std::borrow::ToOwned::to_owned)
    }
}
