use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

use serde::Serialize;

use crate::helper::{Assignable, Succ};
use crate::observe::{AsDerefMut, Observer, ObserverPointer, SerializeObserver, Unsigned, Zero};
use crate::{Adapter, Mutation, MutationKind};

/// A handler trait for implementing change detection strategies in [`GeneralObserver`].
///
/// `GeneralHandler` defines the interface for pluggable change detection strategies used
/// exclusively with `GeneralObserver`. Each handler implementation encapsulates a specific approach
/// to detecting whether a value has changed.
///
/// ## Lifecycle
///
/// - [`on_observe`](GeneralHandler::on_observe) - Called once when observation begins
/// - [`on_deref_mut`](GeneralHandler::on_deref_mut) - Called each time the value is accessed via
///   [`DerefMut`]
/// - [`on_collect`](GeneralHandler::on_collect) - Called once to determine if a change occurred
///
/// ## Example
///
/// A [`ShallowObserver`](super::ShallowObserver) implementation that treats any mutation through
/// [`DerefMut`] as a complete replacement:
///
/// ```
/// # use morphix::observe::{DefaultSpec, GeneralHandler, GeneralObserver};
/// #[derive(Default)]
/// struct ShallowHandler {
///     mutated: bool,
/// }
///
/// impl<T> GeneralHandler<T> for ShallowHandler {
///     type Spec = DefaultSpec;
///
///     fn on_observe(_value: &mut T) -> Self {
///         Self { mutated: false }
///     }
///
///     fn on_deref_mut(&mut self) {
///         self.mutated = true;
///     }
///
///     fn on_collect(&self, _value: &T) -> bool {
///         self.mutated
///     }
/// }
///
/// type ShallowObserver<'ob, T> = GeneralObserver<'ob, T, ShallowHandler>;
/// ```
pub trait GeneralHandler<T: ?Sized>: Default {
    /// Associated specification type for [`GeneralObserver`].
    type Spec;

    /// Called when the observed value is moved.
    ///
    /// ## Safety
    ///
    /// See [`Observer::refresh`].
    #[inline]
    unsafe fn on_refresh(&mut self, value: &mut T) {
        let _ = value;
    }

    /// Called when observation begins.
    ///
    /// See [`Observer::observe`].
    fn on_observe(value: &mut T) -> Self;

    /// Called when the value is accessed through [`DerefMut`].
    fn on_deref_mut(&mut self);

    /// Called when collecting changes, returns whether a change occurred.
    ///
    /// See [`SerializeObserver::collect`].
    fn on_collect(&self, value: &T) -> bool;
}

/// A helper trait for providing a custom name when formatting [`GeneralObserver`] with [`Debug`].
///
/// `DebugHandler` extends [`GeneralHandler`] by adding a [`NAME`](DebugHandler::NAME) constant used
/// as the type label in [`Debug`] output for [`GeneralObserver`].
///
/// ## Example
///
/// ```
/// use morphix::observe::{DebugHandler, GeneralHandler, GeneralObserver, Observer};
///
/// #[derive(Default)]
/// pub struct MyHandler;
///
/// impl<T> GeneralHandler<T> for MyHandler {
///     // omitted for brevity
/// #   type Spec = morphix::observe::DefaultSpec;
/// #   fn on_observe(_value: &mut T) -> Self { MyHandler }
/// #   fn on_deref_mut(&mut self) {}
/// #   fn on_collect(&self, _value: &T) -> bool { true }
/// }
///
/// impl<T> DebugHandler<T> for MyHandler {
///     const NAME: &'static str = "MyObserver";
/// }
///
/// let mut value = 123;
/// let ob = GeneralObserver::<MyHandler, i32>::observe(&mut value);
/// println!("{:?}", ob); // prints: MyObserver(123)
/// ```
pub trait DebugHandler<T: ?Sized>: GeneralHandler<T> {
    /// The name displayed when formatting the observer with [`Debug`].
    const NAME: &'static str;
}

