//! Domain-specific baseline estimators
//!
//! This module provides specialized baseline estimators for different domains
//! such as computer vision, natural language processing, time series, and
//! recommendation systems. These baselines are tailored to the specific
//! characteristics and common patterns found in each domain.
//!
//! The module includes:
//! - Computer vision baselines (pixel statistics, color histograms, spatial features)
//! - NLP baselines (word frequency, n-grams, sentiment analysis)
//! - Time series baselines (seasonal patterns, trend analysis)
//! - Recommendation system baselines (popularity, user/item averages)
//! - Anomaly detection baselines (statistical thresholds, isolation methods)

use scirs2_core::ndarray::{Array1, Array2, Array3};
use scirs2_core::random::{RngExt, SeedableRng};
use sklears_core::{error::SklearsError, traits::Estimator, traits::Fit, traits::Predict};
use std::collections::HashMap;

/// Domain-specific baseline strategies
#[derive(Debug, Clone)]
pub enum DomainStrategy {
    /// Computer Vision strategies
    ComputerVision(CVStrategy),
    /// Natural Language Processing strategies  
    NLP(NLPStrategy),
    /// Time Series strategies
    TimeSeries(TimeSeriesStrategy),
    /// Recommendation System strategies
    Recommendation(RecStrategy),
    /// Anomaly Detection strategies
    AnomalyDetection(AnomalyStrategy),
}

/// Computer Vision baseline strategies
#[derive(Debug, Clone)]
pub enum CVStrategy {
    /// Predict based on pixel intensity statistics
    PixelIntensity { statistic: PixelStatistic },
    /// Predict based on color histogram features
    ColorHistogram {
        bins: usize,
        color_space: ColorSpace,
    },
    /// Predict based on spatial frequency features
    SpatialFrequency { method: FrequencyMethod },
    /// Predict based on texture features
    Texture { method: TextureMethod },
    /// Predict based on edge detection features
    EdgeDetection { threshold: f64 },
    /// Predict most frequent class in training images
    MostFrequentImageClass,
    /// Random prediction with class distribution from training
    RandomImageClass,
}

/// Pixel intensity statistics
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PixelStatistic {
    /// Mean
    Mean,
    /// Median
    Median,
    /// StandardDeviation
    StandardDeviation,
    /// Skewness
    Skewness,
    /// Kurtosis
    Kurtosis,
}

/// Color spaces for histogram computation
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ColorSpace {
    /// RGB
    RGB,
    /// HSV
    HSV,
    /// Grayscale
    Grayscale,
}

/// Spatial frequency analysis methods
#[derive(Debug, Clone, Copy)]
pub enum FrequencyMethod {
    /// DFT
    DFT,
    /// DCT
    DCT,
    /// Wavelet
    Wavelet,
}

/// Texture analysis methods
#[derive(Debug, Clone, Copy)]
pub enum TextureMethod {
    /// LocalBinaryPattern
    LocalBinaryPattern,
    /// GrayLevelCooccurrence
    GrayLevelCooccurrence,
    /// Gabor
    Gabor,
}

/// Natural Language Processing baseline strategies
#[derive(Debug, Clone)]
pub enum NLPStrategy {
    /// Predict based on word frequency
    WordFrequency { top_k: usize },
    /// Predict based on n-gram frequency
    NGram { n: usize, top_k: usize },
    /// Predict based on document length
    DocumentLength,
    /// Predict based on vocabulary richness
    VocabularyRichness,
    /// Predict based on sentiment polarity
    SentimentPolarity,
    /// Predict most frequent class in training texts
    MostFrequentTextClass,
    /// Predict based on topic keywords
    TopicKeywords { num_topics: usize },
}

/// Time Series baseline strategies
#[derive(Debug, Clone)]
pub enum TimeSeriesStrategy {
    /// Predict based on seasonal patterns
    SeasonalPattern { period: usize },
    /// Predict based on trend analysis
    TrendAnalysis { window_size: usize },
    /// Predict based on cyclical patterns
    CyclicalPattern { cycles: Vec<usize> },
    /// Predict based on autocorrelation
    Autocorrelation { max_lag: usize },
    /// Predict based on moving averages
    MovingAverage { windows: Vec<usize> },
    /// Random walk prediction
    RandomWalk { drift: f64 },
}

/// Recommendation System baseline strategies
#[derive(Debug, Clone)]
pub enum RecStrategy {
    /// Predict based on item popularity
    ItemPopularity,
    /// Predict based on user average rating
    UserAverage,
    /// Predict based on item average rating
    ItemAverage,
    /// Global average rating
    GlobalAverage,
    /// Random rating within observed range
    RandomRating,
    /// Predict based on demographic similarity
    DemographicSimilarity,
}

