"""Tests for rich stdout reporter."""

from __future__ import annotations

from razin.model import Evidence, Finding, ScanResult
from razin.reporting.stdout import _GREEN, _RED, _RESET, _YELLOW, StdoutReporter
from razin.types import Severity


def _make_finding(
    *,
    skill: str = "test-skill",
    rule_id: str = "NET_RAW_IP",
    score: int = 80,
    severity: Severity = "high",
    finding_id: str = "abc123",
) -> Finding:
    return Finding(
        id=finding_id,
        severity=severity,
        score=score,
        confidence="high",
        title=f"Test {rule_id}",
        description="Test description",
        evidence=Evidence(path="SKILL.md", line=1, snippet="test"),
        skill=skill,
        rule_id=rule_id,
        recommendation="Fix it",
    )


def _make_result(
    *,
    scanned_files: int = 5,
    total_findings: int = 10,
    aggregate_score: int = 75,
    aggregate_severity: Severity = "high",
    counts: dict[Severity, int] | None = None,
    findings: list[Finding] | None = None,
    duration: float = 1.234,
    cache_hits: int = 2,
    cache_misses: int = 3,
) -> ScanResult:
    resolved_counts: dict[Severity, int] = {"high": 3, "medium": 4, "low": 3} if counts is None else counts

    return ScanResult(
        scanned_files=scanned_files,
        total_findings=total_findings,
        aggregate_score=aggregate_score,
        aggregate_severity=aggregate_severity,
        counts_by_severity=resolved_counts,
        findings=findings or [],
        duration_seconds=duration,
        warnings=[],
        cache_hits=cache_hits,
        cache_misses=cache_misses,
    )


class TestStdoutReporterHeader:
    def test_header_contains_summary_title(self) -> None:
        result = _make_result()
        output = StdoutReporter(result, color=False).render()
        assert "Raisin Scan Summary" in output

    def test_header_shows_risk_score(self) -> None:
        result = _make_result(aggregate_score=94, aggregate_severity="high")
        output = StdoutReporter(result, color=False).render()
        assert "Risk Score" in output
        assert "94" in output
        assert "high" in output

    def test_header_shows_file_count(self) -> None:
        result = _make_result(scanned_files=42)
        output = StdoutReporter(result, color=False).render()
        assert "42" in output

    def test_header_shows_finding_count(self) -> None:
        result = _make_result(total_findings=15)
        output = StdoutReporter(result, color=False).render()
        assert "15" in output

    def test_header_shows_duration(self) -> None:
        result = _make_result(duration=2.567)
        output = StdoutReporter(result, color=False).render()
        assert "2.567s" in output

    def test_header_shows_inline_severity_breakdown(self) -> None:
        result = _make_result(counts={"high": 1, "medium": 26, "low": 4})
        output = StdoutReporter(result, color=False).render()
        assert "1 high" in output
        assert "26 medium" in output
        assert "4 low" in output

    def test_cache_hidden_by_default(self) -> None:
        result = _make_result(cache_hits=10, cache_misses=5)
        output = StdoutReporter(result, color=False).render()
        assert "hits" not in output

    def test_cache_shown_in_verbose(self) -> None:
        result = _make_result(cache_hits=10, cache_misses=5)
        output = StdoutReporter(result, color=False, verbose=True).render()
        assert "10" in output
        assert "hits" in output
        assert "5" in output
        assert "misses" in output

    def test_header_has_separator_line(self) -> None:
        result = _make_result()
        output = StdoutReporter(result, color=False).render()
        assert "\u2500" in output


class TestStdoutReporterFindingsTable:
    def test_findings_table_has_borders(self) -> None:
        findings = [
            _make_finding(skill="evil-skill", rule_id="SECRET_REF", score=90),
        ]
        result = _make_result(findings=findings)
        output = StdoutReporter(result, color=False).render()
        assert "┌" in output
        assert "┬" in output
        assert "┼" in output
        assert "┴" in output
        assert "└" in output
        assert "│" in output

    def test_findings_table_shows_all_findings(self) -> None:
        all_findings = [
            _make_finding(skill="evil-skill", rule_id="SECRET_REF", score=90),
            _make_finding(skill="risky-skill", rule_id="NET_RAW_IP", score=80, finding_id="def456"),
        ]
        result = _make_result(findings=all_findings)
        output = StdoutReporter(result, color=False).render()
        assert "Findings" in output
        assert "evil-skill" in output
        assert "SECRET_REF" in output
        assert "90" in output

    def test_findings_table_hidden_when_empty(self) -> None:
        result = _make_result(findings=[])
        output = StdoutReporter(result, color=False).render()
        # The section title line; "Findings: N" in the header is separate
        assert "\n  Findings\n" not in output


