import io
from unittest.mock import Mock

import pytest
from reportlab.platypus import Image, PageBreak, Paragraph, Table
from tasks.jobs.reports import FRAMEWORK_REGISTRY, ComplianceData, RequirementData
from tasks.jobs.reports import csa as csa_report_module
from tasks.jobs.reports.csa import CSAReportGenerator
from tasks.tests.report_test_helpers import patch_chart_helpers


# Use string status values directly to avoid Django DB initialization
# These match api.models.StatusChoices values
class StatusChoices:
    """Mock StatusChoices to avoid Django DB initialization."""

    PASS = "PASS"
    FAIL = "FAIL"
    MANUAL = "MANUAL"


# =============================================================================
# Fixtures
# =============================================================================


@pytest.fixture
def csa_generator():
    """Create a CSAReportGenerator instance for testing."""
    config = FRAMEWORK_REGISTRY["csa_ccm"]
    return CSAReportGenerator(config)


@pytest.fixture
def patched_csa_charts(monkeypatch):
    return patch_chart_helpers(
        monkeypatch, csa_report_module, ("create_horizontal_bar_chart",)
    )


@pytest.fixture
def mock_csa_requirement_attribute_iam():
    """Create a mock CSA CCM requirement attribute for Identity & Access Management."""
    mock = Mock()
    mock.Section = "Identity & Access Management"
    mock.CCMLite = "Yes"
    mock.IaaS = "Yes"
    mock.PaaS = "Yes"
    mock.SaaS = "Yes"
    mock.ScopeApplicability = [
        {"ReferenceId": "ISO 27001", "Identifiers": ["A.9.1.1", "A.9.2.3"]},
        {"ReferenceId": "NIST 800-53", "Identifiers": ["AC-2", "AC-3", "AC-6"]},
    ]
    return mock


@pytest.fixture
def mock_csa_requirement_attribute_logging():
    """Create a mock CSA CCM requirement attribute for Logging and Monitoring."""
    mock = Mock()
    mock.Section = "Logging and Monitoring"
    mock.CCMLite = "Yes"
    mock.IaaS = "Yes"
    mock.PaaS = "No"
    mock.SaaS = "No"
    mock.ScopeApplicability = [
        {"ReferenceId": "ISO 27001", "Identifiers": ["A.12.4.1"]},
    ]
    return mock


@pytest.fixture
def mock_csa_requirement_attribute_crypto():
    """Create a mock CSA CCM requirement attribute for Cryptography."""
    mock = Mock()
    mock.Section = "Cryptography, Encryption & Key Management"
    mock.CCMLite = "No"
    mock.IaaS = "Yes"
    mock.PaaS = "Yes"
    mock.SaaS = "No"
    mock.ScopeApplicability = []
    return mock


@pytest.fixture
def basic_csa_compliance_data():
    """Create basic ComplianceData for CSA CCM testing."""
    return ComplianceData(
        tenant_id="tenant-123",
        scan_id="scan-456",
        provider_id="provider-789",
        compliance_id="csa_ccm_4.0",
        framework="CSA-CCM",
        name="CSA Cloud Controls Matrix v4.0",
        version="4.0",
        description="Cloud Security Alliance Cloud Controls Matrix",
    )


# =============================================================================
# Generator Initialization Tests
# =============================================================================


class TestCSAGeneratorInitialization:
    """Test suite for CSA generator initialization."""

    def test_generator_creation(self, csa_generator):
        """Test that CSA generator is created correctly."""
        assert csa_generator is not None
        assert csa_generator.config.name == "csa_ccm"
        assert csa_generator.config.language == "en"

    def test_generator_no_niveles(self, csa_generator):
        """Test that CSA config does not use niveles."""
        assert csa_generator.config.has_niveles is False

    def test_generator_no_dimensions(self, csa_generator):
        """Test that CSA config does not use dimensions."""
        assert csa_generator.config.has_dimensions is False

    def test_generator_no_risk_levels(self, csa_generator):
        """Test that CSA config does not use risk levels."""
        assert csa_generator.config.has_risk_levels is False

    def test_generator_no_weight(self, csa_generator):
        """Test that CSA config does not use weight."""
        assert csa_generator.config.has_weight is False


# =============================================================================
# Cover Page Tests
# =============================================================================