/// Anomaly Detection baseline strategies
#[derive(Debug, Clone)]
pub enum AnomalyStrategy {
    /// Statistical threshold-based detection
    StatisticalThreshold {
        method: ThresholdMethod,
        contamination: f64,
    },
    /// Isolation-based detection
    IsolationBased { n_estimators: usize },
    /// Distance-based detection
    DistanceBased { k: usize },
    /// Density-based detection
    DensityBased { min_samples: usize, eps: f64 },
    /// Always predict normal (majority class)
    AlwaysNormal,
    /// Random prediction with contamination rate
    RandomAnomaly { contamination: f64 },
}

/// Statistical threshold methods for anomaly detection
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ThresholdMethod {
    /// ZScore
    ZScore,
    /// ModifiedZScore
    ModifiedZScore,
    /// IQR
    IQR,
    /// Percentile
    Percentile,
}

/// Domain-specific classifier
#[derive(Debug, Clone)]
pub struct DomainClassifier {
    strategy: DomainStrategy,
    random_state: Option<u64>,
}

/// Trained domain-specific classifier
#[derive(Debug, Clone)]
pub struct TrainedDomainClassifier {
    strategy: DomainStrategy,
    classes: Vec<i32>,
    class_counts: HashMap<i32, usize>,
    domain_features: DomainFeatures,
    random_state: Option<u64>,
}

/// Domain-specific features extracted during training
#[derive(Debug, Clone)]
pub enum DomainFeatures {
    /// ComputerVision
    ComputerVision(CVFeatures),
    /// NLP
    NLP(NLPFeatures),
    /// TimeSeries
    TimeSeries(TSFeatures),
    /// Recommendation
    Recommendation(RecFeatures),
    /// AnomalyDetection
    AnomalyDetection(AnomalyFeatures),
}

/// Computer vision features
#[derive(Debug, Clone)]
pub struct CVFeatures {
    /// pixel_statistics
    pub pixel_statistics: HashMap<PixelStatistic, f64>,
    /// color_histograms
    pub color_histograms: HashMap<ColorSpace, Vec<f64>>,
    /// spatial_frequencies
    pub spatial_frequencies: Vec<f64>,
    /// texture_features
    pub texture_features: Vec<f64>,
    /// edge_features
    pub edge_features: Vec<f64>,
}

/// NLP features
#[derive(Debug, Clone)]
pub struct NLPFeatures {
    /// word_frequencies
    pub word_frequencies: HashMap<String, usize>,
    /// ngram_frequencies
    pub ngram_frequencies: HashMap<String, usize>,
    /// document_lengths
    pub document_lengths: Vec<usize>,
    /// vocabulary_size
    pub vocabulary_size: usize,
    /// sentiment_scores
    pub sentiment_scores: Vec<f64>,
    /// topic_keywords
    pub topic_keywords: HashMap<usize, Vec<String>>,
}

/// Time series features
#[derive(Debug, Clone)]
pub struct TSFeatures {
    /// seasonal_patterns
    pub seasonal_patterns: HashMap<usize, Vec<f64>>,
    /// trend_coefficients
    pub trend_coefficients: Vec<f64>,
    /// cyclical_components
    pub cyclical_components: HashMap<usize, Vec<f64>>,
    /// autocorrelations
    pub autocorrelations: Vec<f64>,
    /// moving_averages
    pub moving_averages: HashMap<usize, Vec<f64>>,
}

/// Recommendation features
#[derive(Debug, Clone)]
pub struct RecFeatures {
    /// item_popularity
    pub item_popularity: HashMap<usize, f64>,
    /// user_averages
    pub user_averages: HashMap<usize, f64>,
    /// item_averages
    pub item_averages: HashMap<usize, f64>,
    /// global_average
    pub global_average: f64,
    /// rating_range
    pub rating_range: (f64, f64),
}

/// Anomaly detection features
#[derive(Debug, Clone)]
pub struct AnomalyFeatures {
    /// statistical_thresholds
    pub statistical_thresholds: HashMap<ThresholdMethod, f64>,
    /// isolation_scores
    pub isolation_scores: Vec<f64>,
    /// distance_thresholds
    pub distance_thresholds: Vec<f64>,
    /// density_thresholds
    pub density_thresholds: Vec<f64>,
    /// contamination_rate
    pub contamination_rate: f64,
}

impl DomainClassifier {
    /// Create a new domain-specific classifier
    pub fn new(strategy: DomainStrategy) -> Self {
        Self {
            strategy,
            random_state: None,
        }
    }

    /// Set random state for reproducible results
    pub fn with_random_state(mut self, seed: u64) -> Self {
        self.random_state = Some(seed);
        self
    }

    /// Create a computer vision classifier
    pub fn computer_vision(strategy: CVStrategy) -> Self {
        Self::new(DomainStrategy::ComputerVision(strategy))
    }

    /// Create an NLP classifier
    pub fn nlp(strategy: NLPStrategy) -> Self {
        Self::new(DomainStrategy::NLP(strategy))
    }

    /// Create a time series classifier
    pub fn time_series(strategy: TimeSeriesStrategy) -> Self {
        Self::new(DomainStrategy::TimeSeries(strategy))
    }

