//! Error types for LOWESS operations.
//!
//! ## Purpose
//!
//! This module defines all error conditions that can occur during LOWESS
//! smoothing operations, from input validation to adapter-specific constraints.
//! All errors are represented by the [`LowessError`] enum, which provides
//! detailed context for debugging and user feedback.
//!
//! ## Design notes
//!
//! * Errors are designed to be informative, including relevant values that
//!   caused the error (e.g., actual vs. expected array lengths).
//! * The error type implements `Clone` to support deferred error handling
//!   in the builder pattern (see adapter validation).
//! * String messages are used sparingly and only for truly dynamic errors.
//! * All errors implement `Display` with human-readable messages.
//!
//! ## Error categories
//!
//! 1. **Input validation**: Empty arrays, mismatched lengths, invalid values
//! 2. **Parameter validation**: Invalid fraction, delta, tolerance, intervals
//! 3. **Adapter constraints**: Chunk size, overlap, window capacity limits
//! 4. **Feature support**: Unsupported feature/adapter combinations
//!
//! ## Invariants
//!
//! * All error variants provide enough context to diagnose the issue.
//! * Error messages are consistent in format and tone.
//! * Numeric values in errors match the types used in the API.
//!
//! ## Non-goals
//!
//! * This module does not perform validation itself.
//! * This module does not provide error recovery strategies.
//! * This module does not handle warnings or non-fatal conditions.
//!
//! ## Visibility
//!
//! The [`LowessError`] type is part of the public API and is re-exported
//! at the crate root. Users interact with it through `Result<T, LowessError>`.

#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::string::String;

#[cfg(feature = "std")]
use std::error::Error;

use core::fmt::{Display, Formatter, Result};

// ============================================================================
// Error Type
// ============================================================================

/// Comprehensive error type for LOWESS operations.
///
/// This enum covers all error conditions that can occur during LOWESS
/// smoothing, from input validation to adapter-specific constraints.
#[derive(Debug, Clone, PartialEq)]
pub enum LowessError {
    // ------------------------------------------------------------------------
    // Input validation errors
    // ------------------------------------------------------------------------
    /// Input arrays are empty.
    ///
    /// LOWESS requires at least 2 data points to perform smoothing.
    EmptyInput,

    /// Generic invalid input error with a descriptive message.
    ///
    /// Used for dynamic validation errors that don't fit other categories.
    InvalidInput(String),

    /// Input arrays have different lengths.
    ///
    /// The `x` and `y` arrays must have the same number of elements.
    MismatchedInputs {
        /// Number of elements in the `x` array.
        x_len: usize,
        /// Number of elements in the `y` array.
        y_len: usize,
    },

    /// Invalid numeric value in input data (NaN or infinite).
    ///
    /// LOWESS requires all input values to be finite real numbers.
    InvalidNumericValue(String),

    /// Not enough points for smoothing.
    ///
    /// The number of points must meet the minimum requirement for the
    /// selected smoothing parameters.
    TooFewPoints {
        /// Number of points provided.
        got: usize,
        /// Minimum required points.
        min: usize,
    },

    // ------------------------------------------------------------------------
    // Parameter validation errors
    // ------------------------------------------------------------------------
    /// Smoothing fraction not in valid range (0, 1].
    ///
    /// The fraction parameter controls the proportion of data used for each
    /// local fit and must be positive and at most 1.
    InvalidFraction(f64),

    /// Delta parameter is negative.
    ///
    /// Delta controls interpolation optimization and must be non-negative.
    InvalidDelta(f64),

    /// Iterations must be at least 1.
    InvalidIterations(usize),

    /// Confidence or prediction interval level not in range (0, 1).
    ///
    /// Interval levels represent probabilities and must be strictly between
    /// 0 and 1 (e.g., 0.95 for 95% intervals).
    InvalidIntervals(f64),

    /// Auto-convergence tolerance is not positive and finite.
    ///
    /// The tolerance parameter must be a positive, finite value.
    InvalidTolerance(f64),

