use serde::{Deserialize, Serialize};
pub use tokio::sync::broadcast::{self, Receiver, Sender};
use tracing::*;
use uuid::Uuid;

use crate::{LbErrKind, LocalLb};

#[derive(Clone)]
pub struct EventSubs {
    tx: Sender<Event>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Event {
    /// A metadata for a given id or it's descendants changed. The id returned
    /// may be deleted. Updates to document contents will not cause this
    /// message to be sent (unless a document was deleted).
    MetadataChanged(Actor),

    /// The contents of this document have changed either by this lb
    /// library or as a result of sync
    DocumentWritten(Uuid, Actor),

    PendingSharesChanged,

    Sync(SyncIncrement),

    StatusUpdated,

    UserSignedIn,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Actor {
    User,
    Sync,
}

impl Default for EventSubs {
    fn default() -> Self {
        let (tx, _) = broadcast::channel::<Event>(10000);
        Self { tx }
    }
}

impl EventSubs {
    pub(crate) fn pending_shares_changed(&self) {
        self.queue(Event::PendingSharesChanged);
    }

    pub(crate) fn meta_changed(&self, actor: Actor) {
        self.queue(Event::MetadataChanged(actor));
    }

    pub(crate) fn doc_written(&self, id: Uuid, actor: Actor) {
        self.queue(Event::DocumentWritten(id, actor));
    }

    pub(crate) fn sync_update(&self, s: SyncIncrement) {
        self.queue(Event::Sync(s));
    }

    pub(crate) fn status_updated(&self) {
        self.queue(Event::StatusUpdated);
    }

    /// executed after root and account are created
    pub(crate) fn signed_in(&self) {
        self.queue(Event::UserSignedIn);
    }

    fn queue(&self, evt: Event) {
        if let Err(e) = self.tx.send(evt.clone()) {
            error!(?evt, ?e, "could not queue");
        }
    }
}

impl LocalLb {
    pub fn subscribe(&self) -> Receiver<Event> {
        self.events.tx.subscribe()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SyncIncrement {
    SyncStarted,
    PullingDocument(Uuid, bool),
    PushingDocument(Uuid, bool),
    SyncFinished(Option<LbErrKind>),
}