class TestCSACoverPage:
    """Test suite for CSA cover page generation."""

    def test_cover_page_has_logo(self, csa_generator, basic_csa_compliance_data):
        """Test that cover page contains the Prowler logo."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_cover_page(basic_csa_compliance_data)

        assert len(elements) > 0

    def test_cover_page_has_title(self, csa_generator, basic_csa_compliance_data):
        """Test that cover page contains the CSA CCM title."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_cover_page(basic_csa_compliance_data)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "CSA" in content or "CCM" in content or "Cloud Controls" in content

    def test_cover_page_has_metadata_table(
        self, csa_generator, basic_csa_compliance_data
    ):
        """Test that cover page contains metadata table."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_cover_page(basic_csa_compliance_data)

        tables = [e for e in elements if isinstance(e, Table)]
        assert len(tables) >= 1


# =============================================================================
# Executive Summary Tests
# =============================================================================


class TestCSAExecutiveSummary:
    """Test suite for CSA executive summary generation."""

    def test_executive_summary_has_english_title(
        self, csa_generator, basic_csa_compliance_data
    ):
        """Test that executive summary has English title."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "Executive Summary" in content

    def test_executive_summary_calculates_compliance(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test that executive summary calculates compliance percentage."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Passed requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Failed requirement",
                status=StatusChoices.FAIL,
                passed_findings=0,
                failed_findings=10,
                total_findings=10,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)

        # Should contain tables with metrics
        tables = [e for e in elements if isinstance(e, Table)]
        assert len(tables) >= 1

    def test_executive_summary_shows_all_statuses(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test that executive summary shows passed, failed, and manual counts."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Passed",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Failed",
                status=StatusChoices.FAIL,
                passed_findings=0,
                failed_findings=10,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-003",
                description="Manual",
                status=StatusChoices.MANUAL,
                passed_findings=0,
                failed_findings=0,
                total_findings=0,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-003": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)

        # Should have a summary table with all statuses
        assert len(elements) > 0

    def test_executive_summary_excludes_manual_from_percentage(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test that manual requirements are excluded from compliance percentage."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Passed",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Manual",
                status=StatusChoices.MANUAL,
                passed_findings=0,
                failed_findings=0,
                total_findings=0,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)

        # Should calculate 100% (only 1 evaluated requirement that passed)
        assert len(elements) > 0


# =============================================================================
# Charts Section Tests
# =============================================================================


class TestCSAChartsSection:
    """Test suite for CSA charts section generation."""

    def test_charts_section_has_section_chart_title(
        self, csa_generator, basic_csa_compliance_data, patched_csa_charts
    ):
        """Test that charts section has section compliance title."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_charts_section(basic_csa_compliance_data)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "Section" in content or "Compliance" in content
        assert any(isinstance(e, Image) for e in elements)
        chart_kwargs = patched_csa_charts["create_horizontal_bar_chart"][0]["kwargs"]
        assert chart_kwargs["labels"] == []
        assert chart_kwargs["values"] == []
        assert chart_kwargs["xlabel"] == "Compliance (%)"

    def test_charts_section_has_page_break(
        self, csa_generator, basic_csa_compliance_data, patched_csa_charts
    ):
        """Test that charts section has page breaks."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_charts_section(basic_csa_compliance_data)

        page_breaks = [e for e in elements if isinstance(e, PageBreak)]
        assert len(page_breaks) >= 1
        assert len(patched_csa_charts["create_horizontal_bar_chart"]) == 1

    def test_charts_section_has_section_breakdown(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
        patched_csa_charts,
    ):
        """Test that charts section includes section breakdown table."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Test requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        elements = csa_generator.create_charts_section(basic_csa_compliance_data)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "Section" in content or "Breakdown" in content
        assert any(isinstance(e, Image) for e in elements)
        chart_kwargs = patched_csa_charts["create_horizontal_bar_chart"][0]["kwargs"]
        assert chart_kwargs["labels"] == ["Identity & Access Management"]
        assert chart_kwargs["values"] == [100.0]
        assert chart_kwargs["color_func"]


# =============================================================================
# Section Chart Tests
# =============================================================================


class TestCSASectionChart:
    """Test suite for CSA section compliance chart."""

    def test_section_chart_creation(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
        patched_csa_charts,
    ):
        """Test that section chart is created successfully."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Test requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        chart_buffer = csa_generator._create_section_chart(basic_csa_compliance_data)

        assert isinstance(chart_buffer, io.BytesIO)
        assert chart_buffer.getvalue()  # Not empty
        chart_kwargs = patched_csa_charts["create_horizontal_bar_chart"][0]["kwargs"]
        assert chart_kwargs["labels"] == ["Identity & Access Management"]
        assert chart_kwargs["values"] == [100.0]
        assert chart_kwargs["xlabel"] == "Compliance (%)"

    def test_section_chart_excludes_manual(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
        patched_csa_charts,
    ):
        """Test that manual requirements are excluded from section chart."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Auto requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Manual requirement",
                status=StatusChoices.MANUAL,
                passed_findings=0,
                failed_findings=0,
                total_findings=0,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        # Should not raise any errors
        chart_buffer = csa_generator._create_section_chart(basic_csa_compliance_data)
        assert isinstance(chart_buffer, io.BytesIO)
        chart_kwargs = patched_csa_charts["create_horizontal_bar_chart"][0]["kwargs"]
        assert chart_kwargs["labels"] == ["Identity & Access Management"]
        assert chart_kwargs["values"] == [100.0]

    def test_section_chart_multiple_sections(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
        mock_csa_requirement_attribute_logging,
        mock_csa_requirement_attribute_crypto,
        patched_csa_charts,
    ):
        """Test section chart with multiple sections."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="IAM requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Logging requirement",
                status=StatusChoices.FAIL,
                passed_findings=0,
                failed_findings=10,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-003",
                description="Crypto requirement",
                status=StatusChoices.PASS,
                passed_findings=5,
                failed_findings=0,
                total_findings=5,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {
                    "req_attributes": [mock_csa_requirement_attribute_logging]
                }
            },
            "REQ-003": {
                "attributes": {
                    "req_attributes": [mock_csa_requirement_attribute_crypto]
                }
            },
        }

        chart_buffer = csa_generator._create_section_chart(basic_csa_compliance_data)
        assert isinstance(chart_buffer, io.BytesIO)
        chart_kwargs = patched_csa_charts["create_horizontal_bar_chart"][0]["kwargs"]
        assert chart_kwargs["labels"] == [
            "Cryptography & Encryption",
            "Identity & Access Management",
            "Logging and Monitoring",
        ]
        assert chart_kwargs["values"] == [100.0, 100.0, 0.0]