/// A general-purpose [`Observer`] implementation with extensible change detection strategies.
///
/// `GeneralObserver` provides a flexible framework for implementing different change detection
/// strategies through the [`GeneralHandler`] trait. It serves as the foundation for several
/// built-in observer types.
///
/// ## Capabilities and Limitations
///
/// `GeneralObserver` can:
/// - Detect whether a value has changed via [`DerefMut`]
/// - Produce [`Replace`](MutationKind::Replace) mutations when changes are detected
///
/// `GeneralObserver` cannot:
/// - Track [`Append`](MutationKind::Append) mutations
/// - Track field-level changes
/// - Add specialized implementations for common traits (e.g. [`PartialEq`])
///
/// For types that benefit from more sophisticated change tracking, morphix provides specialized
/// observer implementations. These include built-in support for [`String`] and [`Vec`] (which can
/// track append operations), as well as custom observers generated by `#[derive(Observe)]` (which
/// can track field-level changes).
///
/// ## Built-in Implementations
///
/// The following observer types are built on `GeneralObserver`:
///
/// - [`ShallowObserver`](super::ShallowObserver) - Tracks any [`DerefMut`] access as a change
/// - [`NoopObserver`](super::NoopObserver) - Ignores all changes
/// - [`HashObserver`](super::HashObserver) - Compares hash values to detect changes
/// - [`SnapshotObserver`](super::SnapshotObserver) - Compares cloned snapshots to detect changes
pub struct GeneralObserver<'ob, H, S: ?Sized, N = Zero> {
    ptr: ObserverPointer<S>,
    handler: H,
    phantom: PhantomData<&'ob mut N>,
}

impl<'ob, H, S: ?Sized, N> Deref for GeneralObserver<'ob, H, S, N> {
    type Target = ObserverPointer<S>;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.ptr
    }
}

impl<'ob, H, S: ?Sized, N> DerefMut for GeneralObserver<'ob, H, S, N>
where
    N: Unsigned,
    S: AsDerefMut<N>,
    H: GeneralHandler<S::Target>,
{
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        self.handler.on_deref_mut();
        &mut self.ptr
    }
}

impl<'ob, H, S> Assignable for GeneralObserver<'ob, H, S>
where
    H: GeneralHandler<S>,
{
    type Depth = Succ<Zero>;
}

impl<'ob, H, S: ?Sized, N> Observer<'ob> for GeneralObserver<'ob, H, S, N>
where
    N: Unsigned,
    S: AsDerefMut<N> + 'ob,
    H: GeneralHandler<S::Target>,
{
    type InnerDepth = N;
    type OuterDepth = Zero;
    type Head = S;

    #[inline]
    fn uninit() -> Self {
        Self {
            ptr: ObserverPointer::default(),
            handler: H::default(),
            phantom: PhantomData,
        }
    }

    #[inline]
    unsafe fn refresh(this: &mut Self, value: &mut Self::Head) {
        ObserverPointer::set(&this.ptr, value);
        unsafe { this.handler.on_refresh(value.as_deref_mut()) }
    }

    #[inline]
    fn observe(value: &'ob mut Self::Head) -> Self {
        Self {
            ptr: ObserverPointer::new(value),
            handler: H::on_observe(value.as_deref_mut()),
            phantom: PhantomData,
        }
    }
}

impl<'ob, H, S: ?Sized, N> SerializeObserver<'ob> for GeneralObserver<'ob, H, S, N>
where
    N: Unsigned,
    S: AsDerefMut<N, Target: Serialize> + 'ob,
    H: GeneralHandler<S::Target>,
{
    unsafe fn collect_unchecked<A: Adapter>(this: &mut Self) -> Result<Option<Mutation<A::Value>>, A::Error> {
        Ok(if this.handler.on_collect(this.as_deref()) {
            Some(Mutation {
                path: Default::default(),
                kind: MutationKind::Replace(A::serialize_value(this.as_deref())?),
            })
        } else {
            None
        })
    }
}

macro_rules! impl_fmt {
    ($($trait:ident),* $(,)?) => {
        $(
            impl<'ob, H, S, N: Unsigned> std::fmt::$trait for GeneralObserver<'ob, H, S, N>
            where
                S: AsDerefMut<N, Target: std::fmt::$trait> + ?Sized,
                H: GeneralHandler<S::Target>
            {
                #[inline]
                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                    std::fmt::$trait::fmt(self.as_deref(), f)
                }
            }
        )*
    };
}

impl_fmt! {
    Binary,
    Display,
    LowerExp,
    LowerHex,
    Octal,
    Pointer,
    UpperExp,
    UpperHex,
}

impl<'ob, H, S, N: Unsigned> Debug for GeneralObserver<'ob, H, S, N>
where
    S: AsDerefMut<N, Target: Debug> + ?Sized,
    H: DebugHandler<S::Target>,
{
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple(H::NAME).field(&self.as_deref()).finish()
    }
}

