//! Shallow observer infrastructure: [`ShallowMut`] wrapper and the [`shallow_observer!`] macro
//! for generating simple observers that track mutations via a single boolean flag.
//!
//! ## Stability
//!
//! APIs in this module are unstable and may change in a future version.

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

use serde::Serialize;

use crate::helper::Invalidate;
use crate::mutation::Mutations;

impl<T: ?Sized> Invalidate<T> for bool {
    fn invalidate(&mut self, _: &T) {
        *self = true;
    }
}

/// State trait for shallow observers. Handles observation initialization.
///
/// The [`Observer`](crate::observe::Observer) impl generated by [`shallow_observer!`] requires this
/// trait on the state type.
pub trait ObserverState<T: ?Sized>: Invalidate<T> {
    /// Creates the initial observer state from a reference to the observed value.
    fn observe(value: &T) -> Self;
}

/// Extends [`ObserverState`] with flush logic for serialization.
///
/// The [`SerializeObserver`](crate::observe::SerializeObserver) impl generated by
/// [`shallow_observer!`] requires this trait.
pub trait SerializeObserverState<T: ?Sized>: ObserverState<T> {
    /// Consumes accumulated mutation state and returns collected [`Mutations`].
    ///
    /// Must fully reset internal state so an immediately subsequent call returns empty.
    fn flush(&mut self, value: &T) -> Mutations;

    /// Flat-flush variant for `#[serde(flatten)]` fields.
    ///
    /// Default implementation panics. Override for map-like types that support flattening.
    fn flat_flush(&mut self, _value: &T) -> Mutations {
        panic!("flat_flush is not supported for this type")
    }
}

impl<T: ?Sized> ObserverState<T> for bool {
    fn observe(_: &T) -> Self {
        false
    }
}

impl<T: Serialize + ?Sized + 'static> SerializeObserverState<T> for bool {
    fn flush(&mut self, value: &T) -> Mutations {
        if std::mem::take(self) {
            Mutations::replace(value)
        } else {
            Mutations::new()
        }
    }
}

/// A delegate state that forwards invalidation to an external state `V` via a raw pointer.
///
/// Implements [`Invalidate<T>`] for any `T` where `V: Invalidate<T>`, forwarding the call
/// through the raw pointer. Use this as the state type in generic observers to create
/// views that propagate invalidation to a parent observer's state.
pub struct ShallowDelegate<V: ?Sized> {
    state: *mut V,
}

impl<V: ?Sized> ShallowDelegate<V> {
    /// Creates a new [`ShallowDelegate`] from a raw pointer to the external state.
    pub fn new(state: *mut V) -> Self {
        Self { state }
    }
}

impl<T: ?Sized, V: Invalidate<T> + ?Sized> Invalidate<T> for ShallowDelegate<V> {
    fn invalidate(&mut self, value: &T) {
        unsafe { Invalidate::invalidate(&mut *self.state, value) }
    }
}

/// A mutable handle to a value `T` that invalidates a shared state `V` whenever it is mutated.
///
/// [`ShallowMut`] decouples the borrowed value from its invalidation target: [`Self::inner`] is the
/// value the caller mutates, while [`Self::state`] is a raw pointer to a separate piece of state
/// (often living on a parent observer) that gets invalidated through the [`Invalidate`]
/// trait on each [`DerefMut`].
pub struct ShallowMut<'ob, T: ?Sized, V: ?Sized> {
    pub(crate) inner: &'ob mut T,
    pub(crate) state: *mut V,
}

impl<'ob, T: ?Sized, V: ?Sized> ShallowMut<'ob, T, V> {
    /// Constructs a [`ShallowMut`] from a mutable borrow and a raw pointer to its invalidation
    /// state.
    ///
    /// The state pointer must remain valid for the lifetime `'ob`.
    pub fn new(inner: &'ob mut T, state: *mut V) -> Self {
        Self { inner, state }
    }
}

impl<'ob, T: ?Sized, V: ?Sized> Deref for ShallowMut<'ob, T, V> {
    type Target = T;

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

impl<'ob, T: ?Sized, V: Invalidate<()> + ?Sized> DerefMut for ShallowMut<'ob, T, V> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { Invalidate::invalidate(&mut *self.state, &()) }
        self.inner
    }
}

impl<'ob, T: Debug + ?Sized, V: ?Sized> Debug for ShallowMut<'ob, T, V> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("ShallowMut").field(&self.inner).finish()
    }
}

