//! Built-in dataset loaders
//!
//! This module provides basic dataset loading capabilities.
//! Currently contains stub implementations - full implementations are planned.

use scirs2_core::ndarray::{Array1, Array2};

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// Dataset structure containing features, targets, and metadata
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Dataset {
    /// Feature matrix (samples × features)
    pub data: Array2<f64>,
    /// Target vector
    pub target: Array1<i32>,
    /// Feature names
    pub feature_names: Option<Vec<String>>,
    /// Target names (for classification)
    pub target_names: Option<Vec<String>>,
    /// Dataset description and metadata
    pub description: String,
}

impl Dataset {
    /// Create a new dataset
    pub fn new(
        data: Array2<f64>,
        target: Array1<i32>,
        feature_names: Option<Vec<String>>,
        target_names: Option<Vec<String>>,
        description: String,
    ) -> Self {
        Self {
            data,
            target,
            feature_names,
            target_names,
            description,
        }
    }

    /// Get the number of samples
    pub fn n_samples(&self) -> usize {
        self.data.nrows()
    }

    /// Get the number of features
    pub fn n_features(&self) -> usize {
        self.data.ncols()
    }
}

/// Load the Iris dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Iris dataset.
/// The data is produced by `make_classification` with matching dimensions (150 samples,
/// 4 features, 3 classes) but does not contain actual Iris flower measurements.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_iris() -> Dataset {
    // This is a simplified stub - in the real implementation this would load actual data
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(150, 4, 3, 0, 3, Some(42))
        .expect("Failed to generate iris-like dataset");

    Dataset::new(
        data,
        target,
        Some(vec![
            "sepal_length".to_string(),
            "sepal_width".to_string(),
            "petal_length".to_string(),
            "petal_width".to_string(),
        ]),
        Some(vec![
            "setosa".to_string(),
            "versicolor".to_string(),
            "virginica".to_string(),
        ]),
        "Iris flower classification dataset".to_string(),
    )
}

/// Load the Wine dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Wine recognition dataset.
/// The data is produced by `make_classification` with matching dimensions (178 samples,
/// 13 features, 3 classes) but does not contain actual wine chemical analysis measurements.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_wine() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(178, 13, 3, 0, 3, Some(123))
        .expect("Failed to generate wine-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some(vec![
            "class_0".to_string(),
            "class_1".to_string(),
            "class_2".to_string(),
        ]),
        "Wine recognition dataset".to_string(),
    )
}

/// Load the Breast Cancer dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Breast Cancer Wisconsin dataset.
/// The data is produced by `make_classification` with matching dimensions (569 samples,
/// 30 features, 2 classes) but does not contain actual diagnostic measurements.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_breast_cancer() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(569, 30, 2, 0, 2, Some(456))
        .expect("Failed to generate breast cancer-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some(vec!["malignant".to_string(), "benign".to_string()]),
        "Breast Cancer Wisconsin dataset".to_string(),
    )
}

/// Load the Digits dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Optical Digits dataset.
/// The data is produced by `make_classification` with matching dimensions (1797 samples,
/// 64 features, 10 classes) but does not contain actual handwritten digit pixel values.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_digits() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(1797, 64, 10, 0, 10, Some(789))
        .expect("Failed to generate digits-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some((0..10).map(|i| i.to_string()).collect()),
        "Optical recognition of handwritten digits dataset".to_string(),
    )
}

/// Load the Diabetes dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Diabetes progression dataset.
/// The data is produced by `make_regression` with matching dimensions (442 samples,
/// 10 features) but does not contain actual patient measurements. Additionally, the
/// continuous regression targets are discretized into binary classes, losing the original
/// progression values. Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_diabetes() -> Dataset {
    use crate::generators::basic::make_regression;

    let (data, target_f64) = make_regression(442, 10, 1, 0.1, Some(101112))
        .expect("Failed to generate diabetes-like dataset");

    // Convert regression targets to discrete classes for consistency with Dataset structure
    let target = target_f64.mapv(|x| if x > 0.0 { 1 } else { 0 });

    Dataset::new(
        data,
        target,
        None,
        Some(vec!["low".to_string(), "high".to_string()]),
        "Diabetes progression dataset".to_string(),
    )
}