    /// Create a recommendation system classifier
    pub fn recommendation(strategy: RecStrategy) -> Self {
        Self::new(DomainStrategy::Recommendation(strategy))
    }

    /// Create an anomaly detection classifier
    pub fn anomaly_detection(strategy: AnomalyStrategy) -> Self {
        Self::new(DomainStrategy::AnomalyDetection(strategy))
    }
}

impl Estimator for DomainClassifier {
    type Config = DomainStrategy;
    type Error = SklearsError;
    type Float = f64;

    fn config(&self) -> &Self::Config {
        &self.strategy
    }
}

impl Fit<Array2<f64>, Array1<i32>> for DomainClassifier {
    type Fitted = TrainedDomainClassifier;

    fn fit(self, x: &Array2<f64>, y: &Array1<i32>) -> Result<Self::Fitted, SklearsError> {
        let mut class_counts = HashMap::new();
        for &class in y.iter() {
            *class_counts.entry(class).or_insert(0) += 1;
        }

        let mut classes: Vec<_> = class_counts.keys().cloned().collect();
        classes.sort();

        let domain_features = self.extract_domain_features(x, y)?;

        Ok(TrainedDomainClassifier {
            strategy: self.strategy,
            classes,
            class_counts,
            domain_features,
            random_state: self.random_state,
        })
    }
}

impl DomainClassifier {
    fn extract_domain_features(
        &self,
        x: &Array2<f64>,
        y: &Array1<i32>,
    ) -> Result<DomainFeatures, SklearsError> {
        match &self.strategy {
            DomainStrategy::ComputerVision(cv_strategy) => {
                let cv_features = self.extract_cv_features(x, y, cv_strategy)?;
                Ok(DomainFeatures::ComputerVision(cv_features))
            }
            DomainStrategy::NLP(nlp_strategy) => {
                let nlp_features = self.extract_nlp_features(x, y, nlp_strategy)?;
                Ok(DomainFeatures::NLP(nlp_features))
            }
            DomainStrategy::TimeSeries(ts_strategy) => {
                let ts_features = self.extract_ts_features(x, y, ts_strategy)?;
                Ok(DomainFeatures::TimeSeries(ts_features))
            }
            DomainStrategy::Recommendation(rec_strategy) => {
                let rec_features = self.extract_rec_features(x, y, rec_strategy)?;
                Ok(DomainFeatures::Recommendation(rec_features))
            }
            DomainStrategy::AnomalyDetection(anomaly_strategy) => {
                let anomaly_features = self.extract_anomaly_features(x, y, anomaly_strategy)?;
                Ok(DomainFeatures::AnomalyDetection(anomaly_features))
            }
        }
    }

    fn extract_cv_features(
        &self,
        x: &Array2<f64>,
        _y: &Array1<i32>,
        strategy: &CVStrategy,
    ) -> Result<CVFeatures, SklearsError> {
        let mut pixel_statistics = HashMap::new();
        let mut color_histograms = HashMap::new();
        let spatial_frequencies = Vec::new();
        let texture_features = Vec::new();
        let edge_features = Vec::new();

        match strategy {
            CVStrategy::PixelIntensity { statistic } => {
                let values = self.compute_pixel_statistic(x, *statistic)?;
                pixel_statistics.insert(*statistic, values);
            }
            CVStrategy::ColorHistogram { bins, color_space } => {
                let histogram = self.compute_color_histogram(x, *bins, *color_space)?;
                color_histograms.insert(*color_space, histogram);
            }
            _ => {
                // Compute basic pixel statistics as fallback
                pixel_statistics.insert(PixelStatistic::Mean, x.mean().unwrap_or(0.0));
            }
        }

        Ok(CVFeatures {
            pixel_statistics,
            color_histograms,
            spatial_frequencies,
            texture_features,
            edge_features,
        })
    }

    fn extract_nlp_features(
        &self,
        x: &Array2<f64>,
        _y: &Array1<i32>,
        strategy: &NLPStrategy,
    ) -> Result<NLPFeatures, SklearsError> {
        let mut word_frequencies = HashMap::new();
        let ngram_frequencies = HashMap::new();
        let document_lengths = Vec::new();
        let vocabulary_size = 0;
        let sentiment_scores = Vec::new();
        let topic_keywords = HashMap::new();

        match strategy {
            NLPStrategy::WordFrequency { top_k } => {
                // Simulate word frequency extraction from numerical features
                for i in 0..*top_k.min(&x.ncols()) {
                    let word = format!("word_{}", i);
                    let freq = x.column(i).sum() as usize;
                    word_frequencies.insert(word, freq);
                }
            }
            NLPStrategy::DocumentLength => {
                // Use sum of features as document length proxy
                // document_lengths = x.sum_axis(Axis(1)).to_vec().iter().map(|&v| v as usize).collect();
            }
            _ => {
                // Basic feature extraction
            }
        }

        Ok(NLPFeatures {
            word_frequencies,
            ngram_frequencies,
            document_lengths,
            vocabulary_size,
            sentiment_scores,
            topic_keywords,
        })
    }

