//! Comprehensive Report Generation System
//!
//! This module provides a complete report generation system for image analysis results,
//! supporting multiple output formats (HTML, Markdown, Text) and comprehensive
//! analysis sections including basic statistics, quality metrics, and texture analysis.

use scirs2_core::ndarray::ArrayStatCompat;
use scirs2_core::ndarray::ArrayView2;
use scirs2_core::numeric::{Float, FromPrimitive, ToPrimitive, Zero};
use std::fmt::{Debug, Write};

use crate::analysis::{ImageQualityMetrics, TextureMetrics};
use crate::error::{NdimageError, NdimageResult};
use crate::visualization::types::{ReportConfig, ReportFormat};
use statrs::statistics::Statistics;

/// Generate a comprehensive analysis report
///
/// This function creates a detailed report containing image information, statistics,
/// quality metrics, and texture analysis based on the provided configuration.
///
/// # Arguments
///
/// * `image` - The image data as a 2D array view
/// * `qualitymetrics` - Optional quality metrics for the image
/// * `texturemetrics` - Optional texture analysis metrics
/// * `config` - Configuration specifying report format and sections to include
///
/// # Returns
///
/// A formatted report string in the specified format (HTML, Markdown, or Text)
///
/// # Examples
///
/// ```rust,ignore
/// use scirs2_core::ndarray::Array2;
/// use scirs2_ndimage::visualization::{ReportConfig, ReportFormat, generate_report};
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let image = Array2::zeros((100, 100));
/// let config = ReportConfig::new()
///     .with_format(ReportFormat::Markdown)
///     .with_header("Analysis Report", "SciRS2");
///
/// let report = generate_report(&image.view(), None, None, &config)?;
/// # Ok(())
/// # }
/// ```
#[allow(dead_code)]
pub fn generate_report<T>(
    image: &ArrayView2<T>,
    qualitymetrics: Option<&ImageQualityMetrics<T>>,
    texturemetrics: Option<&TextureMetrics<T>>,
    config: &ReportConfig,
) -> NdimageResult<String>
where
    T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
{
    let mut report = String::new();

    match config.format {
        ReportFormat::Html => {
            writeln!(&mut report, "<!DOCTYPE html>")?;
            writeln!(&mut report, "<html><head><title>{}</title>", config.title)?;
            writeln!(&mut report, "<style>")?;
            writeln!(
                &mut report,
                "body {{ font-family: Arial, sans-serif; margin: 20px; }}"
            )?;
            writeln!(
                &mut report,
                "table {{ border-collapse: collapse; width: 100%; }}"
            )?;
            writeln!(
                &mut report,
                "th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}"
            )?;
            writeln!(&mut report, "th {{ background-color: #f2f2f2; }}")?;
            writeln!(
                &mut report,
                ".metric-value {{ font-weight: bold; color: #2E86AB; }}"
            )?;
            writeln!(&mut report, "</style></head><body>")?;
            writeln!(&mut report, "<h1>{}</h1>", config.title)?;
            writeln!(
                &mut report,
                "<p><em>Generated by {}</em></p>",
                config.author
            )?;
        }
        ReportFormat::Markdown => {
            writeln!(&mut report, "# {}", config.title)?;
            writeln!(&mut report)?;
            writeln!(&mut report, "*Generated by {}*", config.author)?;
            writeln!(&mut report)?;
        }
        ReportFormat::Text => {
            writeln!(&mut report, "{}", config.title)?;
            writeln!(&mut report, "{}", "=".repeat(config.title.len()))?;
            writeln!(&mut report)?;
            writeln!(&mut report, "Generated by: {}", config.author)?;
            writeln!(&mut report)?;
        }
    }

    // Basic image information
    let (height, width) = image.dim();
    add_image_info(&mut report, width, height, config.format)?;

    // Basic statistics
    if config.include_statistics {
        add_basic_statistics(&mut report, image, config.format)?;
    }

    // Quality metrics
    if config.include_qualitymetrics {
        if let Some(metrics) = qualitymetrics {
            add_quality_metrics(&mut report, metrics, config.format)?;
        }
    }

    // Texture analysis
    if config.includetexture_analysis {
        if let Some(metrics) = texturemetrics {
            add_texture_metrics(&mut report, metrics, config.format)?;
        }
    }

    // Close HTML if needed
    if config.format == ReportFormat::Html {
        writeln!(&mut report, "</body></html>")?;
    }

    Ok(report)
}