    // ------------------------------------------------------------------------
    // Adapter-specific errors
    // ------------------------------------------------------------------------
    /// Invalid chunk size for streaming adapter.
    ///
    /// Chunk size must meet the minimum requirement for the smoothing
    /// parameters (typically at least `fraction * n`).
    InvalidChunkSize {
        /// The chunk size provided.
        got: usize,
        /// Minimum required chunk size.
        min: usize,
    },

    /// Invalid overlap for streaming adapter.
    ///
    /// Overlap must be less than chunk size to ensure progress.
    InvalidOverlap {
        /// The overlap provided.
        overlap: usize,
        /// The chunk size.
        chunk_size: usize,
    },

    /// Invalid window capacity for online adapter.
    ///
    /// Window capacity must be large enough to accommodate the smoothing
    /// parameters (typically at least `fraction * expected_n`).
    InvalidWindowCapacity {
        /// The window capacity provided.
        got: usize,
        /// Minimum required window capacity.
        min: usize,
    },

    /// Invalid min_points for online adapter.
    ///
    /// The minimum points parameter must be at least 2 and at most the
    /// window capacity.
    InvalidMinPoints {
        /// The min_points provided.
        got: usize,
        /// The window capacity.
        window_capacity: usize,
    },

    // ------------------------------------------------------------------------
    // Feature support errors
    // ------------------------------------------------------------------------
    /// Feature not supported by the selected adapter.
    ///
    /// Some features (e.g., cross-validation, diagnostics) are only available
    /// in the Batch adapter. This error is deferred until `build()` is called.
    UnsupportedFeature {
        /// Name of the adapter (e.g., "Streaming", "Online").
        adapter: &'static str,
        /// Name of the unsupported feature.
        feature: &'static str,
    },
}

// ============================================================================
// Display Implementation
// ============================================================================

impl Display for LowessError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        match self {
            // Input validation errors
            Self::EmptyInput => write!(f, "Input arrays are empty"),
            Self::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
            Self::MismatchedInputs { x_len, y_len } => {
                write!(f, "Length mismatch: x has {x_len} points, y has {y_len}")
            }
            Self::InvalidNumericValue(s) => write!(f, "Invalid numeric value: {s}"),
            Self::TooFewPoints { got, min } => {
                write!(f, "Too few points: got {got}, need at least {min}")
            }

            // Parameter validation errors
            Self::InvalidFraction(frac) => {
                write!(f, "Invalid fraction: {frac} (must be > 0 and ≤ 1)")
            }
            Self::InvalidDelta(delta) => write!(f, "Invalid delta: {delta} (must be ≥ 0)"),
            Self::InvalidIterations(iter) => write!(f, "Invalid iterations: {iter} (must be ≥ 1)"),
            Self::InvalidIntervals(level) => {
                write!(f, "Invalid interval level: {level} (must be > 0 and < 1)")
            }
            Self::InvalidTolerance(tol) => {
                write!(f, "Invalid tolerance: {tol} (must be > 0 and finite)")
            }

            // Adapter-specific errors
            Self::InvalidChunkSize { got, min } => {
                write!(f, "Invalid chunk_size: {got} (must be at least {min})")
            }
            Self::InvalidOverlap {
                overlap,
                chunk_size,
            } => {
                write!(
                    f,
                    "Invalid overlap: {overlap} (must be less than chunk_size {chunk_size})"
                )
            }
            Self::InvalidWindowCapacity { got, min } => {
                write!(f, "Invalid window_capacity: {got} (must be at least {min})")
            }
            Self::InvalidMinPoints {
                got,
                window_capacity,
            } => {
                write!(
                    f,
                    "Invalid min_points: {got} (must be between 2 and window_capacity {window_capacity})"
                )
            }

            // Feature support errors
            Self::UnsupportedFeature { adapter, feature } => {
                write!(f, "Adapter '{adapter}' does not support feature: {feature}")
            }
        }
    }
}

// ============================================================================
// Standard Error Trait
// ============================================================================

#[cfg(feature = "std")]
impl Error for LowessError {}
