#![deny(rustdoc::broken_intra_doc_links)]

use crate::{
    functions::FunctionErrors,
    kernels::RawScalarTrait,
    validation::{StrictFinitePolicy, capture_backtrace},
};
use duplicate::duplicate_item;
use num::Complex;
use std::backtrace::Backtrace;
use thiserror::Error;
use try_create::ValidationPolicy;

//------------------------------------------------------------------------------------------------
/// Errors that can occur during the input validation phase or due to special input
/// values when attempting to compute the reciprocal of a number.
///
/// This enum is used as a source for the [`Input`](FunctionErrors::Input) variant of [`ReciprocalErrors`].
/// It is generic over `RawScalar: RawScalarTrait`. The [`InvalidArgument`](ReciprocalInputErrors::InvalidArgument)
/// variant contains a `source` of type `<RawScalar as RawScalarTrait>::ValidationErrors`.
#[derive(Debug, Error)]
pub enum ReciprocalInputErrors<RawScalar: RawScalarTrait> {
    /// The input value is zero.
    ///
    /// This error occurs when the input value for the reciprocal computation is zero,
    /// as division by zero is undefined.
    #[error("division by zero!")]
    DivisionByZero {
        /// A captured backtrace for debugging purposes.
        backtrace: Backtrace,
    },

    /// The input value failed basic validation checks.
    ///
    /// This error occurs if the input value itself is considered invalid
    /// according to the validation policy (e.g., [`StrictFinitePolicy`]),
    /// such as being NaN or Infinity, before the reciprocal calculation
    /// is attempted.
    #[error("the input value is invalid according to validation policy")]
    InvalidArgument {
        /// The underlying validation error from the input type.
        ///
        /// This provides more specific details about why the input value
        /// was considered invalid.
        #[source]
        #[backtrace]
        source: <RawScalar as RawScalarTrait>::ValidationErrors,
    },
}

/// A type alias for [`FunctionErrors`], specialized for errors that can occur during
/// the computation of the reciprocal of a scalar number.
///
/// This type represents the possible failures when calling [`Reciprocal::try_reciprocal()`].
/// It is generic over `RawScalar: RawScalarTrait`. This type alias wraps [`FunctionErrors`],
/// where the input error source is [`ReciprocalInputErrors<RawScalar>`] and the output
/// error source is `<RawScalar as RawScalarTrait>::ValidationErrors`.
///
/// # Variants
///
/// This type alias wraps [`FunctionErrors`], which has the following variants in this context:
///
/// - `Input { source: ReciprocalInputErrors<RawScalar> }`: Indicates that the input number
///   was invalid for reciprocal computation. This could be due to failing initial validation
///   (e.g., containing NaN or Infinity) or because the input was zero (division by zero).
///   The `source` field provides more specific details via [`ReciprocalInputErrors`].
///
/// - `Output { source: <RawScalar as RawScalarTrait>::ValidationErrors }`: Indicates that the computed
///   reciprocal value itself failed validation. This typically means the result of the
///   reciprocal operation yielded a non-finite value (NaN or Infinity). The `source` field
///   provides details, usually an instance of [`crate::validation::ErrorsValidationRawReal`]
///   or [`crate::validation::ErrorsValidationRawComplex`].
pub type ReciprocalErrors<RawScalar> = FunctionErrors<
    ReciprocalInputErrors<RawScalar>,
    <RawScalar as RawScalarTrait>::ValidationErrors,
>;

/// A trait for computing the reciprocal (`1/x`) of a number.
///
/// This trait provides an interface for calculating the reciprocal of a number,
/// which can be real or complex. It includes both a fallible version (`try_reciprocal`)
/// that performs validation and an infallible version (`reciprocal`) that may panic
/// in debug builds if validation fails.
///
/// # Implementors
///
/// This trait is implemented for:
/// - `f64`
/// - [`Complex<f64>`](num::Complex)
/// - `RealRugStrictFinite<PRECISION>` (when the `rug` feature is enabled)
/// - `ComplexRugStrictFinite<PRECISION>` (when the `rug` feature is enabled)
///
/// # Validation Policy
///
/// Implementations typically use a [`StrictFinitePolicy`] for validating inputs and outputs.
/// This means that NaN, Infinity, and subnormal numbers (for `f64`-based types)
/// will generally result in an error or panic. Division by zero is also explicitly handled.
pub trait Reciprocal: Sized {
    /// The error type that can be returned by the `try_reciprocal` method.
    ///
    /// This is typically an instantiation of [`ReciprocalErrors`].
    type Error: std::error::Error;

