use std::ops::Deref;

use futures_signals::signal::{Mutable, Signal};

use crate::MutableExt;

#[derive(Debug)]
pub struct MutableOption<T>(Mutable<Option<T>>);

impl<T> Default for MutableOption<T> {
    fn default() -> Self {
        Self(Mutable::new(None))
    }
}

impl<T> Clone for MutableOption<T> {
    fn clone(&self) -> Self {
        Self(self.0.clone())
    }
}

impl<T> Deref for MutableOption<T> {
    type Target = Mutable<Option<T>>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<T> MutableOption<T> {
    pub fn new(value: Option<T>) -> Self {
        Self(Mutable::new(value))
    }

    pub fn new_some(value: T) -> Self {
        Self(Mutable::new(Some(value)))
    }

    pub fn new_default() -> Self
    where
        T: Default,
    {
        Self(Mutable::new(Some(T::default())))
    }

    pub fn is_none(&self) -> bool {
        self.0.lock_ref().is_none()
    }

    pub fn is_some(&self) -> bool {
        self.0.lock_ref().is_some()
    }

    pub fn take(self) -> Option<T> {
        self.0.take()
    }

    pub fn take_if<F>(&self, mut f: F) -> Option<T>
    where
        F: FnMut(&T) -> bool,
    {
        let mut current = self.0.lock_mut();
        match &*current {
            Some(current_value) if f(current_value) => current.take(),
            _ => None,
        }
    }

    pub fn take_if_eq(&self, value: &T) -> Option<T>
    where
        T: PartialEq,
    {
        self.take_if(|current| current == value)
    }

    pub fn as_mutable(&self) -> Mutable<Option<T>> {
        self.0.clone()
    }

    pub fn inspect_some<F>(&self, mut f: F)
    where
        F: FnMut(&T),
    {
        if let Some(lock) = self.0.lock_ref().as_ref() {
            f(lock);
        }
    }

    pub fn inspect_some_mut<F>(&self, mut f: F)
    where
        F: FnMut(&mut T),
    {
        if let Some(lock) = self.0.lock_mut().as_mut() {
            f(lock);
        }
    }

    pub fn map<F>(&self, f: impl FnOnce(&T) -> F) -> Option<F> {
        self.0.lock_ref().as_ref().map(f)
    }

    pub fn map_or<U>(&self, f: impl FnOnce(&T) -> U, default: U) -> U {
        self.map(f).unwrap_or(default)
    }

    pub fn map_or_else<U, D>(&self, f: impl FnOnce(&T) -> U, default: D) -> U
    where
        D: FnOnce() -> U,
    {
        self.map(f).unwrap_or_else(default)
    }

    pub fn map_or_default<U>(&self, f: impl FnOnce(&T) -> U) -> U
    where
        U: Default,
    {
        self.map(f).unwrap_or_default()
    }

    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> Option<U>) -> Option<U> {
        self.0.lock_ref().as_ref().and_then(f)
    }

    pub fn signal_some_default(&self) -> impl Signal<Item = T> + use<T>
    where
        T: Default + Copy,
    {
        self.signal_map_some_default(|v| *v)
    }

    pub fn signal_cloned_some_default(&self) -> impl Signal<Item = T> + use<T>
    where
        T: Default + Clone,
    {
        self.signal_map_some_default(|v| v.clone())
    }

    pub fn signal_map<F, U>(&self, mut f: F) -> impl Signal<Item = U> + use<T, F, U>
    where
        F: FnMut(Option<&T>) -> U,
    {
        self.0.signal_ref(move |v| f(v.as_ref()))
    }

    pub fn signal_map_some<F, U>(&self, mut f: F) -> impl Signal<Item = Option<U>> + use<T, F, U>
    where
        F: FnMut(&T) -> U,
    {
        self.0.signal_ref(move |v| v.as_ref().map(&mut f))
    }

    pub fn signal_map_some_or<F, U>(
        &self,
        mut f: F,
        default: U,
    ) -> impl Signal<Item = U> + use<T, F, U>
    where
        F: FnMut(&T) -> U,
        U: Clone,
    {
        self.0
            .signal_ref(move |v| v.as_ref().map(&mut f).unwrap_or(default.clone()))
    }

    pub fn signal_map_some_or_else<F, D, U>(
        &self,
        mut f: F,
        default: D,
    ) -> impl Signal<Item = U> + use<T, F, D, U>
    where
        F: FnMut(&T) -> U,
        D: FnOnce() -> U + Clone,
    {
        self.0
            .signal_ref(move |v| v.as_ref().map(&mut f).unwrap_or_else(default.clone()))
    }

    pub fn signal_map_some_default<F, U>(&self, mut f: F) -> impl Signal<Item = U> + use<T, F, U>
    where
        F: FnMut(&T) -> U,
        U: Default,
    {
        self.0
            .signal_ref(move |v| v.as_ref().map(&mut f).unwrap_or_default())
    }

    pub fn signal_and_then_some<F, U>(
        &self,
        mut f: F,
    ) -> impl Signal<Item = Option<U>> + use<T, F, U>
    where
        F: FnMut(&T) -> Option<U>,
    {
        self.0.signal_ref(move |v| v.as_ref().and_then(&mut f))
    }

    pub fn signal_and_then_some_or<F, U>(
        &self,
        mut f: F,
        default: U,
    ) -> impl Signal<Item = U> + use<T, F, U>
    where
        F: FnMut(&T) -> Option<U>,
        U: Clone,
    {
        self.0.signal_ref(move |v| {
            v.as_ref()
                .and_then(&mut f)
                .unwrap_or_else(|| default.clone())
        })
    }

    pub fn signal_and_then_some_or_else<F, D, U>(
        &self,
        mut f: F,
        default: D,
    ) -> impl Signal<Item = U> + use<T, F, D, U>
    where
        F: FnMut(&T) -> Option<U>,
        D: FnOnce() -> U + Clone,
    {
        self.0
            .signal_ref(move |v| v.as_ref().and_then(&mut f).unwrap_or_else(default.clone()))
    }
}
