"""Tests for Markdown export functionality."""

import pytest
from pathlib import Path

from xai_suite.utils.markdown_exporter import (
    MarkdownExporter,
    MarkdownExportConfig,
    export_to_markdown,
)
from xai_suite.utils.visualization import save_local_explanation
import xai_suite


class TestMarkdownExportConfig:
    """Tests for MarkdownExportConfig dataclass."""

    def test_default_values(self):
        """Test that config has sensible defaults."""
        config = MarkdownExportConfig(output_path="report.md")

        assert config.output_path == "report.md"
        assert config.title == "XAI Explanation Report"
        assert config.top_n_features is None
        assert config.image_format == "svg"
        assert config.include_shap is True
        assert config.include_lime is True
        assert config.include_narrative is True
        assert config.include_insights is True
        assert config.include_compliance is True
        assert config.figure_dpi == 100

    def test_custom_values(self):
        """Test config with custom values."""
        config = MarkdownExportConfig(
            output_path="/path/to/report.md",
            title="Custom Report",
            top_n_features=5,
            image_format="png",
            include_shap=False,
            figure_dpi=150,
        )

        assert config.output_path == "/path/to/report.md"
        assert config.title == "Custom Report"
        assert config.top_n_features == 5
        assert config.image_format == "png"
        assert config.include_shap is False
        assert config.figure_dpi == 150


class TestSaveLocalExplanation:
    """Tests for save_local_explanation function."""

    def test_saves_svg_file(self, sample_shap_explanation, tmp_path):
        """Test saving explanation as SVG."""
        output_path = tmp_path / "explanation.svg"
        result = save_local_explanation(sample_shap_explanation, str(output_path))

        assert Path(result).exists()
        assert result == str(output_path)

    def test_saves_png_file(self, sample_shap_explanation, tmp_path):
        """Test saving explanation as PNG."""
        output_path = tmp_path / "explanation.png"
        result = save_local_explanation(sample_shap_explanation, str(output_path), dpi=100)

        assert Path(result).exists()

    def test_top_n_filter(self, tmp_path):
        """Test top_n parameter filters features."""
        explanation = {
            "method": "SHAP",
            "feature_names": ["f1", "f2", "f3", "f4", "f5"],
            "importance_scores": [0.1, 0.5, 0.2, 0.8, 0.3],
        }
        output_path = tmp_path / "explanation.svg"
        result = save_local_explanation(explanation, str(output_path), top_n=3)

        assert Path(result).exists()


