use std::path::Path;

use crate::{config::Config, error::AppError, media::probe};

use super::{PlaybackPlan, VideoEncoder, VideoPipeline};

pub async fn plan(config: &Config, media: &Path) -> Result<PlaybackPlan, AppError> {
    let metadata = probe::run(config, media).await?;
    let video = metadata
        .streams
        .iter()
        .find(|stream| stream.codec_type.as_deref() == Some("video"))
        .ok_or_else(|| AppError::bad_request("media file has no video stream"))?;
    let video_pipeline =
        select_video_pipeline(video.codec_name.as_deref(), video.pix_fmt.as_deref());
    let audio_streams = metadata
        .streams
        .iter()
        .filter(|stream| stream.codec_type.as_deref() == Some("audio"))
        .map(|stream| stream.index)
        .collect();
    let duration_seconds = metadata
        .format
        .and_then(|format| format.duration)
        .and_then(|duration| duration.parse().ok());
    Ok(PlaybackPlan::live_dash(
        video_pipeline,
        audio_streams,
        duration_seconds,
    ))
}

fn select_video_pipeline(codec: Option<&str>, pixel_format: Option<&str>) -> VideoPipeline {
    if codec == Some("h264") && matches!(pixel_format, Some("yuv420p" | "yuvj420p")) {
        VideoPipeline::Copy
    } else {
        VideoPipeline::Encode(VideoEncoder::portable_software())
    }
}

#[cfg(test)]
mod tests {
    use super::{VideoEncoder, VideoPipeline, select_video_pipeline};

    #[test]
    fn copies_only_browser_compatible_h264() {
        assert_eq!(
            select_video_pipeline(Some("h264"), Some("yuv420p")),
            VideoPipeline::Copy
        );
        assert_eq!(
            select_video_pipeline(Some("mpeg4"), Some("yuv420p")),
            VideoPipeline::Encode(VideoEncoder::Libx264)
        );
        assert_eq!(
            select_video_pipeline(Some("h264"), Some("yuv420p10le")),
            VideoPipeline::Encode(VideoEncoder::Libx264)
        );
    }
}
