//! Template for adding new mathematical function traits.
//!
//! This template demonstrates the improved pattern for defining mathematical functions
//! with cleaner macro syntax using documentation constants.
//!
//! # Usage Instructions
//!
//! 1. Copy this file to `src/functions/your_function.rs`
//! 2. Search and replace placeholders:
//!    - `FUNC_NAME` → Trait name (e.g., `Sqrt`, `Exp`, `Sin`)
//!    - `func_name` → Method name (e.g., `sqrt`, `exp`, `sin`)
//!    - `MATH_OP_DESC` → Mathematical operation description (e.g., "square root", "exponential")
//!    - `DOMAIN_DESC` → Domain restrictions (e.g., "x ≥ 0 for real numbers")
//! 3. Implement the error types specific to your function's domain
//! 4. Implement trait for raw types (f64, Complex<f64>, rug types if needed)
//! 5. Add to parent module (src/functions.rs)
//! 6. Write tests

#![deny(rustdoc::broken_intra_doc_links)]

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

//------------------------------------------------------------------------------------------------
// DOCUMENTATION CONSTANTS (improved pattern - easier to maintain)
//------------------------------------------------------------------------------------------------

const DOC_FUNC_NAME_TRAIT: &str = "\
Trait for computing the MATH_OP_DESC of a number.

Provides both a fallible method that returns a [`Result`] and a panicking method \
that directly returns the computed value or panics on invalid input.

# Domain

DOMAIN_DESC

# Associated Types

- `Error`: The error type returned by [`try_func_name`](Self::try_func_name).

# Required Methods

- [`try_func_name`](Self::try_func_name): Computes the MATH_OP_DESC and returns a [`Result`]
- [`func_name`](Self::func_name): Computes the MATH_OP_DESC or panics on error";

const DOC_TRY_FUNC_NAME: &str = "\
Computes the MATH_OP_DESC of `self` and returns a [`Result`].

# Errors

Returns an error if:
- Input validation fails (NaN, infinity, subnormal)
- Input is outside the function's domain
- Result validation fails";

const DOC_FUNC_NAME: &str = "\
Computes the MATH_OP_DESC of `self` and directly returns the computed value.

This is a **panicking method** (not `unsafe`) that panics on invalid input. \
For error handling, use [`try_func_name`](Self::try_func_name).

# Panics

Panics if input is invalid or outside the function's domain.";

//------------------------------------------------------------------------------------------------
// ERROR TYPES
//------------------------------------------------------------------------------------------------

/// Input errors for FUNC_NAME computation on real numbers.
#[derive(Debug, Error)]
pub enum FUNC_NAMERealInputErrors<RawReal: RawRealTrait> {
    #[error("the argument is invalid")]
    InvalidArgument {
        #[source]
        source: <RawReal as RawScalarTrait>::ValidationErrors,
    },
    
    // Add domain-specific errors here, e.g.:
    // #[error("FUNC_NAME domain error: value {value} outside valid range")]
    // DomainError {
    //     value: RawReal,
    //     backtrace: Backtrace,
    // },
}

/// Full error type for real FUNC_NAME (combines input and output validation errors).
pub type FUNC_NAMERealErrors<RawReal> = FunctionErrors<
    FUNC_NAMERealInputErrors<RawReal>,
    <RawReal as RawScalarTrait>::ValidationErrors,
>;

/// Input errors for FUNC_NAME computation on complex numbers.
#[derive(Debug, Error)]
pub enum FUNC_NAMEComplexInputErrors<RawComplex: RawComplexTrait> {
    #[error("the argument is invalid")]
    InvalidArgument {
        #[source]
        source: <RawComplex as RawScalarTrait>::ValidationErrors,
    },
}

/// Full error type for complex FUNC_NAME.
pub type FUNC_NAMEComplexErrors<RawComplex> = FunctionErrors<
    FUNC_NAMEComplexInputErrors<RawComplex>,
    <RawComplex as RawScalarTrait>::ValidationErrors,
>;

//------------------------------------------------------------------------------------------------
// TRAIT DEFINITION
//------------------------------------------------------------------------------------------------

#[doc = DOC_FUNC_NAME_TRAIT]
pub trait FUNC_NAME: Sized {
    type Error: std::error::Error;

