/*******************************************************************************
 *
 *    Copyright (c) 2025 - 2026 Haixing Hu.
 *
 *    SPDX-License-Identifier: Apache-2.0
 *
 *    Licensed under the Apache License, Version 2.0.
 *
 ******************************************************************************/

//! # Shared Supplier Methods Macro
//!
//! Generates map, filter, zip method implementations for Arc/Rc-based Supplier
//!
//! Generates transformation methods for Arc/Rc-based suppliers that borrow &self
//! (because Arc/Rc can be cloned).
//!
//! This macro supports single-parameter suppliers.
//!
//! # Parameters
//!
//! * `$struct_name<$generics>` - The struct name with its generic parameters
//!   - Single parameter: `ArcSupplier<T>`
//! * `$supplier_trait` - Supplier trait name (e.g., Supplier, StatefulSupplier)
//! * `($extra_bounds)` - Extra trait bounds in parentheses ('static for both Rc and Arc)
//!
//! # All Macro Invocations
//!
//! | Supplier Type | Struct Signature | `$supplier_trait` | `($extra_bounds)` |
//! |---------------|------------------|-------------------|------------------|
//! | **ArcSupplier** | `ArcSupplier<T>` | Supplier | ('static) |
//! | **RcSupplier** | `RcSupplier<T>` | Supplier | ('static) |
//!
//! # Examples
//!
//! ```rust
//! use qubit_function::Supplier;
//! use qubit_function::{ArcSupplier, RcSupplier};
//!
//! let arc = ArcSupplier::new(|| 2i32);
//! let mapped = arc.map(|v| v * 2);
//! assert_eq!(mapped.get(), 4);
//! let filtered = arc.filter(|v: &i32| *v % 2 == 0);
//! assert_eq!(filtered.get(), Some(2));
//! let zipped = arc.zip(ArcSupplier::new(|| "ok"));
//! assert_eq!(zipped.get(), (2, "ok"));
//!
//! let rc = RcSupplier::new(|| "hello".to_string());
//! let mapped = rc.map(|v: String| v.len());
//! assert_eq!(mapped.get(), 5);
//! let filtered = rc.filter(|v: &String| !v.is_empty());
//! assert_eq!(filtered.get(), Some("hello".to_string()));
//! let zipped = rc.zip(RcSupplier::new(|| 1));
//! assert_eq!(zipped.get(), ("hello".to_string(), 1));
//! ```
//!