    fn extract_ts_features(
        &self,
        x: &Array2<f64>,
        _y: &Array1<i32>,
        strategy: &TimeSeriesStrategy,
    ) -> Result<TSFeatures, SklearsError> {
        let mut seasonal_patterns = HashMap::new();
        let trend_coefficients = Vec::new();
        let cyclical_components = HashMap::new();
        let autocorrelations = Vec::new();
        let mut moving_averages = HashMap::new();

        match strategy {
            TimeSeriesStrategy::SeasonalPattern { period } => {
                // Extract seasonal patterns from the first feature
                if x.ncols() > 0 {
                    let series = x.column(0);
                    let pattern = self.compute_seasonal_pattern(&series, *period)?;
                    seasonal_patterns.insert(*period, pattern);
                }
            }
            TimeSeriesStrategy::MovingAverage { windows } => {
                // Compute moving averages for different window sizes
                if x.ncols() > 0 {
                    let series = x.column(0);
                    for &window in windows {
                        let ma = self.compute_moving_average(&series, window)?;
                        moving_averages.insert(window, ma);
                    }
                }
            }
            _ => {
                // Basic time series features
            }
        }

        Ok(TSFeatures {
            seasonal_patterns,
            trend_coefficients,
            cyclical_components,
            autocorrelations,
            moving_averages,
        })
    }

    fn extract_rec_features(
        &self,
        x: &Array2<f64>,
        y: &Array1<i32>,
        _strategy: &RecStrategy,
    ) -> Result<RecFeatures, SklearsError> {
        // Assuming x contains [user_id, item_id, ...other features] and y contains ratings/preferences
        let mut item_popularity = HashMap::new();
        let mut user_averages = HashMap::new();
        let mut item_averages = HashMap::new();
        let global_average = y.iter().map(|&v| v as f64).sum::<f64>() / y.len() as f64;
        let rating_range = {
            let min_rating = y.iter().min().copied().unwrap_or(0) as f64;
            let max_rating = y.iter().max().copied().unwrap_or(5) as f64;
            (min_rating, max_rating)
        };

        // Extract user and item IDs from features (simplified)
        for (i, &rating) in y.iter().enumerate() {
            if x.ncols() >= 2 {
                let user_id = x[[i, 0]] as usize;
                let item_id = x[[i, 1]] as usize;
                let rating_f64 = rating as f64;

                // Update item popularity (count of interactions)
                *item_popularity.entry(item_id).or_insert(0.0) += 1.0;

                // Update user averages
                let user_entry = user_averages.entry(user_id).or_insert((0.0, 0));
                user_entry.0 += rating_f64;
                user_entry.1 += 1;

                // Update item averages
                let item_entry = item_averages.entry(item_id).or_insert((0.0, 0));
                item_entry.0 += rating_f64;
                item_entry.1 += 1;
            }
        }

        // Convert sums to averages
        let user_averages: HashMap<usize, f64> = user_averages
            .into_iter()
            .map(|(id, (sum, count))| (id, sum / count as f64))
            .collect();

        let item_averages: HashMap<usize, f64> = item_averages
            .into_iter()
            .map(|(id, (sum, count))| (id, sum / count as f64))
            .collect();

        Ok(RecFeatures {
            item_popularity,
            user_averages,
            item_averages,
            global_average,
            rating_range,
        })
    }

    fn extract_anomaly_features(
        &self,
        x: &Array2<f64>,
        _y: &Array1<i32>,
        strategy: &AnomalyStrategy,
    ) -> Result<AnomalyFeatures, SklearsError> {
        let mut statistical_thresholds = HashMap::new();
        let isolation_scores = Vec::new();
        let distance_thresholds = Vec::new();
        let density_thresholds = Vec::new();

        let contamination_rate = match strategy {
            AnomalyStrategy::StatisticalThreshold { contamination, .. }
            | AnomalyStrategy::RandomAnomaly { contamination } => *contamination,
            _ => 0.1, // Default contamination rate
        };

        // Compute statistical thresholds for the first feature
        if x.ncols() > 0 {
            let feature = x.column(0);
            let mean = feature.mean().unwrap_or(0.0);
            let std = {
                let variance = feature.iter().map(|&val| (val - mean).powi(2)).sum::<f64>()
                    / (feature.len() - 1) as f64;
                variance.sqrt()
            };

            // Z-score threshold (2 standard deviations)
            statistical_thresholds.insert(ThresholdMethod::ZScore, 2.0 * std);

            // IQR-based threshold
            let mut sorted_values = feature.to_vec();
            sorted_values.sort_by(|a, b| a.partial_cmp(b).expect("operation should succeed"));
            let q1_idx = sorted_values.len() / 4;
            let q3_idx = 3 * sorted_values.len() / 4;
            let q1 = sorted_values[q1_idx];
            let q3 = sorted_values[q3_idx];
            let iqr = q3 - q1;
            statistical_thresholds.insert(ThresholdMethod::IQR, 1.5 * iqr);
        }

        Ok(AnomalyFeatures {
            statistical_thresholds,
            isolation_scores,
            distance_thresholds,
            density_thresholds,
            contamination_rate,
        })
    }