/// Generates a shallow observer type that tracks mutations via a state implementing
/// [`ObserverState`] and [`SerializeObserverState`].
///
/// ## Syntax
///
/// - `struct $ob(use<T> $ty)` — uses `bool` as the state type; `T` is type-only (impl bounds).
/// - `struct $ob<T>($ty, $state)` — `T` goes on the struct (appears in `$state`).
/// - `struct $ob<K, O>(use<V> $ty, $state)` — `K, O` on struct, `V` type-only.
///
/// Single-param forms (state = `bool`) also generate [`Observe`](crate::Observe).
/// Two-param forms do NOT generate `Observe` — write it by hand with appropriate bounds.
///
/// Also generates [`Observer`](crate::observe::Observer),
/// [`SerializeObserver`](crate::observe::SerializeObserver),
/// [`QuasiObserver`](crate::helper::QuasiObserver), and standard trait impls ([`Deref`],
/// [`DerefMut`], [`Debug`], [`PartialEq`], [`Eq`], [`PartialOrd`], [`Ord`], [`AsMut`]).
#[doc(hidden)]
#[macro_export]
macro_rules! __shallow_observer {
    () => {};

    ($(#[$meta:meta])* struct $ob:ident ($($arg:tt)*); $($rest:tt)*) => {
        $crate::__shallow_observer!(@impl $(#[$meta])* struct $ob [] ($($arg)*););
        $crate::__shallow_observer!($($rest)*);
    };

    ($(#[$meta:meta])* struct $ob:ident < $($rest:tt)*) => {
        $crate::__shallow_observer!(@gen $(#[$meta])* struct $ob [] $($rest)*);
    };

    (@gen $(#[$meta:meta])* struct $ob:ident [$($gen:tt)*] > ($($arg:tt)*); $($rest:tt)*) => {
        $crate::__shallow_observer!(@impl $(#[$meta])* struct $ob [$($gen)* ,] ($($arg)*););
        $crate::__shallow_observer!($($rest)*);
    };

    (@gen $(#[$meta:meta])* struct $ob:ident [$($gen:tt)*] >> ($($arg:tt)*); $($rest:tt)*) => {
        $crate::__shallow_observer!(@impl $(#[$meta])* struct $ob [$($gen)* >,] ($($arg)*););
        $crate::__shallow_observer!($($rest)*);
    };

    (@gen $(#[$meta:meta])* struct $ob:ident [$($gen:tt)*] $tt:tt $($rest:tt)*) => {
        $crate::__shallow_observer!(@gen $(#[$meta])* struct $ob [$($gen)* $tt] $($rest)*);
    };

    // Single-param with use<>: ty_gen in impls only, state = bool
    (@impl $(#[$meta:meta])* struct $ob:ident [$($sg:tt)*] (use<$($tg:ident),*> $ty:ty);) => {
        $crate::__shallow_observer!(@impl_body $(#[$meta])* struct $ob [$($sg)* $($tg,)*] [$($sg)*] ($ty) (bool););
    };

    // Single-param without use<>: no ty_gen, state = bool
    (@impl $(#[$meta:meta])* struct $ob:ident [$($sg:tt)*] ($ty:ty);) => {
        $crate::__shallow_observer!(@impl_body $(#[$meta])* struct $ob [$($sg)*] [$($sg)*] ($ty) (bool););
    };

    // Two-param with use<>: ty_gen in impls only, state_gen on struct
    (@impl $(#[$meta:meta])* struct $ob:ident [$($sg:tt)*] (use<$($tg:ident),*> $vis_ptr:vis $ty:ty, $vis_state:vis $state:ty);) => {
        $crate::__shallow_observer!(@impl_body $(#[$meta])* struct $ob [$($sg)* $($tg,)*] [$($sg)*] ($vis_ptr $ty) ($vis_state $state););
    };

    // Two-param without use<>: state_gen on struct
    (@impl $(#[$meta:meta])* struct $ob:ident [$($sg:tt)*] ($vis_ptr:vis $ty:ty, $vis_state:vis $state:ty);) => {
        $crate::__shallow_observer!(@impl_body $(#[$meta])* struct $ob [$($sg)*] [$($sg)*] ($vis_ptr $ty) ($vis_state $state););
    };

    (@impl_body $(#[$meta:meta])* struct $ob:ident [$($tg:tt)*] [$($sg:tt)*] ($vis_ptr:vis $ty:ty) ($vis_state:vis $state:ty);) => {
        $(#[$meta])*
        pub struct $ob<'ob, $($sg)* S: ?Sized, D = $crate::helper::Zero> {
            $vis_ptr ptr: $crate::helper::Pointer<S>,
            $vis_state state: $state,
            pub(crate) phantom: ::std::marker::PhantomData<&'ob mut D>,
        }

        impl<'ob, $($sg)* S: ?Sized, D> ::std::ops::Deref for $ob<'ob, $($sg)* S, D> {
            type Target = $crate::helper::Pointer<S>;

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

        impl<'ob, $($tg)* S: ?Sized, D> ::std::ops::DerefMut for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
        {
            fn deref_mut(&mut self) -> &mut Self::Target {
                ::std::ptr::from_mut(self).expose_provenance();
                $crate::helper::QuasiObserver::invalidate(&mut self.ptr);
                &mut self.ptr
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> $crate::helper::QuasiObserver for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::Invalidate<$ty>,
        {
            type Head = S;
            type OuterDepth = $crate::helper::Succ<$crate::helper::Zero>;
            type InnerDepth = D;

            fn invalidate(this: &mut Self) {
                $crate::helper::Invalidate::invalidate(&mut this.state, (*this.ptr).as_deref());
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> $crate::observe::Observer for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDerefMut<D, Target = $ty>,
            $state: $crate::helper::shallow::ObserverState<$ty>,
        {
            fn observe(head: &mut Self::Head) -> Self {
                let this = Self {
                    state: $crate::helper::shallow::ObserverState::observe(head.as_deref_mut()),
                    ptr: $crate::helper::Pointer::new(head),
                    phantom: ::std::marker::PhantomData,
                };
                $crate::helper::Pointer::register_state::<_, D>(&this.ptr, &this.state);
                this
            }

            unsafe fn relocate(this: &mut Self, head: &mut Self::Head) {
                $crate::helper::Pointer::set(this, head);
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> $crate::observe::RefObserver for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::shallow::ObserverState<$ty>,
        {
            fn observe(head: &Self::Head) -> Self {
                let this = Self {
                    state: $crate::helper::shallow::ObserverState::observe(head.as_deref()),
                    ptr: $crate::helper::Pointer::new(head),
                    phantom: ::std::marker::PhantomData,
                };
                $crate::helper::Pointer::register_state::<_, D>(&this.ptr, &this.state);
                this
            }

            unsafe fn relocate(this: &mut Self, head: &Self::Head) {
                $crate::helper::Pointer::set(this, head);
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> $crate::observe::SerializeObserver for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::shallow::SerializeObserverState<$ty>,
        {
            unsafe fn flush(this: &mut Self) -> $crate::mutation::Mutations {
                $crate::helper::shallow::SerializeObserverState::flush(
                    &mut this.state,
                    (*this.ptr).as_deref(),
                )
            }

            unsafe fn flat_flush(this: &mut Self) -> $crate::mutation::Mutations {
                $crate::helper::shallow::SerializeObserverState::flat_flush(
                    &mut this.state,
                    (*this.ptr).as_deref(),
                )
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> AsMut<$ty> for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDerefMut<D, Target = $ty>,
            $state: $crate::helper::Invalidate<$ty>,
        {
            fn as_mut(&mut self) -> &mut $ty {
                $crate::helper::QuasiObserver::tracked_mut(self)
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> std::fmt::Debug for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::Invalidate<$ty>,
            $ty: std::fmt::Debug,
        {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                f.debug_tuple(stringify!($ob)).field(&$crate::helper::QuasiObserver::untracked_ref(self)).finish()
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> PartialEq for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::Invalidate<$ty>,
            $ty: PartialEq,
        {
            fn eq(&self, other: &$ob<'ob, $($sg)* S, D>) -> bool {
                $crate::helper::QuasiObserver::untracked_ref(self).eq($crate::helper::QuasiObserver::untracked_ref(other))
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> Eq for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::Invalidate<$ty>,
            $ty: Eq,
        {
        }

        #[allow(clippy::non_canonical_partial_ord_impl)]
        impl<'ob, $($tg)* S: ?Sized, D> PartialOrd for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::Invalidate<$ty>,
            $ty: PartialOrd,
        {
            fn partial_cmp(&self, other: &$ob<'ob, $($sg)* S, D>) -> Option<std::cmp::Ordering> {
                $crate::helper::QuasiObserver::untracked_ref(self).partial_cmp($crate::helper::QuasiObserver::untracked_ref(other))
            }
        }

        impl<'ob, $($tg)* S: ?Sized, D> Ord for $ob<'ob, $($sg)* S, D>
        where
            D: $crate::helper::Unsigned,
            S: $crate::helper::AsDeref<D, Target = $ty>,
            $state: $crate::helper::Invalidate<$ty>,
            $ty: Ord,
        {
            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
                $crate::helper::QuasiObserver::untracked_ref(self).cmp($crate::helper::QuasiObserver::untracked_ref(other))
            }
        }
    };
}

#[doc(inline)]
pub use crate::__shallow_observer as shallow_observer;