class TestMarkdownExporter:
    """Tests for MarkdownExporter class."""

    @pytest.fixture
    def full_result(self, sample_shap_explanation, sample_lime_explanation):
        """Create a full result with all sections."""
        return {
            "shap": sample_shap_explanation,
            "lime": sample_lime_explanation,
            "narrative": {
                "text": "This is a test narrative explaining the model's decision.",
                "method": "SHAP",
                "domain": "generic",
                "timestamp": "2026-02-04T10:30:00Z",
            },
            "insights": [
                {
                    "type": "warning",
                    "priority": "high",
                    "title": "Important Finding",
                    "description": "Feature 0 has high importance.",
                    "action": "Review feature engineering.",
                    "related_features": ["feature_0"],
                }
            ],
            "compliance": {
                "audit_id": "audit-123",
                "timestamp": "2026-02-04T10:30:00Z",
                "applicable_articles": [
                    {
                        "article": "article_13",
                        "title": "Transparency",
                        "relevance": "XAI explanations support transparency.",
                    }
                ],
            },
        }

    @pytest.fixture
    def minimal_result(self, sample_shap_explanation):
        """Create a minimal result with SHAP only."""
        return {"shap": sample_shap_explanation}

    def test_export_full_result(self, full_result, tmp_path):
        """Test exporting a full result with all sections."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path))
        exporter = MarkdownExporter(config)

        result_path = exporter.export(full_result)

        assert Path(result_path).exists()
        content = Path(result_path).read_text()

        # Check header
        assert "# XAI Explanation Report" in content
        assert f"XAI Suite Version:** {xai_suite.__version__}" in content

        # Check narrative
        assert "## Executive Summary" in content
        assert "test narrative" in content

        # Check feature analysis
        assert "## Feature Importance Analysis" in content
        assert "### SHAP Analysis" in content
        assert "### LIME Analysis" in content

        # Check insights
        assert "## Actionable Insights" in content
        assert "Important Finding" in content

        # Check compliance
        assert "## EU AI Act Compliance" in content
        assert "audit-123" in content

    def test_export_minimal_result(self, minimal_result, tmp_path):
        """Test exporting a minimal result with SHAP only."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path))
        exporter = MarkdownExporter(config)

        result_path = exporter.export(minimal_result)

        assert Path(result_path).exists()
        content = Path(result_path).read_text()

        # Check header exists
        assert "# XAI Explanation Report" in content

        # Check SHAP section exists
        assert "### SHAP Analysis" in content

        # Check no LIME section
        assert "### LIME Analysis" not in content

        # Check no narrative section
        assert "## Executive Summary" not in content

        # Check no insights section
        assert "## Actionable Insights" not in content

        # Check no compliance section
        assert "## EU AI Act Compliance" not in content

    def test_generates_images(self, full_result, tmp_path):
        """Test that images are generated alongside the markdown file."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), image_format="svg")
        exporter = MarkdownExporter(config)

        exporter.export(full_result)

        # Check SHAP image exists
        shap_image = tmp_path / "report_shap_importance.svg"
        assert shap_image.exists()

        # Check LIME image exists
        lime_image = tmp_path / "report_lime_importance.svg"
        assert lime_image.exists()

    def test_png_format(self, minimal_result, tmp_path):
        """Test PNG image format."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), image_format="png")
        exporter = MarkdownExporter(config)

        exporter.export(minimal_result)

        png_image = tmp_path / "report_shap_importance.png"
        assert png_image.exists()

    def test_exclude_shap(self, full_result, tmp_path):
        """Test excluding SHAP section."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), include_shap=False)
        exporter = MarkdownExporter(config)

        result_path = exporter.export(full_result)
        content = Path(result_path).read_text()

        assert "### SHAP Analysis" not in content
        assert "### LIME Analysis" in content

    def test_exclude_lime(self, full_result, tmp_path):
        """Test excluding LIME section."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), include_lime=False)
        exporter = MarkdownExporter(config)

        result_path = exporter.export(full_result)
        content = Path(result_path).read_text()

        assert "### SHAP Analysis" in content
        assert "### LIME Analysis" not in content

    def test_exclude_narrative(self, full_result, tmp_path):
        """Test excluding narrative section."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), include_narrative=False)
        exporter = MarkdownExporter(config)

        result_path = exporter.export(full_result)
        content = Path(result_path).read_text()

        assert "## Executive Summary" not in content

    def test_exclude_insights(self, full_result, tmp_path):
        """Test excluding insights section."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), include_insights=False)
        exporter = MarkdownExporter(config)

        result_path = exporter.export(full_result)
        content = Path(result_path).read_text()

        assert "## Actionable Insights" not in content

    def test_exclude_compliance(self, full_result, tmp_path):
        """Test excluding compliance section."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), include_compliance=False)
        exporter = MarkdownExporter(config)

        result_path = exporter.export(full_result)
        content = Path(result_path).read_text()

        assert "## EU AI Act Compliance" not in content

    def test_top_n_features_filter(self, tmp_path):
        """Test top_n_features limits table rows."""
        result = {
            "shap": {
                "method": "SHAP",
                "feature_names": ["f1", "f2", "f3", "f4", "f5"],
                "importance_scores": [0.1, 0.5, 0.2, 0.8, 0.3],
                "feature_values": [1.0, 2.0, 3.0, 4.0, 5.0],
            }
        }

        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path), top_n_features=3)
        exporter = MarkdownExporter(config)

        result_path = exporter.export(result)
        content = Path(result_path).read_text()

        # Count table rows (excluding header rows)
        lines = content.split("\n")
        table_rows = [l for l in lines if l.startswith("| f")]
        assert len(table_rows) == 3

    def test_custom_title(self, minimal_result, tmp_path):
        """Test custom report title."""
        output_path = tmp_path / "report.md"
        config = MarkdownExportConfig(
            output_path=str(output_path), title="Security Analysis Report"
        )
        exporter = MarkdownExporter(config)

        result_path = exporter.export(minimal_result)
        content = Path(result_path).read_text()

        assert "# Security Analysis Report" in content

    def test_creates_parent_directory(self, minimal_result, tmp_path):
        """Test that parent directories are created if needed."""
        output_path = tmp_path / "subdir" / "nested" / "report.md"
        config = MarkdownExportConfig(output_path=str(output_path))
        exporter = MarkdownExporter(config)

        result_path = exporter.export(minimal_result)

        assert Path(result_path).exists()


class TestExportToMarkdownFunction:
    """Tests for export_to_markdown convenience function."""

    def test_basic_export(self, sample_shap_explanation, tmp_path):
        """Test basic export with convenience function."""
        result = {"shap": sample_shap_explanation}
        output_path = tmp_path / "report.md"

        result_path = export_to_markdown(result, str(output_path))

        assert Path(result_path).exists()

    def test_with_options(self, sample_shap_explanation, tmp_path):
        """Test export with custom options."""
        result = {"shap": sample_shap_explanation}
        output_path = tmp_path / "report.md"

        result_path = export_to_markdown(
            result,
            str(output_path),
            title="Custom Title",
            top_n_features=2,
            image_format="png",
        )

        assert Path(result_path).exists()
        content = Path(result_path).read_text()
        assert "# Custom Title" in content


class TestOrchestratorExportMarkdown:
    """Tests for XAIOrchestrator.export_markdown method."""

    def test_export_via_orchestrator(
        self, trained_classifier, iris_data, sample_instance, tmp_path
    ):
        """Test exporting via orchestrator method."""
        from xai_suite import XAIOrchestrator

        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")

        result = orchestrator.explain(sample_instance)
        output_path = tmp_path / "report.md"

        result_path = orchestrator.export_markdown(result, output_path)

        assert Path(result_path).exists()
        content = Path(result_path).read_text()
        assert "### SHAP Analysis" in content

    def test_export_with_options(
        self, trained_classifier, iris_data, sample_instance, tmp_path
    ):
        """Test orchestrator export with custom options."""
        from xai_suite import XAIOrchestrator

        X, _ = iris_data
        orchestrator = XAIOrchestrator(trained_classifier, X)
        orchestrator.add_explainer("shap")

        result = orchestrator.explain(sample_instance)
        output_path = tmp_path / "report.md"

        result_path = orchestrator.export_markdown(
            result, output_path, title="My Report", top_n_features=2
        )

        assert Path(result_path).exists()
        content = Path(result_path).read_text()
        assert "# My Report" in content