    // Helper methods for feature computation
    fn compute_pixel_statistic(
        &self,
        x: &Array2<f64>,
        statistic: PixelStatistic,
    ) -> Result<f64, SklearsError> {
        match statistic {
            PixelStatistic::Mean => Ok(x.mean().unwrap_or(0.0)),
            PixelStatistic::Median => {
                let mut values: Vec<f64> = x.iter().cloned().collect();
                values.sort_by(|a, b| a.partial_cmp(b).expect("operation should succeed"));
                let mid = values.len() / 2;
                Ok(if values.len() % 2 == 0 {
                    (values[mid - 1] + values[mid]) / 2.0
                } else {
                    values[mid]
                })
            }
            PixelStatistic::StandardDeviation => {
                let mean = x.mean().unwrap_or(0.0);
                let variance =
                    x.iter().map(|&val| (val - mean).powi(2)).sum::<f64>() / x.len() as f64;
                Ok(variance.sqrt())
            }
            _ => Ok(0.0), // Simplified for other statistics
        }
    }

    fn compute_color_histogram(
        &self,
        x: &Array2<f64>,
        bins: usize,
        _color_space: ColorSpace,
    ) -> Result<Vec<f64>, SklearsError> {
        // Simplified histogram computation
        let min_val = x.iter().fold(f64::INFINITY, |a, &b| a.min(b));
        let max_val = x.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b));
        let bin_width = (max_val - min_val) / bins as f64;

        let mut histogram = vec![0.0; bins];
        for &value in x.iter() {
            let bin_idx = ((value - min_val) / bin_width).floor() as usize;
            let bin_idx = bin_idx.min(bins - 1);
            histogram[bin_idx] += 1.0;
        }

        // Normalize histogram
        let total: f64 = histogram.iter().sum();
        if total > 0.0 {
            for count in &mut histogram {
                *count /= total;
            }
        }

        Ok(histogram)
    }

    fn compute_seasonal_pattern(
        &self,
        series: &scirs2_core::ndarray::ArrayView1<f64>,
        period: usize,
    ) -> Result<Vec<f64>, SklearsError> {
        let mut pattern = vec![0.0; period];
        let mut counts = vec![0; period];

        for (i, &value) in series.iter().enumerate() {
            let seasonal_idx = i % period;
            pattern[seasonal_idx] += value;
            counts[seasonal_idx] += 1;
        }

        // Average by count
        for (i, count) in counts.iter().enumerate() {
            if *count > 0 {
                pattern[i] /= *count as f64;
            }
        }

        Ok(pattern)
    }

    fn compute_moving_average(
        &self,
        series: &scirs2_core::ndarray::ArrayView1<f64>,
        window: usize,
    ) -> Result<Vec<f64>, SklearsError> {
        let mut moving_avg = Vec::new();

        for i in 0..series.len() {
            let start = if i >= window { i - window + 1 } else { 0 };
            let end = i + 1;
            let window_sum: f64 = series.slice(scirs2_core::ndarray::s![start..end]).sum();
            let window_size = end - start;
            moving_avg.push(window_sum / window_size as f64);
        }

        Ok(moving_avg)
    }
}

impl Predict<Array2<f64>, Array1<i32>> for TrainedDomainClassifier {
    fn predict(&self, x: &Array2<f64>) -> Result<Array1<i32>, SklearsError> {
        let n_samples = x.nrows();
        let mut predictions = Array1::zeros(n_samples);

        match &self.strategy {
            DomainStrategy::ComputerVision(cv_strategy) => {
                self.predict_cv(x, cv_strategy, &mut predictions)?;
            }
            DomainStrategy::NLP(nlp_strategy) => {
                self.predict_nlp(x, nlp_strategy, &mut predictions)?;
            }
            DomainStrategy::TimeSeries(ts_strategy) => {
                self.predict_ts(x, ts_strategy, &mut predictions)?;
            }
            DomainStrategy::Recommendation(rec_strategy) => {
                self.predict_rec(x, rec_strategy, &mut predictions)?;
            }
            DomainStrategy::AnomalyDetection(anomaly_strategy) => {
                self.predict_anomaly(x, anomaly_strategy, &mut predictions)?;
            }
        }

        Ok(predictions)
    }
}

