"""Tests for reporting/report_data.py — ReportData builder."""

from __future__ import annotations

from complyform.core.assessor.control_matcher import Finding
from complyform.core.profiles.models import (
    FrameworkMeta,
    FrameworkRegistry,
)
from complyform.reporting.report_data import (
    CrossValidationSection,
    build_report_data,
)


def _finding(
    *,
    control_id: str = "CC6.1",
    framework: str = "soc2",
    resource_address: str = "google_sql_database_instance.main",
    resource_type: str = "google_sql_database_instance",
    status: str = "FAIL",
    severity: str = "HIGH",
    finding: str = "SSL not enforced",
    remediation: str = "Enable SSL",
    checkov_id: str | None = "CKV_GCP_6",
    cross_mappings: list[dict[str, str]] | None = None,
    native_ids: dict[str, str] | None = None,
    confidence: str = "verified",
    not_assessed_reason: str | None = None,
) -> Finding:
    return Finding(
        control_id=control_id,
        framework=framework,
        resource_address=resource_address,
        resource_type=resource_type,
        status=status,
        severity=severity,
        finding=finding,
        remediation=remediation,
        checkov_id=checkov_id,
        cross_mappings=cross_mappings,
        native_ids=native_ids,
        confidence=confidence,
        not_assessed_reason=not_assessed_reason,
    )


def _registry() -> FrameworkRegistry:
    return FrameworkRegistry(
        schema_version=1,
        frameworks=[
            FrameworkMeta(
                id="soc2",
                name="SOC 2 Type II",
                version="2017",
                authority="AICPA",
                tier="community",
                cloud="all",
                category="global",
                control_count=64,
            ),
            FrameworkMeta(
                id="hipaa",
                name="HIPAA",
                version="2013",
                authority="HHS",
                tier="pro",
                cloud="all",
                category="industry",
                control_count=75,
            ),
        ],
    )


def _metadata(**overrides: object) -> dict[str, object]:
    defaults: dict[str, object] = {
        "cloud": "gcp",
        "scan_source": "state",
        "state_hash": "sha256:abc123",
        "frameworks": ["soc2"],
        "generated_at": "2026-03-16T00:00:00+00:00",
        "resource_count": 10,
        "assessment_duration_ms": 500,
        "project_id": None,
    }
    defaults.update(overrides)
    return defaults