impl<'ob, H, S, N: Unsigned, I> std::ops::Index<I> for GeneralObserver<'ob, H, S, N>
where
    S: AsDerefMut<N, Target: std::ops::Index<I>> + ?Sized,
    H: GeneralHandler<S::Target>,
{
    type Output = <S::Target as std::ops::Index<I>>::Output;

    #[inline]
    fn index(&self, index: I) -> &Self::Output {
        self.as_deref().index(index)
    }
}

impl<'ob, H, S, N: Unsigned, I> std::ops::IndexMut<I> for GeneralObserver<'ob, H, S, N>
where
    S: AsDerefMut<N, Target: std::ops::IndexMut<I>> + ?Sized,
    H: GeneralHandler<S::Target>,
{
    #[inline]
    fn index_mut(&mut self, index: I) -> &mut Self::Output {
        Observer::as_inner(self).index_mut(index)
    }
}

impl<'ob, H, S, N: Unsigned, U: ?Sized> PartialEq<U> for GeneralObserver<'ob, H, S, N>
where
    S: AsDerefMut<N, Target: PartialEq<U>> + ?Sized,
    H: GeneralHandler<S::Target>,
{
    #[inline]
    fn eq(&self, other: &U) -> bool {
        self.as_deref().eq(other)
    }
}

impl<'ob, H, S, N: Unsigned, U: ?Sized> PartialOrd<U> for GeneralObserver<'ob, H, S, N>
where
    S: AsDerefMut<N, Target: PartialOrd<U>> + ?Sized,
    H: GeneralHandler<S::Target>,
{
    #[inline]
    fn partial_cmp(&self, other: &U) -> Option<std::cmp::Ordering> {
        self.as_deref().partial_cmp(other)
    }
}

macro_rules! impl_assign_ops {
    ($($trait:ident => $method:ident),* $(,)?) => {
        $(
            impl<'ob, H, S, N: Unsigned, U> std::ops::$trait<U> for GeneralObserver<'ob, H, S, N>
            where
                S: AsDerefMut<N, Target: std::ops::$trait<U>> + ?Sized,
                H: GeneralHandler<S::Target>,
            {
                #[inline]
                fn $method(&mut self, rhs: U) {
                    Observer::as_inner(self).$method(rhs);
                }
            }
        )*
    };
}

impl_assign_ops! {
    AddAssign => add_assign,
    SubAssign => sub_assign,
    MulAssign => mul_assign,
    DivAssign => div_assign,
    RemAssign => rem_assign,
    BitAndAssign => bitand_assign,
    BitOrAssign => bitor_assign,
    BitXorAssign => bitxor_assign,
    ShlAssign => shl_assign,
    ShrAssign => shr_assign,
}

macro_rules! impl_ops_copy {
    ($($trait:ident => $method:ident),* $(,)?) => {
        $(
            impl<'ob, H, S, N: Unsigned, U> std::ops::$trait<U> for GeneralObserver<'ob, H, S, N>
            where
                S: AsDerefMut<N, Target: std::ops::$trait<U> + Copy> + ?Sized,
                H: GeneralHandler<S::Target>,
            {
                type Output = <S::Target as std::ops::$trait<U>>::Output;

                #[inline]
                fn $method(self, rhs: U) -> Self::Output {
                    self.as_deref().$method(rhs)
                }
            }
        )*
    };
}

impl_ops_copy! {
    Add => add,
    Sub => sub,
    Mul => mul,
    Div => div,
    Rem => rem,
    BitAnd => bitand,
    BitOr => bitor,
    BitXor => bitxor,
    Shl => shl,
    Shr => shr,
}

impl<'ob, H, S, N: Unsigned> std::ops::Neg for GeneralObserver<'ob, H, S, N>
where
    S: AsDerefMut<N, Target: std::ops::Neg + Copy> + ?Sized,
    H: GeneralHandler<S::Target>,
{
    type Output = <S::Target as std::ops::Neg>::Output;

    #[inline]
    fn neg(self) -> Self::Output {
        (*self.as_deref()).neg()
    }
}

impl<'ob, H, S, N: Unsigned> std::ops::Not for GeneralObserver<'ob, H, S, N>
where
    S: AsDerefMut<N, Target: std::ops::Not + Copy> + ?Sized,
    H: GeneralHandler<S::Target>,
{
    type Output = <S::Target as std::ops::Not>::Output;

    #[inline]
    fn not(self) -> Self::Output {
        (*self.as_deref()).not()
    }
}