impl TrainedDomainClassifier {
    fn predict_cv(
        &self,
        x: &Array2<f64>,
        strategy: &CVStrategy,
        predictions: &mut Array1<i32>,
    ) -> Result<(), SklearsError> {
        match strategy {
            CVStrategy::MostFrequentImageClass => {
                let most_frequent = self
                    .class_counts
                    .iter()
                    .max_by_key(|(_, &count)| count)
                    .map(|(&class, _)| class)
                    .unwrap_or(0);
                predictions.fill(most_frequent);
            }
            CVStrategy::RandomImageClass => {
                let mut rng = if let Some(seed) = self.random_state {
                    scirs2_core::random::rngs::StdRng::seed_from_u64(seed)
                } else {
                    scirs2_core::random::rngs::StdRng::seed_from_u64(0)
                };

                let total_count: usize = self.class_counts.values().sum();
                for i in 0..predictions.len() {
                    let rand_val = rng.random_range(0..total_count);
                    let mut cumsum = 0;
                    for (&class, &count) in &self.class_counts {
                        cumsum += count;
                        if rand_val < cumsum {
                            predictions[i] = class;
                            break;
                        }
                    }
                }
            }
            CVStrategy::PixelIntensity { statistic } => {
                // Use pixel statistics to make predictions
                if let DomainFeatures::ComputerVision(cv_features) = &self.domain_features {
                    if let Some(&threshold) = cv_features.pixel_statistics.get(statistic) {
                        for i in 0..predictions.len() {
                            let pixel_value = x.row(i).mean().unwrap_or(0.0);
                            predictions[i] = if pixel_value > threshold { 1 } else { 0 };
                        }
                    }
                }
            }
            _ => {
                // Default to most frequent class
                let most_frequent = self
                    .class_counts
                    .iter()
                    .max_by_key(|(_, &count)| count)
                    .map(|(&class, _)| class)
                    .unwrap_or(0);
                predictions.fill(most_frequent);
            }
        }
        Ok(())
    }

    fn predict_nlp(
        &self,
        x: &Array2<f64>,
        strategy: &NLPStrategy,
        predictions: &mut Array1<i32>,
    ) -> Result<(), SklearsError> {
        match strategy {
            NLPStrategy::MostFrequentTextClass => {
                let most_frequent = self
                    .class_counts
                    .iter()
                    .max_by_key(|(_, &count)| count)
                    .map(|(&class, _)| class)
                    .unwrap_or(0);
                predictions.fill(most_frequent);
            }
            NLPStrategy::DocumentLength => {
                // Use document length (sum of features) to predict
                let median_length = {
                    let mut lengths: Vec<f64> = (0..x.nrows()).map(|i| x.row(i).sum()).collect();
                    lengths.sort_by(|a, b| a.partial_cmp(b).expect("operation should succeed"));
                    lengths[lengths.len() / 2]
                };

                for i in 0..predictions.len() {
                    let doc_length = x.row(i).sum();
                    predictions[i] = if doc_length > median_length { 1 } else { 0 };
                }
            }
            _ => {
                // Default to most frequent class
                let most_frequent = self
                    .class_counts
                    .iter()
                    .max_by_key(|(_, &count)| count)
                    .map(|(&class, _)| class)
                    .unwrap_or(0);
                predictions.fill(most_frequent);
            }
        }
        Ok(())
    }

    fn predict_ts(
        &self,
        x: &Array2<f64>,
        strategy: &TimeSeriesStrategy,
        predictions: &mut Array1<i32>,
    ) -> Result<(), SklearsError> {
        match strategy {
            TimeSeriesStrategy::SeasonalPattern { period } => {
                // Use seasonal patterns to predict
                if let DomainFeatures::TimeSeries(ts_features) = &self.domain_features {
                    if let Some(pattern) = ts_features.seasonal_patterns.get(period) {
                        for i in 0..predictions.len() {
                            let seasonal_idx = i % period;
                            let seasonal_value = pattern.get(seasonal_idx).unwrap_or(&0.0);
                            predictions[i] = if *seasonal_value > 0.5 { 1 } else { 0 };
                        }
                    }
                }
            }
            TimeSeriesStrategy::RandomWalk { drift } => {
                let mut current_value = 0.0;
                let mut rng = if let Some(seed) = self.random_state {
                    scirs2_core::random::rngs::StdRng::seed_from_u64(seed)
                } else {
                    scirs2_core::random::rngs::StdRng::seed_from_u64(0)
                };

                for i in 0..predictions.len() {
                    current_value += drift + rng.random_range(-0.1..0.1);
                    predictions[i] = if current_value > 0.0 { 1 } else { 0 };
                }
            }
            _ => {
                // Default to most frequent class
                let most_frequent = self
                    .class_counts
                    .iter()
                    .max_by_key(|(_, &count)| count)
                    .map(|(&class, _)| class)
                    .unwrap_or(0);
                predictions.fill(most_frequent);
            }
        }
        Ok(())
    }