class TestStdoutReporterColors:
    def test_high_severity_colored_red(self) -> None:
        f = [_make_finding(severity="high", score=80)]
        result = _make_result(findings=f)
        output = StdoutReporter(result, color=True).render()
        assert _RED in output
        assert _RESET in output

    def test_medium_severity_colored_yellow(self) -> None:
        f = [_make_finding(severity="medium", score=55, finding_id="med1")]
        result = _make_result(findings=f)
        output = StdoutReporter(result, color=True).render()
        assert _YELLOW in output

    def test_low_severity_colored_green(self) -> None:
        f = [_make_finding(severity="low", score=20, finding_id="low1")]
        result = _make_result(findings=f)
        output = StdoutReporter(result, color=True).render()
        assert _GREEN in output

    def test_high_score_colored_red(self) -> None:
        f = [_make_finding(score=85)]
        result = _make_result(findings=f)
        output = StdoutReporter(result, color=True).render()
        assert f"{_RED}85{_RESET}" in output

    def test_medium_score_colored_yellow(self) -> None:
        f = [_make_finding(score=50, severity="medium", finding_id="m1")]
        result = _make_result(findings=f)
        output = StdoutReporter(result, color=True).render()
        assert f"{_YELLOW}50{_RESET}" in output

    def test_low_score_colored_green(self) -> None:
        f = [_make_finding(score=20, severity="low", finding_id="l1")]
        result = _make_result(findings=f)
        output = StdoutReporter(result, color=True).render()
        assert f"{_GREEN}20{_RESET}" in output

    def test_color_disabled_produces_no_ansi(self) -> None:
        f = [_make_finding(score=85)]
        result = _make_result(findings=f, counts={"high": 1, "medium": 0, "low": 0})
        output = StdoutReporter(result, color=False).render()
        assert "\033[" not in output

    def test_risk_score_colored_in_header(self) -> None:
        result = _make_result(aggregate_score=94, aggregate_severity="high")
        output = StdoutReporter(result, color=True).render()
        assert _RED in output


class TestStdoutReporterDeterminism:
    def test_render_is_deterministic(self) -> None:
        """Same input always produces same output."""
        findings = [
            _make_finding(skill="a-skill", rule_id="NET_RAW_IP", score=80, finding_id="aaa"),
            _make_finding(skill="b-skill", rule_id="SECRET_REF", score=70, finding_id="bbb"),
        ]
        result = _make_result(
            findings=findings,
            duration=1.000,
        )

        first = StdoutReporter(result).render()
        second = StdoutReporter(result).render()
        assert first == second


class TestStdoutReporterGolden:
    def test_golden_snapshot_no_color(self) -> None:
        """Golden snapshot test for stable formatting (no color)."""
        findings = [
            _make_finding(skill="risky-skill", rule_id="NET_RAW_IP", score=85, finding_id="f01"),
            _make_finding(skill="risky-skill", rule_id="SECRET_REF", score=75, finding_id="f02"),
        ]
        result = ScanResult(
            scanned_files=3,
            total_findings=6,
            aggregate_score=94,
            aggregate_severity="high",
            counts_by_severity={"high": 2, "medium": 2, "low": 2},
            findings=findings,
            duration_seconds=0.500,
            warnings=[],
            cache_hits=1,
            cache_misses=2,
        )

        output = StdoutReporter(result, color=False).render()

        # Verify header content
        assert "Raisin Scan Summary" in output
        assert "─" in output  # separator line
        assert "Risk Score" in output
        assert "94" in output
        assert "high" in output
        assert "3" in output  # Files
        assert "6" in output  # Findings
        assert "0.500s" in output
        assert "2 high" in output
        assert "2 medium" in output
        assert "2 low" in output

        # Cache hidden by default
        assert "hits" not in output

        # Verbose shows cache
        verbose_output = StdoutReporter(result, color=False, verbose=True).render()
        assert "hits" in verbose_output

        # Findings table present
        assert "Findings" in output
        assert "risky-skill" in output
        assert "NET_RAW_IP" in output

        # Verify table borders with proper intersections
        assert "┌" in output
        assert "┬" in output
        assert "│" in output
        assert "┼" in output
        assert "┴" in output
        assert "└" in output

        # Verify removed sections are absent
        assert "Per-Skill Findings" not in output
        assert "Rule Distribution" not in output
        assert "█" not in output  # no histogram bars

        # No ANSI escapes in no-color mode
        assert "\033[" not in output
