//! Output types and result structures for LOWESS operations.
//!
//! ## Purpose
//!
//! This module defines the [`LowessResult`] struct which encapsulates all
//! outputs from a LOWESS smoothing operation. It provides a comprehensive
//! container for smoothed values, diagnostics, confidence/prediction intervals,
//! and metadata about the smoothing process.
//!
//! ## Design notes
//!
//! * All optional outputs use `Option<Vec<T>>` for memory efficiency.
//! * Results are generic over `Float` types to support f32 and f64.
//! * Provides convenience methods for common queries.
//! * Implements `Display` for human-readable output with adaptive formatting.
//! * Sorted x-values are stored to maintain correspondence with outputs.
//! * All vectors have the same length (number of data points).
//!
//! ## Available outputs
//!
//! * **Core outputs**: Sorted x-values, smoothed y-values
//! * **Uncertainty**: Standard errors, confidence intervals, prediction intervals
//! * **Diagnostics**: RMSE, MAE, R², AIC, AICc, effective DF
//! * **Residuals**: Differences between original and smoothed values
//! * **Robustness**: Final robustness weights from iterative refinement
//! * **Metadata**: Fraction used, iterations performed, CV scores
//!
//! ## Key concepts
//!
//! ### Optional Outputs
//! Most outputs are optional and only computed when requested. This saves
//! memory and computation time when only basic smoothing is needed.
//!
//! ### Confidence vs Prediction Intervals
//! * **Confidence intervals**: Uncertainty in the fitted curve (mean function)
//! * **Prediction intervals**: Uncertainty for new observations (includes residual variance)
//! * Prediction intervals are always wider than confidence intervals
//!
//! ### Cross-Validation Metadata
//! When cross-validation is used, the result includes:
//! * `fraction_used`: The optimal fraction selected by CV
//! * `cv_scores`: RMSE scores for each tested fraction
//!
//! ### Iteration Tracking
//! When auto-convergence is enabled, `iterations_used` records how many
//! robustness iterations were actually performed before convergence.
//!
//! ## Invariants
//!
//! * All vectors (when present) have the same length.
//! * x-values are sorted in ascending order.
//! * Confidence/prediction intervals: lower ≤ upper at each point.
//! * Robustness weights are in [0, 1].
//! * fraction_used is in (0, 1].
//!
//! ## Non-goals
//!
//! * This module does not perform the smoothing itself.
//! * This module does not validate the consistency of outputs.
//! * This module does not provide serialization (use external crates).
//! * This module does not provide plotting capabilities.
//!
//! ## Visibility
//!
//! The [`LowessResult`] struct is part of the public API and is the primary
//! way users interact with LOWESS outputs. The structure is stable and
//! changes will follow semantic versioning.

#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

#[cfg(feature = "std")]
use std::vec::Vec;

use core::cmp::Ordering;
use num_traits::Float;

use crate::evaluation::diagnostics::Diagnostics;

// ============================================================================
// Result Structure
// ============================================================================

/// Comprehensive LOWESS result with all diagnostic information.
///
/// Contains all outputs from a LOWESS smoothing operation, including
/// smoothed values, uncertainty estimates, diagnostics, and metadata.
/// Most fields are optional and only populated when requested.
///
/// # Fields
///
/// ## Core Outputs
/// * `x` - Sorted x-values (independent variable)
/// * `y` - Smoothed y-values (dependent variable)
///
/// ## Uncertainty Quantification
/// * `standard_errors` - Standard errors of the fit at each point
/// * `confidence_lower` - Lower confidence interval bounds
/// * `confidence_upper` - Upper confidence interval bounds
/// * `prediction_lower` - Lower prediction interval bounds
/// * `prediction_upper` - Upper prediction interval bounds
///
/// ## Diagnostics and Residuals
/// * `residuals` - Residuals (original y - smoothed y)
/// * `robustness_weights` - Final robustness weights from iterative refinement
/// * `diagnostics` - Comprehensive diagnostic metrics (RMSE, R², AIC, etc.)
///
/// ## Metadata
/// * `iterations_used` - Number of robustness iterations performed
/// * `fraction_used` - Smoothing fraction used (optimal if CV was performed)
/// * `cv_scores` - Cross-validation scores for each tested fraction
#[derive(Debug, Clone, PartialEq)]
pub struct LowessResult<T> {
    /// Sorted x-values (independent variable)
    pub x: Vec<T>,

    /// Smoothed y-values (dependent variable)
    pub y: Vec<T>,

    /// Standard errors (if computed)
    pub standard_errors: Option<Vec<T>>,

    /// Lower confidence interval bounds (if computed)
    pub confidence_lower: Option<Vec<T>>,

    /// Upper confidence interval bounds (if computed)
    pub confidence_upper: Option<Vec<T>>,

    /// Lower prediction interval bounds (if computed)
    pub prediction_lower: Option<Vec<T>>,

    /// Upper prediction interval bounds (if computed)
    pub prediction_upper: Option<Vec<T>>,

    /// Residuals from the fit (original y - smoothed y)
    pub residuals: Option<Vec<T>>,

    /// Robustness weights from final iteration
    pub robustness_weights: Option<Vec<T>>,

    /// Diagnostic metrics (RMSE, MAE, R², AIC, AICc, etc.)
    pub diagnostics: Option<Diagnostics<T>>,

    /// Number of iterations actually performed (for auto-convergence)
    pub iterations_used: Option<usize>,

    /// Fraction used for smoothing (optimal if CV was performed)
    pub fraction_used: T,