    fn predict_rec(
        &self,
        x: &Array2<f64>,
        strategy: &RecStrategy,
        predictions: &mut Array1<i32>,
    ) -> Result<(), SklearsError> {
        match strategy {
            RecStrategy::GlobalAverage => {
                if let DomainFeatures::Recommendation(rec_features) = &self.domain_features {
                    let threshold = rec_features.global_average;
                    for i in 0..predictions.len() {
                        // Use some feature as a rating proxy
                        let rating_proxy = if x.ncols() > 2 { x[[i, 2]] } else { threshold };
                        predictions[i] = if rating_proxy > threshold { 1 } else { 0 };
                    }
                }
            }
            RecStrategy::ItemPopularity => {
                if let DomainFeatures::Recommendation(rec_features) = &self.domain_features {
                    let median_popularity = {
                        let mut popularities: Vec<f64> =
                            rec_features.item_popularity.values().cloned().collect();
                        if popularities.is_empty() {
                            0.0
                        } else {
                            popularities.sort_by(|a, b| {
                                a.partial_cmp(b).expect("operation should succeed")
                            });
                            popularities[popularities.len() / 2]
                        }
                    };

                    for i in 0..predictions.len() {
                        let item_id = if x.ncols() > 1 { x[[i, 1]] as usize } else { 0 };
                        let popularity = rec_features.item_popularity.get(&item_id).unwrap_or(&0.0);
                        predictions[i] = if *popularity > median_popularity {
                            1
                        } else {
                            0
                        };
                    }
                }
            }
            _ => {
                // Default prediction
                let most_frequent = self
                    .class_counts
                    .iter()
                    .max_by_key(|(_, &count)| count)
                    .map(|(&class, _)| class)
                    .unwrap_or(0);
                predictions.fill(most_frequent);
            }
        }
        Ok(())
    }

    fn predict_anomaly(
        &self,
        x: &Array2<f64>,
        strategy: &AnomalyStrategy,
        predictions: &mut Array1<i32>,
    ) -> Result<(), SklearsError> {
        match strategy {
            AnomalyStrategy::AlwaysNormal => {
                predictions.fill(0); // 0 = normal, 1 = anomaly
            }
            AnomalyStrategy::RandomAnomaly { contamination } => {
                let mut rng = if let Some(seed) = self.random_state {
                    scirs2_core::random::rngs::StdRng::seed_from_u64(seed)
                } else {
                    scirs2_core::random::rngs::StdRng::seed_from_u64(0)
                };

                for i in 0..predictions.len() {
                    predictions[i] = if rng.random::<f64>() < *contamination {
                        1
                    } else {
                        0
                    };
                }
            }
            AnomalyStrategy::StatisticalThreshold { method, .. } => {
                if let DomainFeatures::AnomalyDetection(anomaly_features) = &self.domain_features {
                    if let Some(&threshold) = anomaly_features.statistical_thresholds.get(method) {
                        for i in 0..predictions.len() {
                            if x.ncols() > 0 {
                                let value = x[[i, 0]];
                                let is_anomaly = match method {
                                    ThresholdMethod::ZScore | ThresholdMethod::ModifiedZScore => {
                                        value.abs() > threshold
                                    }
                                    ThresholdMethod::IQR => value > threshold,
                                    ThresholdMethod::Percentile => value > threshold,
                                };
                                predictions[i] = if is_anomaly { 1 } else { 0 };
                            }
                        }
                    }
                }
            }
            _ => {
                // Default to always normal
                predictions.fill(0);
            }
        }
        Ok(())
    }
}

/// Utility functions for domain-specific data preprocessing
pub struct DomainPreprocessor;

impl DomainPreprocessor {
    /// Preprocess image data for computer vision baselines
    pub fn preprocess_images(images: &Array3<f64>) -> Result<Array2<f64>, SklearsError> {
        // Flatten images to feature vectors
        let (n_images, height, width) = images.dim();
        let features = images
            .clone()
            .into_shape((n_images, height * width))
            .map_err(|e| SklearsError::InvalidInput(format!("Shape error: {}", e)))?;
        Ok(features)
    }

    /// Preprocess text data for NLP baselines (placeholder)
    pub fn preprocess_text(texts: &[String]) -> Result<Array2<f64>, SklearsError> {
        // Simple text preprocessing - convert to feature vectors
        let n_texts = texts.len();
        let max_length = texts.iter().map(|s| s.len()).max().unwrap_or(0);

        let mut features = Array2::zeros((n_texts, max_length));
        for (i, text) in texts.iter().enumerate() {
            for (j, byte) in text.bytes().enumerate() {
                if j < max_length {
                    features[[i, j]] = byte as f64 / 255.0; // Normalize
                }
            }
        }

        Ok(features)
    }

    /// Preprocess time series data
    pub fn preprocess_timeseries(
        series: &Array2<f64>,
        window_size: usize,
    ) -> Result<Array2<f64>, SklearsError> {
        let (n_series, length) = series.dim();
        if length < window_size {
            return Err(SklearsError::InvalidInput(
                "Time series length must be at least window size".to_string(),
            ));
        }

        let n_windows = length - window_size + 1;
        let mut windowed = Array2::zeros((n_series * n_windows, window_size));

        for i in 0..n_series {
            for j in 0..n_windows {
                let window = series.slice(scirs2_core::ndarray::s![i, j..j + window_size]);
                windowed
                    .slice_mut(scirs2_core::ndarray::s![i * n_windows + j, ..])
                    .assign(&window);
            }
        }

        Ok(windowed)
    }
}

