use crate::{
    synchronization::sync::{SyncData, Syncable},
    ui::MaybeProgressBar,
};
use anyhow::{Result, anyhow};
use serde::Deserialize;
use serde_aux::field_attributes::deserialize_number_from_string;
use std::{collections::HashMap, io::Read, path::PathBuf, process::Stdio};

use super::sync::TimestampMS;

pub struct CueMarkersSynchronizer {
    pub path: PathBuf,
}

#[derive(Debug, Deserialize)]
struct FFprobeChapterTags {
    title: String,
}

#[derive(Debug, Deserialize)]
struct FFprobeChapter {
    // id: usize,
    // time_base: String,

    // start: usize,
    #[serde(deserialize_with = "deserialize_number_from_string")]
    start_time: f32,

    // end: usize,
    // #[serde(deserialize_with = "deserialize_number_from_string")]
    // end_time: f32,
    tags: FFprobeChapterTags,
}

#[derive(Debug, Deserialize)]
struct FFprobeOutput {
    chapters: Vec<FFprobeChapter>,
}

impl Syncable for CueMarkersSynchronizer {
    fn new(path: impl Into<PathBuf>) -> Self {
        Self { path: path.into() }
    }

    fn load(
        &self,
        progress: Option<&indicatif::ProgressBar>,
    ) -> Result<SyncData> {
        progress.set_length(4);
        progress.set_message("Running ffprobe");

        let mut ffprobe = std::process::Command::new("ffprobe")
            .args(["-v", "error"])
            .args(["-i", &self.path.to_string_lossy()])
            .args(["-output_format", "json"])
            .arg("-show_chapters")
            .stdout(Stdio::piped())
            .spawn()?;

        progress.inc(1);
        progress.set_message("Getting ffprobe output");

        let mut raw_output = String::new();
        ffprobe
            .stdout
            .take()
            .ok_or(anyhow!("Couldn't get stdout of ffprobe"))?
            .read_to_string(&mut raw_output)
            .map_err(|e| anyhow!("Couldn't read ffprobe stdout: {e:?}"))?;

        progress.inc(1);
        progress.set_message("Parsing ffprobe output");

        let output: FFprobeOutput = serde_json::from_str(&raw_output)?;

        progress.inc(1);
        progress.set_message("Gathering chapters");

        Ok(SyncData {
            stems: HashMap::new(),
            bpm: None,
            markers: output
                .chapters
                .iter()
                .map(|ch| {
                    (
                        (ch.start_time.to_owned() * 1_000.0) as TimestampMS,
                        ch.tags.title.clone(),
                    )
                })
                .collect(),
        })
    }
}