    /// CV scores for each tested fraction (if CV was used)
    pub cv_scores: Option<Vec<T>>,
}

impl<T: Float> LowessResult<T> {
    // ========================================================================
    // Query Methods
    // ========================================================================

    /// Check if confidence intervals were computed.
    ///
    /// # Returns
    ///
    /// `true` if both lower and upper confidence bounds are present.
    pub fn has_confidence_intervals(&self) -> bool {
        self.confidence_lower.is_some() && self.confidence_upper.is_some()
    }

    /// Check if prediction intervals were computed.
    ///
    /// # Returns
    ///
    /// `true` if both lower and upper prediction bounds are present.
    pub fn has_prediction_intervals(&self) -> bool {
        self.prediction_lower.is_some() && self.prediction_upper.is_some()
    }

    /// Check if cross-validation was performed.
    ///
    /// # Returns
    ///
    /// `true` if CV scores are present.
    pub fn has_cv_scores(&self) -> bool {
        self.cv_scores.is_some()
    }

    /// Get the best (minimum) CV score.
    ///
    /// Returns the minimum RMSE score from cross-validation if CV was
    /// performed. This corresponds to the optimal fraction selected.
    ///
    /// # Returns
    ///
    /// The best CV score, or `None` if CV was not performed.
    pub fn best_cv_score(&self) -> Option<T> {
        self.cv_scores.as_ref().and_then(|scores| {
            scores
                .iter()
                .copied()
                .min_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal))
        })
    }
}

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

impl<T: Float + core::fmt::Display> core::fmt::Display for LowessResult<T> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        // ====================================================================
        // Summary Section
        // ====================================================================

        writeln!(f, "Summary:")?;
        writeln!(f, "  Data points: {}", self.x.len())?;
        writeln!(f, "  Fraction: {}", self.fraction_used)?;

        if let Some(iters) = self.iterations_used {
            writeln!(f, "  Iterations: {}", iters)?;
        }

        // Show robustness status
        if self.robustness_weights.is_some() {
            writeln!(f, "  Robustness: Applied")?;
        }

        if self.has_cv_scores() {
            if let Some(best_score) = self.best_cv_score() {
                writeln!(f, "  Best CV score: {}", best_score)?;
            }
        }
        writeln!(f)?;

        // ====================================================================
        // Diagnostics Section
        // ====================================================================

        if let Some(diag) = &self.diagnostics {
            writeln!(f, "{}", diag)?;
        }

        // ====================================================================
        // Data Table Section
        // ====================================================================

        writeln!(f, "Smoothed Data:")?;

        // Determine which columns to show
        let has_std_err = self.standard_errors.is_some();
        let has_conf = self.has_confidence_intervals();
        let has_pred = self.has_prediction_intervals();
        let has_resid = self.residuals.is_some();
        let has_weights = self.robustness_weights.is_some();

        // Build header
        write!(f, "{:>8} {:>12}", "X", "Y_smooth")?;
        if has_std_err {
            write!(f, " {:>12}", "Std_Err")?;
        }
        if has_conf {
            write!(f, " {:>12} {:>12}", "Conf_Lower", "Conf_Upper")?;
        }
        if has_pred {
            write!(f, " {:>12} {:>12}", "Pred_Lower", "Pred_Upper")?;
        }
        if has_resid {
            write!(f, " {:>12}", "Residual")?;
        }
        if has_weights {
            write!(f, " {:>10}", "Rob_Weight")?;
        }
        writeln!(f)?;

        // Separator line
        let line_width = 21
            + if has_std_err { 13 } else { 0 }
            + if has_conf { 26 } else { 0 }
            + if has_pred { 26 } else { 0 }
            + if has_resid { 13 } else { 0 }
            + if has_weights { 11 } else { 0 };
        writeln!(f, "{:-<width$}", "", width = line_width)?;

        // Data rows (show first 10 and last 10 if more than 20 points)
        let n = self.x.len();
        let show_all = n <= 20;
        let rows_to_show: Vec<usize> = if show_all {
            (0..n).collect()
        } else {
            (0..10).chain(n - 10..n).collect()
        };

        let mut prev_idx = 0;
        for (i, &idx) in rows_to_show.iter().enumerate() {
            // Add ellipsis if we skipped rows
            if i > 0 && idx != prev_idx + 1 {
                writeln!(f, "{:>8}", "...")?;
            }
            prev_idx = idx;

            write!(f, "{:>8.2} {:>12.6}", self.x[idx], self.y[idx])?;

            // Standard error
            if has_std_err {
                if let Some(se) = &self.standard_errors {
                    write!(f, " {:>12.6}", se[idx])?;
                }
            }

            // Confidence intervals
            if has_conf {
                if let (Some(lower), Some(upper)) = (&self.confidence_lower, &self.confidence_upper)
                {
                    write!(f, " {:>12.6} {:>12.6}", lower[idx], upper[idx])?;
                }
            }

            // Prediction intervals
            if has_pred {
                if let (Some(lower), Some(upper)) = (&self.prediction_lower, &self.prediction_upper)
                {
                    write!(f, " {:>12.6} {:>12.6}", lower[idx], upper[idx])?;
                }
            }

            // Residuals
            if has_resid {
                if let Some(resid) = &self.residuals {
                    write!(f, " {:>12.6}", resid[idx])?;
                }
            }

            // Robustness weights
            if has_weights {
                if let Some(weights) = &self.robustness_weights {
                    write!(f, " {:>10.4}", weights[idx])?;
                }
            }

            writeln!(f)?;
        }

        Ok(())
    }
}