/// Add basic image information to the report
///
/// Includes dimensions, total pixels, and aspect ratio information.
#[allow(dead_code)]
pub fn add_image_info(
    report: &mut String,
    width: usize,
    height: usize,
    format: ReportFormat,
) -> Result<(), std::fmt::Error> {
    match format {
        ReportFormat::Html => {
            writeln!(report, "<h2>Image Information</h2>")?;
            writeln!(report, "<table>")?;
            writeln!(report, "<tr><th>Property</th><th>Value</th></tr>")?;
            writeln!(
                report,
                "<tr><td>Width</td><td class='metric-value'>{}</td></tr>",
                width
            )?;
            writeln!(
                report,
                "<tr><td>Height</td><td class='metric-value'>{}</td></tr>",
                height
            )?;
            writeln!(
                report,
                "<tr><td>Total Pixels</td><td class='metric-value'>{}</td></tr>",
                width * height
            )?;
            writeln!(
                report,
                "<tr><td>Aspect Ratio</td><td class='metric-value'>{:.3}</td></tr>",
                width as f64 / height as f64
            )?;
            writeln!(report, "</table>")?;
        }
        ReportFormat::Markdown => {
            writeln!(report, "## Image Information")?;
            writeln!(report)?;
            writeln!(report, "| Property | Value |")?;
            writeln!(report, "|----------|-------|")?;
            writeln!(report, "| Width | {} |", width)?;
            writeln!(report, "| Height | {} |", height)?;
            writeln!(report, "| Total Pixels | {} |", width * height)?;
            writeln!(
                report,
                "| Aspect Ratio | {:.3} |",
                width as f64 / height as f64
            )?;
            writeln!(report)?;
        }
        ReportFormat::Text => {
            writeln!(report, "Image Information")?;
            writeln!(report, "-----------------")?;
            writeln!(report, "Width:        {}", width)?;
            writeln!(report, "Height:       {}", height)?;
            writeln!(report, "Total Pixels: {}", width * height)?;
            writeln!(report, "Aspect Ratio: {:.3}", width as f64 / height as f64)?;
            writeln!(report)?;
        }
    }
    Ok(())
}

/// Add basic statistical information to the report
///
/// Computes and includes mean, standard deviation, variance, minimum, maximum, and range.
#[allow(dead_code)]
pub fn add_basic_statistics<T>(
    report: &mut String,
    image: &ArrayView2<T>,
    format: ReportFormat,
) -> Result<(), std::fmt::Error>
where
    T: Float + FromPrimitive + ToPrimitive + Debug + Clone,
{
    let mean = image.mean_or(T::zero());
    let min_val = image.iter().cloned().fold(T::infinity(), T::min);
    let max_val = image.iter().cloned().fold(T::neg_infinity(), T::max);

    let variance = image
        .mapv(|x| (x - mean) * (x - mean))
        .mean()
        .unwrap_or(T::zero());
    let std_dev = variance.sqrt();

    match format {
        ReportFormat::Html => {
            writeln!(report, "<h2>Basic Statistics</h2>")?;
            writeln!(report, "<table>")?;
            writeln!(report, "<tr><th>Statistic</th><th>Value</th></tr>")?;
            writeln!(
                report,
                "<tr><td>Mean</td><td class='metric-value'>{:.6}</td></tr>",
                mean.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Standard Deviation</td><td class='metric-value'>{:.6}</td></tr>",
                std_dev.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Variance</td><td class='metric-value'>{:.6}</td></tr>",
                variance.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Minimum</td><td class='metric-value'>{:.6}</td></tr>",
                min_val.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Maximum</td><td class='metric-value'>{:.6}</td></tr>",
                max_val.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Range</td><td class='metric-value'>{:.6}</td></tr>",
                (max_val - min_val).to_f64().unwrap_or(0.0)
            )?;
            writeln!(report, "</table>")?;
        }
        ReportFormat::Markdown => {
            writeln!(report, "## Basic Statistics")?;
            writeln!(report)?;
            writeln!(report, "| Statistic | Value |")?;
            writeln!(report, "|-----------|-------|")?;
            writeln!(report, "| Mean | {:.6} |", mean.to_f64().unwrap_or(0.0))?;
            writeln!(
                report,
                "| Standard Deviation | {:.6} |",
                std_dev.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Variance | {:.6} |",
                variance.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Minimum | {:.6} |",
                min_val.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Maximum | {:.6} |",
                max_val.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Range | {:.6} |",
                (max_val - min_val).to_f64().unwrap_or(0.0)
            )?;
            writeln!(report)?;
        }
        ReportFormat::Text => {
            writeln!(report, "Basic Statistics")?;
            writeln!(report, "----------------")?;
            writeln!(
                report,
                "Mean:              {:.6}",
                mean.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Standard Deviation:{:.6}",
                std_dev.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Variance:          {:.6}",
                variance.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Minimum:           {:.6}",
                min_val.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Maximum:           {:.6}",
                max_val.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Range:             {:.6}",
                (max_val - min_val).to_f64().unwrap_or(0.0)
            )?;
            writeln!(report)?;
        }
    }
    Ok(())
}