class TestBuildReportData:
    """Tests for build_report_data function."""

    def test_basic(self) -> None:
        """PASS findings excluded by default, sorted severity DESC, score correct."""
        findings = [
            _finding(status="FAIL", severity="HIGH", control_id="CC6.1"),
            _finding(status="FAIL", severity="CRITICAL", control_id="CC7.1"),
            _finding(status="MANUAL_REVIEW", severity="MEDIUM", control_id="CC8.1"),
            _finding(status="PASS", severity="LOW", control_id="CC1.1"),
            _finding(status="PASS", severity="LOW", control_id="CC1.2"),
        ]
        rd = build_report_data(findings, None, _registry(), "pro", _metadata())
        # PASS excluded
        assert len(rd.findings) == 3
        # Sorted severity DESC
        assert rd.findings[0].severity == "CRITICAL"
        assert rd.findings[1].severity == "HIGH"
        assert rd.findings[2].severity == "MEDIUM"
        # Score computed: total_weight = 10+5+2+1+1 = 19, failed = 10+5 = 15
        expected_score = round(((19 - 15) / 19) * 100, 1)
        assert rd.summary.score == expected_score

    def test_include_passing(self) -> None:
        """With include_passing=True, all 5 findings present."""
        findings = [
            _finding(status="FAIL"),
            _finding(status="FAIL", control_id="CC7.1"),
            _finding(status="MANUAL_REVIEW", control_id="CC8.1", severity="MEDIUM"),
            _finding(status="PASS", control_id="CC1.1", severity="LOW"),
            _finding(status="PASS", control_id="CC1.2", severity="LOW"),
        ]
        rd = build_report_data(
            findings,
            None,
            _registry(),
            "pro",
            _metadata(include_passing=True),
        )
        assert len(rd.findings) == 5

    def test_severity_filter(self) -> None:
        """Only CRITICAL findings when severity metadata is used."""
        findings = [
            _finding(status="FAIL", severity="CRITICAL", control_id="C1"),
            _finding(status="FAIL", severity="HIGH", control_id="C2"),
            _finding(status="FAIL", severity="MEDIUM", control_id="C3"),
        ]
        # Severity filter is applied at the CLI level, not in build_report_data.
        # build_report_data sees pre-filtered findings.
        crit_only = [f for f in findings if f.severity == "CRITICAL"]
        rd = build_report_data(crit_only, None, _registry(), "pro", _metadata())
        assert len(rd.findings) == 1
        assert rd.findings[0].severity == "CRITICAL"

    def test_multi_framework(self) -> None:
        """2 frameworks: overlap_matrix populated, frameworks_detail has 2 entries."""
        findings = [
            _finding(status="FAIL", framework="soc2", control_id="CC6.1"),
            _finding(status="FAIL", framework="hipaa", control_id="164.312"),
        ]
        rd = build_report_data(
            findings,
            None,
            _registry(),
            "pro",
            _metadata(frameworks=["soc2", "hipaa"]),
        )
        assert rd.cross_mappings.overlap_matrix is not None
        assert len(rd.frameworks_detail) == 2

    def test_single_framework_no_overlap(self) -> None:
        """1 framework: overlap_matrix is None."""
        findings = [
            _finding(status="FAIL", framework="soc2"),
        ]
        rd = build_report_data(findings, None, _registry(), "pro", _metadata())
        assert rd.cross_mappings.overlap_matrix is None

    def test_upgrade_cta_pro(self) -> None:
        """Pro tier: upgrade_cta is not None, contains pricing URL."""
        rd = build_report_data([], None, None, "pro", _metadata())
        assert rd.upgrade_cta is not None
        assert "pricing" in rd.upgrade_cta["url"]

    def test_upgrade_cta_team(self) -> None:
        """Team tier: upgrade_cta is None."""
        rd = build_report_data([], None, None, "team", _metadata())
        assert rd.upgrade_cta is None

    def test_upgrade_cta_agency(self) -> None:
        """Agency tier: upgrade_cta is None."""
        rd = build_report_data([], None, None, "agency", _metadata())
        assert rd.upgrade_cta is None

    def test_empty_findings(self) -> None:
        """0 findings: score=100.0, empty findings list."""
        rd = build_report_data([], None, None, "pro", _metadata())
        assert rd.summary.score == 100.0
        assert len(rd.findings) == 0
        assert rd.summary.passed == 0
        assert rd.summary.failed == 0
        assert rd.summary.manual_review == 0

    def test_not_assessed_groups(self) -> None:
        """Resources with NOT_ASSESSED populate not_assessed_groups."""
        findings = [
            _finding(
                status="NOT_ASSESSED",
                resource_type="google_compute_instance",
                control_id="NA1",
                not_assessed_reason="no_mapping",
            ),
            _finding(
                status="NOT_ASSESSED",
                resource_type="google_compute_instance",
                control_id="NA2",
                not_assessed_reason="no_mapping",
            ),
            _finding(
                status="NOT_ASSESSED",
                resource_type="google_storage_bucket",
                control_id="NA3",
                not_assessed_reason="no_mapping",
            ),
        ]
        rd = build_report_data(findings, None, None, "pro", _metadata())
        assert len(rd.not_assessed_groups) == 2
        types = {g.resource_type for g in rd.not_assessed_groups}
        assert "google_compute_instance" in types
        assert "google_storage_bucket" in types

    def test_cross_validation_included(self) -> None:
        """cross_validation is included in ReportData regardless of tier."""
        cv = CrossValidationSection(
            score=95.0,
            confirmed=40,
            complyform_only=3,
            native_only=2,
            native_tool="AWS Security Hub",
        )
        rd = build_report_data([], None, None, "pro", _metadata(), cross_validation=cv)
        assert rd.cross_validation is not None
        assert rd.cross_validation.score == 95.0

        rd_team = build_report_data(
            [], None, None, "team", _metadata(), cross_validation=cv
        )
        assert rd_team.cross_validation is not None
