use std::path::{Path, PathBuf};

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum PlaylistKind {
    M3u,
    Pls,
}

/// Classify a path by extension into a playlist kind, or None if it is not a
/// recognized playlist file. Case-insensitive: m3u/m3u8 -> M3u, pls -> Pls.
pub fn playlist_kind(path: &Path) -> Option<PlaylistKind> {
    let ext = path.extension()?.to_string_lossy();
    match ext.to_ascii_lowercase().as_str() {
        "m3u" | "m3u8" => Some(PlaylistKind::M3u),
        "pls" => Some(PlaylistKind::Pls),
        _ => None,
    }
}

/// Parse an m3u/m3u8 or pls playlist into local audio file paths. Remote entries
/// (http/https/ftp URLs) and non-audio entries are skipped; relative paths are
/// resolved against `base_dir` (the playlist file's parent directory). Order is
/// preserved. Does not touch the filesystem.
pub fn parse_playlist(contents: &str, base_dir: &Path, kind: PlaylistKind) -> Vec<PathBuf> {
    let entries: Vec<&str> = match kind {
        PlaylistKind::M3u => contents
            .lines()
            .map(str::trim)
            .filter(|line| !line.is_empty() && !line.starts_with('#'))
            .collect(),
        PlaylistKind::Pls => contents
            .lines()
            .filter_map(|line| {
                let (key, value) = line.split_once('=')?;
                key.trim()
                    .to_ascii_lowercase()
                    .starts_with("file")
                    .then_some(value.trim())
            })
            .filter(|line| !line.is_empty())
            .collect(),
    };

    entries
        .into_iter()
        .filter_map(|entry| playlist_entry_path(entry, base_dir))
        .collect()
}

fn playlist_entry_path(entry: &str, base_dir: &Path) -> Option<PathBuf> {
    let lower = entry.to_ascii_lowercase();
    if lower.starts_with("http://") || lower.starts_with("https://") || lower.starts_with("ftp://")
    {
        return None;
    }

    let path = Path::new(entry);
    let resolved = if path.is_absolute() {
        path.to_path_buf()
    } else {
        base_dir.join(path)
    };
    let file_name = resolved.file_name()?.to_string_lossy();
    crate::files::looks_like_audio_name(&file_name).then_some(resolved)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn m3u_skips_comments_and_remote_urls() {
        let base = Path::new("/music/lists");
        let contents = "\
#EXTM3U
#EXTINF:123,Artist - Song
tracks/song.ogg
https://example.com/stream.mp3
";
        assert_eq!(
            parse_playlist(contents, base, PlaylistKind::M3u),
            vec![PathBuf::from("/music/lists/tracks/song.ogg")]
        );
    }

    #[test]
    fn pls_keeps_file_values_and_skips_non_audio() {
        let base = Path::new("/music");
        let contents = "\
[playlist]
File1=one.flac
Title1=One
File2=notes.txt
File3=/var/audio/two.MP3
";
        assert_eq!(
            parse_playlist(contents, base, PlaylistKind::Pls),
            vec![
                PathBuf::from("/music/one.flac"),
                PathBuf::from("/var/audio/two.MP3"),
            ]
        );
    }

    #[test]
    fn playlist_kind_is_case_insensitive() {
        assert_eq!(playlist_kind(Path::new("mix.M3U")), Some(PlaylistKind::M3u));
        assert_eq!(
            playlist_kind(Path::new("mix.m3u8")),
            Some(PlaylistKind::M3u)
        );
        assert_eq!(playlist_kind(Path::new("mix.PLS")), Some(PlaylistKind::Pls));
        assert_eq!(playlist_kind(Path::new("mix.txt")), None);
    }
}