    #[doc = DOC_TRY_FUNC_NAME]
    fn try_func_name(self) -> Result<Self, Self::Error>;

    #[doc = DOC_FUNC_NAME]
    fn func_name(self) -> Self;
}

//------------------------------------------------------------------------------------------------
// IMPLEMENTATIONS FOR RAW TYPES
//------------------------------------------------------------------------------------------------

// Implementation for f64
impl FUNC_NAME for f64 {
    type Error = FUNC_NAMERealErrors<Self>;

    #[inline(always)]
    fn try_func_name(self) -> Result<Self, Self::Error> {
        // 1. Validate input
        StrictFinitePolicy::<Self, 53>::validate(self)
            .map_err(|e| FUNC_NAMERealInputErrors::InvalidArgument { source: e })?;
        
        // 2. Domain checks (customize based on function)
        // Example:
        // if self < 0.0 {
        //     return Err(FUNC_NAMERealInputErrors::DomainError {
        //         value: self,
        //         backtrace: capture_backtrace(),
        //     }.into());
        // }
        
        // 3. Compute result (use f64's inherent method or libm)
        let result = self.func_name(); // or: libm::func_name(self)
        
        // 4. Validate output
        StrictFinitePolicy::<Self, 53>::validate(result)
            .map_err(|e| FunctionErrors::Output { source: e })?;
        
        Ok(result)
    }

    #[inline(always)]
    fn func_name(self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_func_name().unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            f64::func_name(self)
        }
    }
}

// Implementation for Complex<f64>
impl FUNC_NAME for Complex<f64> {
    type Error = FUNC_NAMEComplexErrors<Self>;

    #[inline(always)]
    fn try_func_name(self) -> Result<Self, Self::Error> {
        // Complex functions often have different domain restrictions
        StrictFinitePolicy::<Self, 53>::validate(self)
            .map_err(|e| FUNC_NAMEComplexInputErrors::InvalidArgument { source: e })?;
        
        let result = self.func_name(); // num::Complex has many methods
        
        StrictFinitePolicy::<Self, 53>::validate(result)
            .map_err(|e| FunctionErrors::Output { source: e })?;
        
        Ok(result)
    }

    #[inline(always)]
    fn func_name(self) -> Self {
        #[cfg(debug_assertions)]
        {
            self.try_func_name().unwrap()
        }
        #[cfg(not(debug_assertions))]
        {
            Complex::func_name(&self)
        }
    }
}

//------------------------------------------------------------------------------------------------
// IMPLEMENTATIONS FOR VALIDATED WRAPPERS (using duplicate_item for brevity)
//------------------------------------------------------------------------------------------------

#[duplicate_item(
    kernel_type;
    [Native64StrictFinite];
    [Native64StrictFiniteInDebug];
    // Add rug kernels when #[cfg(feature = "rug")]:
    // #[cfg(feature = "rug")]
    // [RugStrictFinite];
    // #[cfg(feature = "rug")]
    // [RugStrictFiniteInDebug];
)]
impl FUNC_NAME for crate::RealValidated<kernel_type> {
    type Error = FUNC_NAMERealErrors<kernel_type::RawReal>;

    fn try_func_name(self) -> Result<Self, Self::Error> {
        let result = self.value.try_func_name()?;
        Ok(Self::new(result))
    }

    fn func_name(self) -> Self {
        self.try_func_name().unwrap()
    }
}

// Similar implementation for ComplexValidated<K> if function supports complex numbers

//------------------------------------------------------------------------------------------------
// TESTS
//------------------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;
    use crate::RealNative64StrictFinite;

    #[test]
    fn test_func_name_valid_input() {
        let x = RealNative64StrictFinite::from_f64(/* valid value */);
        let result = x.try_func_name().unwrap();
        // Add assertions
    }

    #[test]
    fn test_func_name_domain_error() {
        let x = RealNative64StrictFinite::from_f64(/* invalid value */);
        assert!(x.try_func_name().is_err());
    }

    #[test]
    #[should_panic(expected = "domain")]
    fn test_func_name_panic() {
        let x = RealNative64StrictFinite::from_f64(/* invalid value */);
        x.func_name(); // Should panic
    }

    #[cfg(feature = "rug")]
    #[test]
    fn test_func_name_rug() {
        // Test with arbitrary precision
    }
}