    /// Attempts to compute the reciprocal of `self` (`1/self`), returning a `Result`.
    ///
    /// This method first validates the input `self` using [`StrictFinitePolicy`] and
    /// also checks if it is zero. If the input is valid (finite, non-zero, normal),
    /// it computes the reciprocal and then validates the result using the same policy.
    fn try_reciprocal(self) -> Result<Self, <Self as Reciprocal>::Error>;

    /// Returns the reciprocal of `self` (`1/self`).
    fn reciprocal(self) -> Self;
}

#[duplicate_item(
    T implementation trait_comment;
    [f64] [recip()] ["Implementation of the [`Reciprocal`] trait for [`f64`]."];
    [Complex::<f64>] [inv()] ["Implementation of the [`Reciprocal`] trait for [`Complex<f64>`]."];
)]
impl Reciprocal for T {
    type Error = ReciprocalErrors<Self>;

    /// Attempts to compute the reciprocal of `self` (`1/self`), returning a `Result`.
    ///
    /// This method first validates the input `self` using [`StrictFinitePolicy`] and
    /// also checks if it is zero. If the input is valid (finite, non-zero, normal),
    /// it computes the reciprocal and then validates the result using the same policy.
    ///
    /// # Returns
    ///
    /// - `Ok(Self)`: If both the input and the computed reciprocal are valid.
    /// - `Err(Self::Error)`: If the input is invalid (e.g., NaN, Infinity, zero, subnormal)
    ///   or if the computed reciprocal is invalid (e.g., NaN, Infinity, overflow).
    ///
    /// # Examples
    ///
    /// ```rust
    /// use num_valid::{ComplexScalar, functions::{ComplexScalarConstructors, Reciprocal}};
    /// use num::Complex;
    /// use try_create::TryNew;
    ///
    /// // For f64
    /// let x = 4.0_f64;
    /// match x.try_reciprocal() {
    ///     Ok(val) => println!("1/{} = {}", x, val), // 1/4.0 = 0.25
    ///     Err(e) => println!("Error: {:?}", e),
    /// }
    ///
    /// assert!(0.0_f64.try_reciprocal().is_err());
    /// assert!(f64::NAN.try_reciprocal().is_err());
    ///
    /// // For Complex<f64>
    /// let z = Complex::new(2.0, 0.0);
    /// assert_eq!(z.try_reciprocal().unwrap(), Complex::try_new_pure_real(0.5).unwrap());
    /// ```
    #[inline(always)]
    fn try_reciprocal(self) -> Result<Self, <Self as Reciprocal>::Error> {
        StrictFinitePolicy::<T, 53>::validate(self)
            .map_err(|e| ReciprocalInputErrors::InvalidArgument { source: e }.into())
            .and_then(|value| {
                if RawScalarTrait::is_zero(&value) {
                    Err(ReciprocalInputErrors::DivisionByZero {
                        backtrace: capture_backtrace(),
                    }
                    .into())
                } else {
                    // value is different from zero, so we can "safely" compute the reciprocal
                    StrictFinitePolicy::<T, 53>::validate(value.implementation)
                        .map_err(|e| ReciprocalErrors::<Self>::Output { source: e })
                }
            })
    }