# =============================================================================
# Section Table Tests
# =============================================================================


class TestCSASectionTable:
    """Test suite for CSA section breakdown table."""

    def test_section_table_creation(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test that section table is created successfully."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Test requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        table = csa_generator._create_section_table(basic_csa_compliance_data)

        assert isinstance(table, Table)

    def test_section_table_counts_statuses(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test that section table counts passed, failed, and manual."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Passed",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Failed",
                status=StatusChoices.FAIL,
                passed_findings=0,
                failed_findings=10,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-003",
                description="Manual",
                status=StatusChoices.MANUAL,
                passed_findings=0,
                failed_findings=0,
                total_findings=0,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-003": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        table = csa_generator._create_section_table(basic_csa_compliance_data)
        assert isinstance(table, Table)


# =============================================================================
# Requirements Index Tests
# =============================================================================


class TestCSARequirementsIndex:
    """Test suite for CSA requirements index generation."""

    def test_requirements_index_has_title(
        self, csa_generator, basic_csa_compliance_data
    ):
        """Test that requirements index has English title."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_requirements_index(basic_csa_compliance_data)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "Requirements Index" in content

    def test_requirements_index_organized_by_section(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
        mock_csa_requirement_attribute_logging,
    ):
        """Test that requirements index is organized by section."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="IAM requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Logging requirement",
                status=StatusChoices.PASS,
                passed_findings=5,
                failed_findings=0,
                total_findings=5,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {
                    "req_attributes": [mock_csa_requirement_attribute_logging]
                }
            },
        }

        elements = csa_generator.create_requirements_index(basic_csa_compliance_data)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        # Should have section headers
        assert "Identity" in content or "Logging" in content or "Access" in content

    def test_requirements_index_shows_status_indicators(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test that requirements index shows pass/fail/manual indicators."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="Passed requirement",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Failed requirement",
                status=StatusChoices.FAIL,
                passed_findings=0,
                failed_findings=10,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-003",
                description="Manual requirement",
                status=StatusChoices.MANUAL,
                passed_findings=0,
                failed_findings=0,
                total_findings=0,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-003": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        elements = csa_generator.create_requirements_index(basic_csa_compliance_data)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        # Should have status indicators
        assert "\u2713" in content or "\u2717" in content or "\u2299" in content

    def test_requirements_index_truncates_long_descriptions(
        self, csa_generator, basic_csa_compliance_data
    ):
        """Test that long descriptions are truncated."""
        mock_attr = Mock()
        mock_attr.Section = "Identity & Access Management"
        mock_attr.CCMLite = "Yes"
        mock_attr.IaaS = "Yes"
        mock_attr.PaaS = "Yes"
        mock_attr.SaaS = "Yes"
        mock_attr.ScopeApplicability = []

        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="A" * 100,
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {"attributes": {"req_attributes": [mock_attr]}},
        }

        # Should not raise errors
        elements = csa_generator.create_requirements_index(basic_csa_compliance_data)
        assert len(elements) > 0