/// Load the Boston Housing dataset (stub implementation with ethical note)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Boston Housing dataset.
/// The data is produced by `make_regression` with matching dimensions (506 samples,
/// 13 features) but does not contain actual housing data. The continuous regression
/// targets are discretized into binary classes. Note: the real Boston Housing dataset
/// has known ethical concerns and has been deprecated by scikit-learn.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_boston() -> Dataset {
    use crate::generators::basic::make_regression;

    let (data, target_f64) = make_regression(506, 13, 1, 0.1, Some(131415))
        .expect("Failed to generate boston-like dataset");

    // Convert to classes for consistency
    let target = target_f64.mapv(|x| if x > 0.0 { 1 } else { 0 });

    Dataset::new(
        data,
        target,
        None,
        Some(vec!["low_price".to_string(), "high_price".to_string()]),
        "Boston Housing dataset (Note: This dataset has ethical concerns and is included for historical/compatibility purposes only)".to_string(),
    )
}

/// Load California Housing dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real California Housing dataset.
/// The data is produced by `make_regression` with matching dimensions (20640 samples,
/// 8 features) but does not contain actual census-derived housing data. The continuous
/// regression targets are discretized into binary classes.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_california_housing() -> Dataset {
    use crate::generators::basic::make_regression;

    let (data, target_f64) = make_regression(20640, 8, 1, 0.1, Some(161718))
        .expect("Failed to generate california housing-like dataset");

    let target = target_f64.mapv(|x| if x > 0.0 { 1 } else { 0 });

    Dataset::new(
        data,
        target,
        None,
        Some(vec!["affordable".to_string(), "expensive".to_string()]),
        "California Housing dataset".to_string(),
    )
}

/// Load Linnerud dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Linnerud physiological dataset.
/// The data is produced by `make_classification` with matching dimensions (20 samples,
/// 3 features, 3 classes) but does not contain actual exercise and physiological measurements.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_linnerud() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(20, 3, 3, 0, 3, Some(192021))
        .expect("Failed to generate linnerud-like dataset");

    Dataset::new(
        data,
        target,
        Some(vec![
            "chins".to_string(),
            "situps".to_string(),
            "jumps".to_string(),
        ]),
        Some(vec![
            "weight".to_string(),
            "waist".to_string(),
            "pulse".to_string(),
        ]),
        "Linnerud physiological dataset".to_string(),
    )
}

/// Load MNIST dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real MNIST dataset.
/// The data is produced by `make_classification` with reduced dimensions (1000 samples,
/// 784 features, 10 classes) but does not contain actual handwritten digit images.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_mnist() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(1000, 784, 10, 0, 10, Some(222324))
        .expect("Failed to generate mnist-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some((0..10).map(|i| i.to_string()).collect()),
        "MNIST handwritten digit recognition dataset (subset)".to_string(),
    )
}

/// Load Fashion-MNIST dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Fashion-MNIST dataset.
/// The data is produced by `make_classification` with reduced dimensions (1000 samples,
/// 784 features, 10 classes) but does not contain actual clothing item images.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_fashion_mnist() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(1000, 784, 10, 0, 10, Some(252627))
        .expect("Failed to generate fashion-mnist-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some(vec![
            "T-shirt/top".to_string(),
            "Trouser".to_string(),
            "Pullover".to_string(),
            "Dress".to_string(),
            "Coat".to_string(),
            "Sandal".to_string(),
            "Shirt".to_string(),
            "Sneaker".to_string(),
            "Bag".to_string(),
            "Ankle boot".to_string(),
        ]),
        "Fashion-MNIST clothing classification dataset (subset)".to_string(),
    )
}

/// Load CIFAR-10 dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real CIFAR-10 dataset.
/// The data is produced by `make_classification` with reduced dimensions (1000 samples,
/// 3072 features, 10 classes) but does not contain actual object images.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_cifar10() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(1000, 3072, 10, 0, 10, Some(282930))
        .expect("Failed to generate cifar10-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some(vec![
            "airplane".to_string(),
            "automobile".to_string(),
            "bird".to_string(),
            "cat".to_string(),
            "deer".to_string(),
            "dog".to_string(),
            "frog".to_string(),
            "horse".to_string(),
            "ship".to_string(),
            "truck".to_string(),
        ]),
        "CIFAR-10 object recognition dataset (subset)".to_string(),
    )
}