    /// Returns the reciprocal of `self` (`1/self`).
    ///
    /// # Behavior
    ///
    /// - **Debug Builds (`#[cfg(debug_assertions)]`)**: This method internally calls `try_reciprocal().unwrap()`.
    ///   It will panic if the input `self` is invalid (e.g., NaN, Infinity, zero, subnormal)
    ///   or if the computed reciprocal is invalid.
    /// - **Release Builds (`#[cfg(not(debug_assertions))]`)**: This method calls the underlying
    ///   reciprocal function directly (e.g., `1.0 / x` for `f64`, `num::Complex::inv`).
    ///   The behavior for non-finite inputs or division by zero (like NaN propagation or
    ///   overflow resulting in Infinity) depends on the underlying implementation for the specific type.
    ///
    /// # Panics
    ///
    /// In debug builds, this method will panic if `try_reciprocal()` would return an `Err`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use num_valid::functions::Reciprocal;
    /// use num::Complex;
    ///
    /// let x = 2.0_f64;
    /// println!("1/{} = {}", x, x.reciprocal()); // 1/2.0 = 0.5
    ///
    /// let z = Complex::new(0.0, -4.0); // 1 / (-4i) = i/4 = 0.25i
    /// println!("1/({:?}) = {:?}", z, z.reciprocal()); // 1/Complex { re: 0.0, im: -4.0 } = Complex { re: 0.0, im: 0.25 }
    /// ```
    #[inline(always)]
    fn reciprocal(self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_reciprocal().unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            self.implementation
        }
    }
}