# =============================================================================
# Requirement Attributes Tests
# =============================================================================


class TestCSARequirementAttributes:
    """Test suite for CSA requirement attributes display."""

    def test_format_attributes_applicability_line(
        self, csa_generator, mock_csa_requirement_attribute_iam
    ):
        """Test that applicability attributes (CCMLite, IaaS, PaaS, SaaS) are rendered."""
        elements = csa_generator._format_requirement_attributes(
            mock_csa_requirement_attribute_iam
        )

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "CCMLite: Yes" in content
        assert "IaaS: Yes" in content
        assert "PaaS: Yes" in content
        assert "SaaS: Yes" in content

    def test_format_attributes_partial_applicability(
        self, csa_generator, mock_csa_requirement_attribute_logging
    ):
        """Test attributes when some applicability fields are 'No'."""
        elements = csa_generator._format_requirement_attributes(
            mock_csa_requirement_attribute_logging
        )

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "IaaS: Yes" in content
        assert "PaaS: No" in content

    def test_format_attributes_scope_applicability_refs(
        self, csa_generator, mock_csa_requirement_attribute_iam
    ):
        """Test that ScopeApplicability references are displayed."""
        elements = csa_generator._format_requirement_attributes(
            mock_csa_requirement_attribute_iam
        )

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "ISO 27001" in content
        assert "NIST 800-53" in content

    def test_format_attributes_empty_scope(
        self, csa_generator, mock_csa_requirement_attribute_crypto
    ):
        """Test that empty ScopeApplicability produces no reference line."""
        elements = csa_generator._format_requirement_attributes(
            mock_csa_requirement_attribute_crypto
        )

        # Should have applicability line but no scope reference line
        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        assert len(paragraphs) == 1  # Only the applicability line

    def test_format_attributes_no_applicability(self, csa_generator):
        """Test attributes when all applicability fields are empty."""
        mock = Mock()
        mock.CCMLite = ""
        mock.IaaS = ""
        mock.PaaS = ""
        mock.SaaS = ""
        mock.ScopeApplicability = []

        elements = csa_generator._format_requirement_attributes(mock)

        assert len(elements) == 0

    def test_format_attributes_truncates_long_identifiers(self, csa_generator):
        """Test that ScopeApplicability with many identifiers is truncated."""
        mock = Mock()
        mock.CCMLite = "Yes"
        mock.IaaS = "Yes"
        mock.PaaS = "Yes"
        mock.SaaS = "Yes"
        mock.ScopeApplicability = [
            {
                "ReferenceId": "NIST 800-53",
                "Identifiers": ["AC-1", "AC-2", "AC-3", "AC-4", "AC-5", "AC-6"],
            },
        ]

        elements = csa_generator._format_requirement_attributes(mock)

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        # Should show first 4 and ellipsis
        assert "AC-1" in content
        assert "AC-4" in content
        assert "..." in content

    def test_attr_style_returns_paragraph_style(self, csa_generator):
        """Test that _attr_style returns a valid ParagraphStyle."""
        from reportlab.lib.styles import ParagraphStyle

        style = csa_generator._attr_style()
        assert isinstance(style, ParagraphStyle)
        assert style.fontSize == 10
        assert style.leftIndent == 30

    def test_render_requirement_detail_extras(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test that detail extras hook renders CSA attributes."""
        req = RequirementData(
            id="REQ-001",
            description="IAM requirement",
            status=StatusChoices.PASS,
            passed_findings=10,
            failed_findings=0,
            total_findings=10,
        )
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
        }

        elements = csa_generator._render_requirement_detail_extras(
            req, basic_csa_compliance_data
        )

        paragraphs = [e for e in elements if isinstance(e, Paragraph)]
        content = " ".join(str(p.text) for p in paragraphs)
        assert "CCMLite" in content
        assert "ISO 27001" in content

    def test_render_requirement_detail_extras_no_metadata(
        self,
        csa_generator,
        basic_csa_compliance_data,
    ):
        """Test that detail extras returns empty when no metadata found."""
        req = RequirementData(
            id="REQ-UNKNOWN",
            description="No metadata",
            status=StatusChoices.PASS,
            passed_findings=0,
            failed_findings=0,
            total_findings=0,
        )
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator._render_requirement_detail_extras(
            req, basic_csa_compliance_data
        )

        assert elements == []


# =============================================================================
# Empty Data Tests
# =============================================================================


class TestCSAEmptyData:
    """Test suite for CSA with empty or minimal data."""

    def test_executive_summary_empty_requirements(
        self, csa_generator, basic_csa_compliance_data
    ):
        """Test executive summary with no requirements."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)

        assert len(elements) > 0

    def test_charts_section_empty_requirements(
        self, csa_generator, basic_csa_compliance_data
    ):
        """Test charts section with no requirements."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_charts_section(basic_csa_compliance_data)

        assert len(elements) > 0

    def test_requirements_index_empty(self, csa_generator, basic_csa_compliance_data):
        """Test requirements index with no requirements."""
        basic_csa_compliance_data.requirements = []
        basic_csa_compliance_data.attributes_by_requirement_id = {}

        elements = csa_generator.create_requirements_index(basic_csa_compliance_data)

        # Should at least have the title
        assert len(elements) >= 1


# =============================================================================
# All Pass / All Fail Tests
# =============================================================================


class TestCSAEdgeCases:
    """Test suite for CSA edge cases."""

    def test_all_requirements_pass(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test with all requirements passing."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id=f"REQ-{i:03d}",
                description=f"Passing requirement {i}",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            )
            for i in range(1, 6)
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            f"REQ-{i:03d}": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            }
            for i in range(1, 6)
        }

        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)
        assert len(elements) > 0

    def test_all_requirements_fail(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test with all requirements failing."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id=f"REQ-{i:03d}",
                description=f"Failing requirement {i}",
                status=StatusChoices.FAIL,
                passed_findings=0,
                failed_findings=10,
                total_findings=10,
            )
            for i in range(1, 6)
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            f"REQ-{i:03d}": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            }
            for i in range(1, 6)
        }

        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)
        assert len(elements) > 0

    def test_all_requirements_manual(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
    ):
        """Test with all requirements being manual."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id=f"REQ-{i:03d}",
                description=f"Manual requirement {i}",
                status=StatusChoices.MANUAL,
                passed_findings=0,
                failed_findings=0,
                total_findings=0,
            )
            for i in range(1, 6)
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            f"REQ-{i:03d}": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            }
            for i in range(1, 6)
        }

        # Should handle gracefully - compliance should be 100% when no evaluated
        elements = csa_generator.create_executive_summary(basic_csa_compliance_data)
        assert len(elements) > 0


