use lunar_lib::database::{Db, Entry, Id, TransactionError};

use crate::{database::Library, library::artist::Artist};

pub trait ArtistGroup {
    fn main_artist_id(&self) -> Option<Id<Artist>>;

    fn main_artist(&self, db: &Db<Library>) -> Result<Option<Entry<Artist>>, TransactionError>;

    fn featuring_artists(&self) -> &[Id<Artist>];

    fn add_artist(&mut self, artist: Id<Artist>);

    fn add_artists(&mut self, artists: impl IntoIterator<Item = Id<Artist>>);

    fn remove_artist(&mut self, artist: Id<Artist>);

    fn remove_artists(&mut self, artists: impl IntoIterator<Item = Id<Artist>>);
}

impl ArtistGroup for Vec<Id<Artist>> {
    fn main_artist_id(&self) -> Option<Id<Artist>> {
        self.first().copied()
    }

    fn main_artist(&self, db: &Db<Library>) -> Result<Option<Entry<Artist>>, TransactionError> {
        if let Some(first) = self.first() {
            let artist = first
                .db_get(db)?
                .expect("ArtistGroup main artist did not exist in the database");
            Ok(Some(artist))
        } else {
            Ok(None)
        }
    }

    fn featuring_artists(&self) -> &[Id<Artist>] {
        self.get(1..).unwrap_or_default()
    }

    fn add_artist(&mut self, artist: Id<Artist>) {
        if !self.contains(&artist) {
            self.push(artist);
        }
    }

    fn add_artists(&mut self, artists: impl IntoIterator<Item = Id<Artist>>) {
        artists.into_iter().for_each(|artist| {
            self.add_artist(artist);
        });
    }

    fn remove_artist(&mut self, artist: Id<Artist>) {
        self.retain(|a| *a != artist);
    }

    fn remove_artists(&mut self, artists: impl IntoIterator<Item = Id<Artist>>) {
        let artists: Vec<Id<Artist>> = artists.into_iter().collect();
        self.retain(|a| !artists.contains(a));
    }
}