/// Add image quality metrics to the report
///
/// Includes PSNR, SSIM, MSE, SNR, entropy, and sharpness measurements.
#[allow(dead_code)]
pub fn add_quality_metrics<T>(
    report: &mut String,
    metrics: &ImageQualityMetrics<T>,
    format: ReportFormat,
) -> Result<(), std::fmt::Error>
where
    T: Float + ToPrimitive,
{
    match format {
        ReportFormat::Html => {
            writeln!(report, "<h2>Quality Metrics</h2>")?;
            writeln!(report, "<table>")?;
            writeln!(report, "<tr><th>Metric</th><th>Value</th></tr>")?;
            writeln!(
                report,
                "<tr><td>PSNR</td><td class='metric-value'>{:.3} dB</td></tr>",
                metrics.psnr.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>SSIM</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.ssim.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>MSE</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.mse.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>SNR</td><td class='metric-value'>{:.3} dB</td></tr>",
                metrics.snr.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Entropy</td><td class='metric-value'>{:.3} bits</td></tr>",
                metrics.entropy.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Sharpness</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.sharpness.to_f64().unwrap_or(0.0)
            )?;
            writeln!(report, "</table>")?;
        }
        ReportFormat::Markdown => {
            writeln!(report, "## Quality Metrics")?;
            writeln!(report)?;
            writeln!(report, "| Metric | Value |")?;
            writeln!(report, "|--------|-------|")?;
            writeln!(
                report,
                "| PSNR | {:.3} dB |",
                metrics.psnr.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| SSIM | {:.6} |",
                metrics.ssim.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| MSE | {:.6} |",
                metrics.mse.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| SNR | {:.3} dB |",
                metrics.snr.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Entropy | {:.3} bits |",
                metrics.entropy.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Sharpness | {:.6} |",
                metrics.sharpness.to_f64().unwrap_or(0.0)
            )?;
            writeln!(report)?;
        }
        ReportFormat::Text => {
            writeln!(report, "Quality Metrics")?;
            writeln!(report, "---------------")?;
            writeln!(
                report,
                "PSNR:      {:.3} dB",
                metrics.psnr.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "SSIM:      {:.6}",
                metrics.ssim.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "MSE:       {:.6}",
                metrics.mse.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "SNR:       {:.3} dB",
                metrics.snr.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Entropy:   {:.3} bits",
                metrics.entropy.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Sharpness: {:.6}",
                metrics.sharpness.to_f64().unwrap_or(0.0)
            )?;
            writeln!(report)?;
        }
    }
    Ok(())
}

