use std::time::Duration;

use lofty::{
    file::{AudioFile, TaggedFile, TaggedFileExt},
    tag::{Accessor, Tag, items::Timestamp},
};

use crate::{Metadata, MetadataError};

pub fn extract_metadata(file_path: &str) -> Result<Metadata, MetadataError> {
    let file = lofty::read_from_path(file_path).map_err(|_| MetadataError::ReadError)?;

    let metadata = match extract_tag(file_path) {
        Ok(t) => Metadata {
            title: extract_title(&t),
            artist: extract_artist(&t),
            album: extract_album(&t),
            time_stamp: extract_time_stamp(&t),
            genre: extract_genre(&t),
            cover: extract_cover(&t),
            duration: extract_duration(&file),
            sample_rate: extract_sample_rate(&file),
            channels: extract_channels(&file),
            has_tag: true,
        },
        Err(_) => Metadata {
            title: None,
            artist: None,
            album: None,
            time_stamp: None,
            genre: None,
            cover: None,
            duration: extract_duration(&file),
            sample_rate: extract_sample_rate(&file),
            channels: extract_channels(&file),
            has_tag: false,
        },
    };

    Ok(metadata)
}

pub fn extract_tag(file_path: &str) -> Result<Tag, MetadataError> {
    let file = lofty::read_from_path(file_path).map_err(|_| MetadataError::ReadError)?;
    if let Some(tag) = file.primary_tag().or_else(|| file.first_tag()) {
        Ok(tag.clone())
    } else {
        Err(MetadataError::NoTag)
    }
}

pub fn extract_title(tag: &Tag) -> Option<String> {
    tag.title().map(|t| t.to_string())
}

pub fn extract_artist(tag: &Tag) -> Option<String> {
    tag.artist().map(|a| a.to_string())
}

pub fn extract_album(tag: &Tag) -> Option<String> {
    tag.album().map(|a| a.to_string())
}

pub fn extract_time_stamp(tag: &Tag) -> Option<Timestamp> {
    tag.date()
}

pub fn extract_genre(tag: &Tag) -> Option<String> {
    tag.genre().map(|g| g.to_string())
}

pub fn extract_cover(tag: &Tag) -> Option<Vec<u8>> {
    if let Some(cover) = tag.pictures().first() {
        Some(cover.data().to_vec())
    } else {
        None
    }
}

pub fn extract_sample_rate(file: &TaggedFile) -> Option<u32> {
    file.properties().sample_rate()
}

pub fn extract_duration(file: &TaggedFile) -> Duration {
    file.properties().duration()
}

pub fn extract_channels(file: &TaggedFile) -> Option<u8> {
    file.properties().channels()
}

pub fn extract_bitrate(file: &TaggedFile) -> Option<u32> {
    file.properties().audio_bitrate()
}
