use crate::consumer::ConsumerDelegate;

use std::{
    fmt,
    sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
};

#[derive(Clone, Default)]
pub(crate) struct ConsumerStatus(Arc<RwLock<ConsumerStatusInner>>);

impl ConsumerStatus {
    pub(crate) fn state(&self) -> ConsumerState {
        self.read().state()
    }

    pub(crate) fn delegate(&self) -> Option<Arc<dyn ConsumerDelegate>> {
        self.read().delegate()
    }

    pub(crate) fn try_read(&self) -> Option<RwLockReadGuard<'_, ConsumerStatusInner>> {
        self.0.try_read().ok()
    }

    pub(crate) fn read(&self) -> RwLockReadGuard<'_, ConsumerStatusInner> {
        self.0.read().unwrap_or_else(|e| e.into_inner())
    }

    pub(crate) fn write(&self) -> RwLockWriteGuard<'_, ConsumerStatusInner> {
        self.0.write().unwrap_or_else(|e| e.into_inner())
    }
}

impl fmt::Debug for ConsumerStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_struct("ConsumerStatus");
        if let Some(inner) = self.try_read() {
            debug.field("state", &inner.state);
        }
        debug.finish()
    }
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) enum ConsumerState {
    #[default]
    Active,
    ActiveWithDelegate,
    Canceling,
    Canceled,
}

impl ConsumerState {
    pub(crate) fn is_active(self) -> bool {
        matches!(
            self,
            ConsumerState::Active | ConsumerState::ActiveWithDelegate
        )
    }
}

#[derive(Default)]
pub(crate) struct ConsumerStatusInner {
    state: ConsumerState,
    delegate: Option<Arc<dyn ConsumerDelegate>>,
}

impl ConsumerStatusInner {
    pub(crate) fn state(&self) -> ConsumerState {
        self.state
    }

    pub(crate) fn delegate(&self) -> Option<Arc<dyn ConsumerDelegate>> {
        self.delegate.clone()
    }

    pub(crate) fn set_delegate(&mut self, delegate: Option<Arc<dyn ConsumerDelegate>>) {
        if self.state.is_active() {
            self.state = ConsumerState::ActiveWithDelegate;
            self.delegate = delegate;
        }
    }

    pub(crate) fn start_cancel(&mut self) {
        self.state = ConsumerState::Canceling;
        self.delegate = None;
    }

    pub(crate) fn cancel(&mut self) {
        self.state = ConsumerState::Canceled;
        self.delegate = None;
    }
}