//--------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use crate::validation::{ErrorsValidationRawComplex, ErrorsValidationRawReal};
    use num::Complex;

    #[cfg(feature = "rug")]
    use try_create::TryNew;

    mod reciprocal {
        use super::*;
        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn test_f64_reciprocal_valid() {
                    let value = 4.0;
                    assert_eq!(value.try_reciprocal().unwrap(), 0.25);
                    assert_eq!(value.reciprocal(), 0.25);
                }

                #[test]
                fn test_f64_reciprocal_zero() {
                    let value = 0.0;
                    let result = value.try_reciprocal();
                    assert!(matches!(
                        result,
                        Err(ReciprocalErrors::<f64>::Input {
                            source: ReciprocalInputErrors::DivisionByZero { .. }
                        })
                    ));
                }

                #[test]
                fn test_f64_reciprocal_nan() {
                    let value = f64::NAN;
                    let result = value.try_reciprocal();
                    assert!(matches!(
                        result,
                        Err(ReciprocalErrors::<f64>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawReal::IsNaN { .. }
                            }
                        })
                    ));
                }

                #[test]
                fn test_f64_reciprocal_subnormal() {
                    let value = f64::MIN_POSITIVE / 2.0;
                    let result = value.try_reciprocal();
                    assert!(matches!(
                        result,
                        Err(ReciprocalErrors::<f64>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawReal::IsSubnormal { .. }
                            }
                        })
                    ));
                }

                #[test]
                fn test_f64_reciprocal_infinite() {
                    let value = f64::INFINITY;
                    let result = value.try_reciprocal();
                    assert!(matches!(
                        result,
                        Err(ReciprocalErrors::<f64>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawReal::IsPosInfinity { .. }
                            }
                        })
                    ));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn test_complex_f64_reciprocal_valid() {
                    let value = Complex::new(4.0, 0.0);
                    assert_eq!(value.try_reciprocal().unwrap(), Complex::new(0.25, 0.0));
                    assert_eq!(value.reciprocal(), Complex::new(0.25, 0.0));
                }

                #[test]
                fn test_complex_f64_reciprocal_zero() {
                    let value = Complex::new(0.0, 0.0);
                    let result = value.try_reciprocal();
                    assert!(matches!(
                        result,
                        Err(ReciprocalErrors::<Complex<f64>>::Input {
                            source: ReciprocalInputErrors::DivisionByZero { .. }
                        })
                    ));
                }

                #[test]
                fn test_complex_f64_reciprocal_nan() {
                    let value = Complex::new(f64::NAN, 0.0);
                    assert!(matches!(
                        value.try_reciprocal(),
                        Err(ReciprocalErrors::<Complex<f64>>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawComplex::InvalidRealPart {
                                    source: ErrorsValidationRawReal::IsNaN { .. }
                                }
                            }
                        })
                    ));

                    let value = Complex::new(0.0, f64::NAN);
                    assert!(matches!(
                        value.try_reciprocal(),
                        Err(ReciprocalErrors::<Complex<f64>>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawComplex::InvalidImaginaryPart {
                                    source: ErrorsValidationRawReal::IsNaN { .. }
                                }
                            }
                        })
                    ));
                }

                #[test]
                fn test_complex_f64_reciprocal_infinite() {
                    let value = Complex::new(f64::INFINITY, 0.0);
                    assert!(matches!(
                        value.try_reciprocal(),
                        Err(ReciprocalErrors::<Complex<f64>>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawComplex::InvalidRealPart {
                                    source: ErrorsValidationRawReal::IsPosInfinity { .. }
                                }
                            }
                        })
                    ));

                    let value = Complex::new(0.0, f64::INFINITY);
                    assert!(matches!(
                        value.try_reciprocal(),
                        Err(ReciprocalErrors::<Complex<f64>>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawComplex::InvalidImaginaryPart {
                                    source: ErrorsValidationRawReal::IsPosInfinity { .. }
                                }
                            }
                        })
                    ));
                }

                #[test]
                fn test_complex_f64_reciprocal_sbnormal() {
                    let value = Complex::new(f64::MIN_POSITIVE / 2.0, 0.0);
                    assert!(matches!(
                        value.try_reciprocal(),
                        Err(ReciprocalErrors::<Complex<f64>>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawComplex::InvalidRealPart {
                                    source: ErrorsValidationRawReal::IsSubnormal { .. }
                                }
                            }
                        })
                    ));

                    let value = Complex::new(0.0, f64::MIN_POSITIVE / 2.0);
                    assert!(matches!(
                        value.try_reciprocal(),
                        Err(ReciprocalErrors::<Complex<f64>>::Input {
                            source: ReciprocalInputErrors::InvalidArgument {
                                source: ErrorsValidationRawComplex::InvalidImaginaryPart {
                                    source: ErrorsValidationRawReal::IsSubnormal { .. }
                                }
                            }
                        })
                    ));
                }
            }
        }

        #[cfg(feature = "rug")]
        mod rug53 {
            use super::*;
            use crate::kernels::rug::{ComplexRugStrictFinite, RealRugStrictFinite};

            mod real {
                use super::*;

                #[test]
                fn test_rug_float_reciprocal_valid() {
                    let value =
                        RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 4.0)).unwrap();
                    let result = value.try_reciprocal();
                    assert!(result.is_ok());
                }

                #[test]
                fn test_rug_float_reciprocal_zero() {
                    let value =
                        RealRugStrictFinite::<53>::try_new(rug::Float::with_val(53, 0.0)).unwrap();
                    let result = value.try_reciprocal();
                    assert!(matches!(
                        result,
                        Err(ReciprocalErrors::Input {
                            source: ReciprocalInputErrors::DivisionByZero { .. }
                        })
                    ));
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn test_complex_rug_float_reciprocal_valid() {
                    let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                        53,
                        (rug::Float::with_val(53, 4.0), rug::Float::with_val(53, 0.0)),
                    ))
                    .unwrap();

                    let expected_result =
                        ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                            53,
                            (
                                rug::Float::with_val(53, 0.25),
                                rug::Float::with_val(53, 0.0),
                            ),
                        ))
                        .unwrap();

                    assert_eq!(value.clone().try_reciprocal().unwrap(), expected_result);
                    assert_eq!(value.reciprocal(), expected_result);
                }

                #[test]
                fn test_complex_rug_float_reciprocal_zero() {
                    let value = ComplexRugStrictFinite::<53>::try_new(rug::Complex::with_val(
                        53,
                        (rug::Float::with_val(53, 0.0), rug::Float::with_val(53, 0.0)),
                    ))
                    .unwrap();
                    assert!(matches!(
                        value.try_reciprocal(),
                        Err(ReciprocalErrors::Input {
                            source: ReciprocalInputErrors::DivisionByZero { .. }
                        })
                    ));
                }
            }
        }
    }
}
//------------------------------------------------------------------------------------------------
