use futures_signals::{
    map_ref,
    signal::{Signal, SignalExt},
    signal_vec::{MutableVec, SignalVecExt},
};

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum CollectionState {
    #[default]
    Empty,
    NotEmpty,
    Pending,
}

impl CollectionState {
    pub fn empty(&self) -> bool {
        matches!(*self, Self::Empty)
    }

    pub fn empty_pending(&self) -> bool {
        matches!(*self, Self::Empty | Self::Pending)
    }

    pub fn not_empty(&self) -> bool {
        matches!(*self, Self::NotEmpty)
    }

    pub fn not_empty_pending(&self) -> bool {
        matches!(*self, Self::NotEmpty | Self::Pending)
    }

    pub fn pending(&self) -> bool {
        matches!(*self, Self::Pending)
    }
}

pub fn combine_collection_states_2<S1, S2>(cs1: S1, cs2: S2) -> impl Signal<Item = CollectionState>
where
    S1: Signal<Item = CollectionState>,
    S2: Signal<Item = CollectionState>,
{
    map_ref!(
        cs1, cs2 => {
            match (cs1, cs2) {
                (CollectionState::Pending, _) | (_, CollectionState::Pending) => CollectionState::Pending,
                (CollectionState::NotEmpty, _) | (_, CollectionState::NotEmpty) => CollectionState::NotEmpty,
                (CollectionState::Empty, CollectionState::Empty) => CollectionState::Empty,
            }
        }
    )
}

pub fn combine_collection_states_3<S1, S2, S3>(
    cs1: S1,
    cs2: S2,
    cs3: S3,
) -> impl Signal<Item = CollectionState>
where
    S1: Signal<Item = CollectionState>,
    S2: Signal<Item = CollectionState>,
    S3: Signal<Item = CollectionState>,
{
    map_ref!(
        cs1, cs2, cs3 => {
            match (cs1, cs2, cs3) {
                (CollectionState::Pending, _, _)
                | (_, CollectionState::Pending, _)
                | (_, _, CollectionState::Pending) => CollectionState::Pending,
                (CollectionState::NotEmpty, _, _)
                | (_, CollectionState::NotEmpty, _)
                | (_, _, CollectionState::NotEmpty) => CollectionState::NotEmpty,
                (CollectionState::Empty, CollectionState::Empty, CollectionState::Empty) => CollectionState::Empty,
            }
        }
    )
}

pub fn collection_state_from_vec<T, S>(
    vec: &MutableVec<T>,
    pending: S,
) -> impl Signal<Item = CollectionState> + use<T, S>
where
    T: Clone,
    S: Signal<Item = bool>,
{
    let empty = vec.signal_vec_cloned().is_empty();
    map_ref! {
        pending, empty => (*pending, *empty)
    }
    .map(|state| match state {
        (true, _) => CollectionState::Pending,
        (false, true) => CollectionState::Empty,
        (false, false) => CollectionState::NotEmpty,
    })
}