#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
    use super::*;
    use scirs2_core::ndarray::array;

    #[test]
    fn test_cv_pixel_intensity_classifier() {
        let x = Array2::from_shape_vec(
            (4, 4),
            vec![
                0.1, 0.2, 0.3, 0.4, 0.8, 0.9, 0.7, 0.6, 0.2, 0.1, 0.4, 0.3, 0.9, 0.8, 0.6, 0.7,
            ],
        )
        .expect("operation should succeed");
        let y = array![0, 1, 0, 1];

        let classifier = DomainClassifier::computer_vision(CVStrategy::PixelIntensity {
            statistic: PixelStatistic::Mean,
        });
        let fitted = classifier
            .fit(&x, &y)
            .expect("model fitting should succeed");
        let predictions = fitted.predict(&x).expect("prediction should succeed");

        assert_eq!(predictions.len(), 4);
    }

    #[test]
    fn test_nlp_document_length_classifier() {
        let x = Array2::from_shape_vec(
            (4, 3),
            vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5],
        )
        .expect("operation should succeed");
        let y = array![0, 1, 0, 1];

        let classifier = DomainClassifier::nlp(NLPStrategy::DocumentLength);
        let fitted = classifier
            .fit(&x, &y)
            .expect("model fitting should succeed");
        let predictions = fitted.predict(&x).expect("prediction should succeed");

        assert_eq!(predictions.len(), 4);
    }

    #[test]
    fn test_anomaly_detection_classifier() {
        let x = Array2::from_shape_vec(
            (4, 2),
            vec![
                1.0, 2.0, 3.0, 4.0, 100.0, 200.0, // Potential anomaly
                2.0, 3.0,
            ],
        )
        .expect("operation should succeed");
        let y = array![0, 0, 1, 0]; // 1 indicates anomaly

        let classifier =
            DomainClassifier::anomaly_detection(AnomalyStrategy::StatisticalThreshold {
                method: ThresholdMethod::ZScore,
                contamination: 0.25,
            });
        let fitted = classifier
            .fit(&x, &y)
            .expect("model fitting should succeed");
        let predictions = fitted.predict(&x).expect("prediction should succeed");

        assert_eq!(predictions.len(), 4);
    }

    #[test]
    fn test_time_series_seasonal_classifier() {
        let x = Array2::from_shape_vec((8, 1), vec![1.0, 2.0, 3.0, 4.0, 1.0, 2.0, 3.0, 4.0])
            .expect("shape and data length should match");
        let y = array![0, 1, 1, 0, 0, 1, 1, 0];

        let classifier =
            DomainClassifier::time_series(TimeSeriesStrategy::SeasonalPattern { period: 4 });
        let fitted = classifier
            .fit(&x, &y)
            .expect("model fitting should succeed");
        let predictions = fitted.predict(&x).expect("prediction should succeed");

        assert_eq!(predictions.len(), 8);
    }

    #[test]
    fn test_recommendation_classifier() {
        let x = Array2::from_shape_vec(
            (4, 3),
            vec![
                0.0, 0.0, 4.0, // user_id, item_id, rating
                0.0, 1.0, 5.0, 1.0, 0.0, 3.0, 1.0, 1.0, 2.0,
            ],
        )
        .expect("operation should succeed");
        let y = array![1, 1, 0, 0]; // 1 = recommend, 0 = don't recommend

        let classifier = DomainClassifier::recommendation(RecStrategy::GlobalAverage);
        let fitted = classifier
            .fit(&x, &y)
            .expect("model fitting should succeed");
        let predictions = fitted.predict(&x).expect("prediction should succeed");

        assert_eq!(predictions.len(), 4);
    }

    #[test]
    fn test_domain_preprocessor() {
        // Test image preprocessing
        let images = Array3::zeros((2, 4, 4)); // 2 images of 4x4
        let flattened =
            DomainPreprocessor::preprocess_images(&images).expect("operation should succeed");
        assert_eq!(flattened.shape(), &[2, 16]);

        // Test text preprocessing
        let texts = vec!["hello".to_string(), "world".to_string()];
        let text_features =
            DomainPreprocessor::preprocess_text(&texts).expect("operation should succeed");
        assert_eq!(text_features.shape(), &[2, 5]);

        // Test time series preprocessing
        let series = Array2::from_shape_vec(
            (2, 6),
            vec![
                1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0,
            ],
        )
        .expect("operation should succeed");
        let windowed = DomainPreprocessor::preprocess_timeseries(&series, 3)
            .expect("operation should succeed");
        assert_eq!(windowed.shape(), &[8, 3]); // 2 series * 4 windows, window size 3
    }
}