/// Load 20 Newsgroups dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real 20 Newsgroups dataset.
/// The data is produced by `make_classification` with reduced dimensions (1000 samples,
/// 1000 features, 20 classes) but does not contain actual newsgroup text data or TF-IDF features.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_newsgroups() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(1000, 1000, 20, 0, 20, Some(313233))
        .expect("Failed to generate newsgroups-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some((0..20).map(|i| format!("topic_{}", i)).collect()),
        "20 Newsgroups text classification dataset (subset)".to_string(),
    )
}

/// Load Reuters dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Reuters news dataset.
/// The data is produced by `make_classification` with reduced dimensions (1000 samples,
/// 500 features, 8 classes) but does not contain actual news article text or features.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_reuters() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(1000, 500, 8, 0, 8, Some(343536))
        .expect("Failed to generate reuters-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some((0..8).map(|i| format!("category_{}", i)).collect()),
        "Reuters news categorization dataset (subset)".to_string(),
    )
}

/// Load Olivetti Faces dataset (stub implementation)
///
/// # Warning
///
/// This function returns **synthetic generated data**, not the real Olivetti Faces dataset.
/// The data is produced by `make_classification` with matching dimensions (400 samples,
/// 4096 features, 40 classes) but does not contain actual face images.
/// Full implementation loading real data is planned for v0.2.0.
#[deprecated(note = "Returns synthetic data, not real dataset. Full implementation planned for v0.2.0.")]
pub fn load_olivetti_faces() -> Dataset {
    use crate::generators::basic::make_classification;

    let (data, target) = make_classification(400, 4096, 40, 0, 40, Some(373839))
        .expect("Failed to generate olivetti-like dataset");

    Dataset::new(
        data,
        target,
        None,
        Some((0..40).map(|i| format!("person_{}", i)).collect()),
        "Olivetti faces recognition dataset".to_string(),
    )
}

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

    #[test]
    fn test_load_iris() {
        let dataset = load_iris();
        assert_eq!(dataset.n_samples(), 150);
        assert_eq!(dataset.n_features(), 4);
        assert!(dataset.feature_names.is_some());
        assert!(dataset.target_names.is_some());
    }

    #[test]
    fn test_load_wine() {
        let dataset = load_wine();
        assert_eq!(dataset.n_samples(), 178);
        assert_eq!(dataset.n_features(), 13);
        assert!(dataset.target_names.is_some());
    }

    #[test]
    fn test_load_breast_cancer() {
        let dataset = load_breast_cancer();
        assert_eq!(dataset.n_samples(), 569);
        assert_eq!(dataset.n_features(), 30);
        assert!(dataset.target_names.is_some());
        assert_eq!(dataset.target_names.as_ref().expect("operation should succeed").len(), 2);
    }

    #[test]
    fn test_load_digits() {
        let dataset = load_digits();
        assert_eq!(dataset.n_samples(), 1797);
        assert_eq!(dataset.n_features(), 64);
        assert!(dataset.target_names.is_some());
        assert_eq!(dataset.target_names.as_ref().expect("operation should succeed").len(), 10);
    }

    #[test]
    fn test_dataset_new() {
        use crate::generators::basic::make_classification;
        use scirs2_core::ndarray::Array1;

        let (data, target) = make_classification(100, 4, 3, 0, 3, Some(42)).expect("operation should succeed");
        let dataset = Dataset::new(
            data,
            target,
            Some(vec![
                "f1".to_string(),
                "f2".to_string(),
                "f3".to_string(),
                "f4".to_string(),
            ]),
            Some(vec!["c1".to_string(), "c2".to_string(), "c3".to_string()]),
            "Test dataset".to_string(),
        );

        assert_eq!(dataset.n_samples(), 100);
        assert_eq!(dataset.n_features(), 4);
        assert!(dataset.feature_names.is_some());
        assert!(dataset.target_names.is_some());
        assert_eq!(dataset.description, "Test dataset");
    }
}
