use std::collections::HashMap;
use std::str::FromStr;
use zbus::zvariant::OwnedValue;

use crate::error::{Error, Result};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PlaybackStatus {
    Playing,
    Paused,
    Stopped,
}

impl FromStr for PlaybackStatus {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self> {
        match s {
            "Playing" => Ok(Self::Playing),
            "Paused" => Ok(Self::Paused),
            "Stopped" => Ok(Self::Stopped),
            _ => Err(Error::InvalidPlaybackStatus(s.to_string())),
        }
    }
}

#[derive(Debug, Clone, Default)]
pub struct Metadata {
    pub title: Option<String>,
    pub artist: Option<String>,
    pub album: Option<String>,
}

impl Metadata {
    pub fn from_mpris(map: HashMap<String, OwnedValue>) -> Self {
        let title = map
            .get("xesam:title")
            .and_then(|v| TryInto::<String>::try_into(v.clone()).ok());

        let artist = map.get("xesam:artist").and_then(|v| {
            if let Ok(arr) = TryInto::<Vec<String>>::try_into(v.clone()) {
                arr.into_iter().next()
            } else {
                TryInto::<String>::try_into(v.clone()).ok()
            }
        });

        let album = map
            .get("xesam:album")
            .and_then(|v| TryInto::<String>::try_into(v.clone()).ok());

        Self {
            title,
            artist,
            album,
        }
    }
}

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

    #[test]
    fn test_playback_status_from_str() {
        assert_eq!(
            "Playing".parse::<PlaybackStatus>().unwrap(),
            PlaybackStatus::Playing
        );
        assert_eq!(
            "Paused".parse::<PlaybackStatus>().unwrap(),
            PlaybackStatus::Paused
        );
        assert_eq!(
            "Stopped".parse::<PlaybackStatus>().unwrap(),
            PlaybackStatus::Stopped
        );
        assert!("Invalid".parse::<PlaybackStatus>().is_err());
    }

    #[test]
    fn test_metadata_default() {
        let meta = Metadata::default();
        assert!(meta.title.is_none());
        assert!(meta.artist.is_none());
        assert!(meta.album.is_none());
    }

    #[test]
    fn test_metadata_from_empty_map() {
        let map = HashMap::new();
        let meta = Metadata::from_mpris(map);
        assert!(meta.title.is_none());
        assert!(meta.artist.is_none());
        assert!(meta.album.is_none());
    }
}