/// Generates map, filter, zip method implementations for Arc/Rc-based Supplier
///
/// This macro should be used inside an existing impl block for the target
/// struct. It generates individual methods but does not create a complete
/// impl block itself. Generates transformation methods for Arc/Rc-based suppliers
/// that borrow &self (because Arc/Rc can be cloned).
///
/// This macro supports single-parameter suppliers.
///
/// # Parameters
///
/// * `$struct_name<$generics>` - The struct name with its generic parameters
///   - Single parameter: `ArcSupplier<T>`
/// * `$supplier_trait` - Supplier trait name (e.g., Supplier, StatefulSupplier)
/// * `$extra_bounds` - Extra trait bounds ('static for both Rc and Arc)
///
/// # All Macro Invocations
///
/// | Supplier Type | Struct Signature | `$supplier_trait` | `$extra_bounds` |
/// |---------------|------------------|-------------------|----------------|
/// | **ArcSupplier** | `ArcSupplier<T>` | Supplier | 'static |
/// | **RcSupplier** | `RcSupplier<T>` | Supplier | 'static |
///
/// # Examples
///
/// ```rust
/// use qubit_function::Supplier;
/// use qubit_function::{ArcSupplier, RcSupplier};
///
/// let arc = ArcSupplier::new(|| 2i32);
/// let mapped = arc.map(|v| v * 2);
/// assert_eq!(mapped.get(), 4);
/// let filtered = arc.filter(|v: &i32| *v % 2 == 0);
/// assert_eq!(filtered.get(), Some(2));
/// let zipped = arc.zip(ArcSupplier::new(|| "ok"));
/// assert_eq!(zipped.get(), (2, "ok"));
///
/// let rc = RcSupplier::new(|| "hello".to_string());
/// let mapped = rc.map(|v: String| v.len());
/// assert_eq!(mapped.get(), 5);
/// let filtered = rc.filter(|v: &String| !v.is_empty());
/// assert_eq!(filtered.get(), Some("hello".to_string()));
/// let zipped = rc.zip(RcSupplier::new(|| 1));
/// assert_eq!(zipped.get(), ("hello".to_string(), 1));
/// ```
macro_rules! impl_shared_supplier_methods {
    // Special case for Arc: T only needs 'static, but zip's S parameter needs Send + Sync
    (
        $struct_name:ident < $t:ident >,
        $supplier_trait:ident,
        (arc)
    ) => {
        /// Maps the output using a transformation function.
        ///
        /// # Parameters
        ///
        /// * `mapper` - The transformation function to apply
        ///
        /// # Returns
        ///
        /// A new `$struct_name<U>` with the mapped output
        ///
        /// # Examples
        ///
        /// ```rust
        /// use qubit_function::{ArcSupplier, Supplier};
        ///
        /// let source = ArcSupplier::new(|| 10);
        /// let mapped = source.map(|x| x * 2);
        /// // source is still usable
        /// assert_eq!(mapped.get(), 20);
        /// ```
        #[allow(unused_mut)]
        #[inline]
        pub fn map<U, M>(&self, mapper: M) -> $struct_name<U>
        where
            $t: 'static,
            M: Transformer<$t, U> + Send + Sync + 'static,
            U: 'static,
        {
            let mut self_cloned = self.clone();
            $struct_name::new(move || {
                let value = self_cloned.get();
                mapper.apply(value)
            })
        }

        /// Filters output based on a predicate.
        ///
        /// # Parameters
        ///
        /// * `predicate` - The predicate to test the supplied value
        ///
        /// # Returns
        ///
        /// A new filtered `$struct_name<Option<$t>>`
        ///
        /// # Examples
        ///
        /// ```rust
        /// use qubit_function::{ArcSupplier, Supplier};
        ///
        /// let source = ArcSupplier::new(|| 42);
        /// let filtered = source.filter(|x: &i32| x % 2 == 0);
        ///
        /// assert_eq!(filtered.get(), Some(42));
        /// ```
        #[allow(unused_mut)]
        #[inline]
        pub fn filter<P>(&self, predicate: P) -> $struct_name<Option<$t>>
        where
            $t: 'static,
            P: Predicate<$t> + Send + Sync + 'static,
        {
            let mut self_cloned = self.clone();
            $struct_name::new(move || {
                let value = self_cloned.get();
                if predicate.test(&value) {
                    Some(value)
                } else {
                    None
                }
            })
        }

        /// Combines this supplier with another, producing a tuple.
        ///
        /// # Parameters
        ///
        /// * `other` - The other supplier to combine with
        ///
        /// # Returns
        ///
        /// A new `$struct_name<($t, U)>`
        ///
        /// # Examples
        ///
        /// ```rust
        /// use qubit_function::{ArcSupplier, Supplier};
        ///
        /// let first = ArcSupplier::new(|| 42);
        /// let second = ArcSupplier::new(|| "hello");
        ///
        /// let zipped = first.zip(second);
        ///
        /// assert_eq!(zipped.get(), (42, "hello"));
        /// ```
        #[allow(unused_mut)]
        #[inline]
        pub fn zip<U, S>(&self, mut other: S) -> $struct_name<($t, U)>
        where
            $t: 'static,
            S: $supplier_trait<U> + Send + Sync + 'static,
            U: 'static,
        {
            let mut self_cloned = self.clone();
            $struct_name::new(move || {
                let first = self_cloned.get();
                let second = other.get();
                (first, second)
            })
        }
    };

    // Generic case for other bounds (e.g., 'static for Rc)
    (
        $struct_name:ident < $t:ident >,
        $supplier_trait:ident,
        ($($extra_bounds:tt)*)
    ) => {
        /// Maps the output using a transformation function.
        ///
        /// # Parameters
        ///
        /// * `mapper` - The transformation function to apply
        ///
        /// # Returns
        ///
        /// A new `$struct_name<U>` with the mapped output
        ///
        /// # Examples
        ///
        /// ```rust
        /// use qubit_function::{RcSupplier, Supplier};
        ///
        /// let source = RcSupplier::new(|| 10);
        /// let mapped = source.map(|x| x * 2);
        /// // source is still usable
        /// assert_eq!(mapped.get(), 20);
        /// ```
        #[allow(unused_mut)]
        #[inline]
        pub fn map<U, M>(&self, mapper: M) -> $struct_name<U>
        where
            $t: 'static,
            M: Transformer<$t, U> + $($extra_bounds)+,
            U: $($extra_bounds)+,
        {
            let mut self_cloned = self.clone();
            $struct_name::new(move || {
                let value = self_cloned.get();
                mapper.apply(value)
            })
        }

        /// Filters output based on a predicate.
        ///
        /// # Parameters
        ///
        /// * `predicate` - The predicate to test the supplied value
        ///
        /// # Returns
        ///
        /// A new filtered `$struct_name<Option<$t>>`
        ///
        /// # Examples
        ///
        /// ```rust
        /// use qubit_function::{RcSupplier, Supplier};
        ///
        /// let source = RcSupplier::new(|| 42);
        /// let filtered = source.filter(|x: &i32| x % 2 == 0);
        ///
        /// assert_eq!(filtered.get(), Some(42));
        /// ```
        #[allow(unused_mut)]
        #[inline]
        pub fn filter<P>(&self, predicate: P) -> $struct_name<Option<$t>>
        where
            $t: 'static,
            P: Predicate<$t> + $($extra_bounds)+,
        {
            let mut self_cloned = self.clone();
            $struct_name::new(move || {
                let value = self_cloned.get();
                if predicate.test(&value) {
                    Some(value)
                } else {
                    None
                }
            })
        }

        /// Combines this supplier with another, producing a tuple.
        ///
        /// # Parameters
        ///
        /// * `other` - The other supplier to combine with
        ///
        /// # Returns
        ///
        /// A new `$struct_name<($t, U)>`
        ///
        /// # Examples
        ///
        /// ```rust
        /// use qubit_function::{RcSupplier, Supplier};
        ///
        /// let first = RcSupplier::new(|| 42);
        /// let second = RcSupplier::new(|| "hello");
        ///
        /// let zipped = first.zip(second);
        ///
        /// assert_eq!(zipped.get(), (42, "hello"));
        /// ```
        #[allow(unused_mut)]
        #[inline]
        pub fn zip<U, S>(&self, mut other: S) -> $struct_name<($t, U)>
        where
            $t: 'static,
            S: $supplier_trait<U> + $($extra_bounds)+,
            U: $($extra_bounds)+,
        {
            let mut self_cloned = self.clone();
            $struct_name::new(move || {
                let first = self_cloned.get();
                let second = other.get();
                (first, second)
            })
        }
    };
}

pub(crate) use impl_shared_supplier_methods;