# =============================================================================
# Integration Tests
# =============================================================================


class TestCSAIntegration:
    """Integration tests for CSA report generation."""

    def test_full_report_generation_flow(
        self,
        csa_generator,
        basic_csa_compliance_data,
        mock_csa_requirement_attribute_iam,
        mock_csa_requirement_attribute_logging,
    ):
        """Test the complete report generation flow."""
        basic_csa_compliance_data.requirements = [
            RequirementData(
                id="REQ-001",
                description="IAM passed",
                status=StatusChoices.PASS,
                passed_findings=10,
                failed_findings=0,
                total_findings=10,
            ),
            RequirementData(
                id="REQ-002",
                description="Logging failed",
                status=StatusChoices.FAIL,
                passed_findings=0,
                failed_findings=5,
                total_findings=5,
            ),
        ]
        basic_csa_compliance_data.attributes_by_requirement_id = {
            "REQ-001": {
                "attributes": {"req_attributes": [mock_csa_requirement_attribute_iam]}
            },
            "REQ-002": {
                "attributes": {
                    "req_attributes": [mock_csa_requirement_attribute_logging]
                }
            },
        }

        # Generate all sections
        exec_summary = csa_generator.create_executive_summary(basic_csa_compliance_data)
        charts = csa_generator.create_charts_section(basic_csa_compliance_data)
        index = csa_generator.create_requirements_index(basic_csa_compliance_data)

        # All sections should generate without errors
        assert len(exec_summary) > 0
        assert len(charts) > 0
        assert len(index) > 0
