use lifeline::Receiver;
use thiserror::Error;
use tokio::sync::watch;

#[derive(Error, Debug)]
#[error("state never resolved to a value")]
pub struct StateUninitalizedError {}

#[allow(dead_code)]
pub async fn await_message<T: Clone, F, R>(
    channel: &mut impl Receiver<T>,
    mut condition: F,
) -> Result<R, StateUninitalizedError>
where
    F: FnMut(T) -> Option<R>,
{
    while let Some(message) = channel.recv().await {
        if let Some(ret) = condition(message) {
            return Ok(ret);
        }
    }

    Err(StateUninitalizedError {})
}

pub async fn await_state<T: Clone>(
    channel: &mut watch::Receiver<Option<T>>,
) -> Result<T, StateUninitalizedError> {
    if let Some(ref value) = *channel.borrow() {
        return Ok(value.clone());
    }

    while let Some(update) = channel.recv().await {
        if let Some(value) = update {
            return Ok(value);
        }
    }

    Err(StateUninitalizedError {})
}

pub async fn await_condition<T: Clone, F>(
    channel: &mut watch::Receiver<Option<T>>,
    mut condition: F,
) -> Result<T, StateUninitalizedError>
where
    T: Clone,
    F: FnMut(&T) -> bool,
{
    if let Some(ref value) = *channel.borrow() {
        if condition(value) {
            return Ok(value.clone());
        }
    }

    while let Some(update) = channel.recv().await {
        if let Some(value) = update {
            if condition(&value) {
                return Ok(value);
            }
        }
    }

    Err(StateUninitalizedError {})
}
