use crate::UError;
use anyhow::Error;
use once_cell::sync::OnceCell;
use tokio::sync::mpsc::{channel, error::TryRecvError, Receiver, Sender};
use tokio::sync::{Mutex, MutexGuard};

type ChanError = Error;
static ERR_CHAN: OnceCell<Mutex<ErrChan>> = OnceCell::new();

pub struct ErrChan {
    tx: Sender<ChanError>,
    rx: Receiver<ChanError>,
}

impl ErrChan {
    async fn get() -> MutexGuard<'static, Self> {
        ERR_CHAN
            .get_or_init(|| {
                let (tx, rx) = channel(20);
                Mutex::new(Self { tx, rx })
            })
            .lock()
            .await
    }

    pub async fn send(err: impl Into<ChanError>, ctx: impl AsRef<str>) {
        let err = err.into();
        error!("Encountered an error at '{}': {:?}", ctx.as_ref(), err);
        Self::get().await.tx.try_send(err).unwrap();
    }

    pub async fn recv() -> Option<UError> {
        match Self::get().await.rx.try_recv() {
            Ok(err) => Some(UError::from(err)),
            Err(TryRecvError::Disconnected) => panic!("err chan disconnected"),
            Err(TryRecvError::Empty) => None,
        }
    }
}