/// Add texture analysis metrics to the report
///
/// Includes GLCM-based metrics, LBP uniformity, Gabor filter responses, and fractal dimension.
#[allow(dead_code)]
pub fn add_texture_metrics<T>(
    report: &mut String,
    metrics: &TextureMetrics<T>,
    format: ReportFormat,
) -> Result<(), std::fmt::Error>
where
    T: Float + ToPrimitive,
{
    match format {
        ReportFormat::Html => {
            writeln!(report, "<h2>Texture Analysis</h2>")?;
            writeln!(report, "<table>")?;
            writeln!(report, "<tr><th>Metric</th><th>Value</th></tr>")?;
            writeln!(
                report,
                "<tr><td>GLCM Contrast</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.glcm_contrast.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>GLCM Homogeneity</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.glcm_homogeneity.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>GLCM Energy</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.glcm_energy.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>LBP Uniformity</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.lbp_uniformity.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Gabor Mean</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.gabor_mean.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Gabor Std</td><td class='metric-value'>{:.6}</td></tr>",
                metrics.gabor_std.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "<tr><td>Fractal Dimension</td><td class='metric-value'>{:.3}</td></tr>",
                metrics.fractal_dimension.to_f64().unwrap_or(0.0)
            )?;
            writeln!(report, "</table>")?;
        }
        ReportFormat::Markdown => {
            writeln!(report, "## Texture Analysis")?;
            writeln!(report)?;
            writeln!(report, "| Metric | Value |")?;
            writeln!(report, "|--------|-------|")?;
            writeln!(
                report,
                "| GLCM Contrast | {:.6} |",
                metrics.glcm_contrast.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| GLCM Homogeneity | {:.6} |",
                metrics.glcm_homogeneity.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| GLCM Energy | {:.6} |",
                metrics.glcm_energy.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| LBP Uniformity | {:.6} |",
                metrics.lbp_uniformity.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Gabor Mean | {:.6} |",
                metrics.gabor_mean.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Gabor Std | {:.6} |",
                metrics.gabor_std.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "| Fractal Dimension | {:.3} |",
                metrics.fractal_dimension.to_f64().unwrap_or(0.0)
            )?;
            writeln!(report)?;
        }
        ReportFormat::Text => {
            writeln!(report, "Texture Analysis")?;
            writeln!(report, "----------------")?;
            writeln!(
                report,
                "GLCM Contrast:     {:.6}",
                metrics.glcm_contrast.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "GLCM Homogeneity:  {:.6}",
                metrics.glcm_homogeneity.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "GLCM Energy:       {:.6}",
                metrics.glcm_energy.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "LBP Uniformity:    {:.6}",
                metrics.lbp_uniformity.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Gabor Mean:        {:.6}",
                metrics.gabor_mean.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Gabor Std:         {:.6}",
                metrics.gabor_std.to_f64().unwrap_or(0.0)
            )?;
            writeln!(
                report,
                "Fractal Dimension: {:.3}",
                metrics.fractal_dimension.to_f64().unwrap_or(0.0)
            )?;
            writeln!(report)?;
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::visualization::types::{ReportConfig, ReportFormat};
    use scirs2_core::ndarray::Array2;

    #[test]
    fn test_generate_basic_report() {
        let image = Array2::<f64>::ones((10, 10));
        let config = ReportConfig::new()
            .with_format(ReportFormat::Text)
            .with_header("Test Report", "Test Suite")
            .minimal(); // Only basic info, no advanced metrics

        let result = generate_report(&image.view(), None, None, &config);
        assert!(result.is_ok());

        let report = result.expect("Operation failed");
        assert!(report.contains("Test Report"));
        assert!(report.contains("Test Suite"));
        assert!(report.contains("Image Information"));
        assert!(report.contains("Width:        10"));
        assert!(report.contains("Height:       10"));
    }

    #[test]
    fn test_generate_html_report() {
        let image = Array2::<f64>::zeros((5, 5));
        let config = ReportConfig::new()
            .with_format(ReportFormat::Html)
            .with_header("HTML Test", "HTML Suite")
            .with_sections(true, false, false, false, false); // Only statistics

        let result = generate_report(&image.view(), None, None, &config);
        assert!(result.is_ok());

        let report = result.expect("Operation failed");
        assert!(report.contains("<!DOCTYPE html>"));
        assert!(report.contains("<h1>HTML Test</h1>"));
        assert!(report.contains("Basic Statistics"));
        assert!(report.contains("</body></html>"));
    }

    #[test]
    fn test_generate_markdown_report() {
        let image = Array2::from_elem((3, 3), 2.5f64);
        let config = ReportConfig::new()
            .with_format(ReportFormat::Markdown)
            .with_header("Markdown Test", "MD Suite")
            .with_sections(true, false, false, false, false);

        let result = generate_report(&image.view(), None, None, &config);
        assert!(result.is_ok());

        let report = result.expect("Operation failed");
        assert!(report.contains("# Markdown Test"));
        assert!(report.contains("*Generated by MD Suite*"));
        assert!(report.contains("## Image Information"));
        assert!(report.contains("## Basic Statistics"));
        assert!(report.contains("| Mean | 2.500000 |"));
    }

    #[test]
    fn test_add_image_info_all_formats() {
        let mut html_report = String::new();
        let mut md_report = String::new();
        let mut text_report = String::new();

        // Test HTML format
        assert!(add_image_info(&mut html_report, 100, 200, ReportFormat::Html).is_ok());
        assert!(html_report.contains("<h2>Image Information</h2>"));
        assert!(html_report.contains("100"));
        assert!(html_report.contains("200"));
        assert!(html_report.contains("20000")); // Total pixels

        // Test Markdown format
        assert!(add_image_info(&mut md_report, 100, 200, ReportFormat::Markdown).is_ok());
        assert!(md_report.contains("## Image Information"));
        assert!(md_report.contains("| Width | 100 |"));

        // Test Text format
        assert!(add_image_info(&mut text_report, 100, 200, ReportFormat::Text).is_ok());
        assert!(text_report.contains("Width:        100"));
        assert!(text_report.contains("Height:       200"));
        assert!(text_report.contains("Aspect Ratio: 0.500"));
    }

    #[test]
    fn test_add_basic_statistics() {
        let image =
            Array2::from_shape_vec((2, 2), vec![1.0, 2.0, 3.0, 4.0]).expect("Operation failed");
        let mut report = String::new();

        let result = add_basic_statistics(&mut report, &image.view(), ReportFormat::Text);
        assert!(result.is_ok());

        assert!(report.contains("Basic Statistics"));
        assert!(report.contains("Mean:              2.500000")); // Mean of [1,2,3,4]
        assert!(report.contains("Minimum:           1.000000"));
        assert!(report.contains("Maximum:           4.000000"));
        assert!(report.contains("Range:             3.000000"));
    }
}
