"""Tests for CLI output formatting."""

import re

import pytest

from deepsweep.models import FileResult, Finding, Severity, ValidationResult
from deepsweep.output import OutputConfig, OutputFormatter, supports_color


@pytest.fixture
def formatter():
    """Create a no-color formatter for consistent testing."""
    config = OutputConfig(use_color=False)
    return OutputFormatter(config)


@pytest.fixture
def sample_result():
    """Create a sample validation result."""
    finding = Finding(
        severity=Severity.HIGH,
        file_path=".cursorrules",
        line=5,
        message="Prompt injection detected",
        pattern_id="CURSOR-RULES-001",
        cve="CVE-2025-43570",
        remediation="Remove instruction override patterns",
    )
    file_result = FileResult(
        path=".cursorrules",
        findings=(finding,),
    )
    return ValidationResult(
        files=(file_result,),
        pattern_count=20,
    )


class TestNoEmojis:
    """Verify no emojis appear anywhere in output."""

    EMOJI_PATTERN = re.compile(
        "["
        "\U0001f600-\U0001f64f"
        "\U0001f300-\U0001f5ff"
        "\U0001f680-\U0001f6ff"
        "\U0001f1e0-\U0001f1ff"
        "\U00002702-\U000027b0"
        "]+"
    )

    def test_header_no_emojis(self, formatter: OutputFormatter):
        header = formatter.format_header("1.0.1")
        assert not self.EMOJI_PATTERN.search(header)

    def test_summary_no_emojis(self, formatter: OutputFormatter, sample_result: ValidationResult):
        summary = formatter.format_summary(sample_result)
        assert not self.EMOJI_PATTERN.search(summary)

    def test_finding_no_emojis(self, formatter: OutputFormatter):
        finding = Finding(
            severity=Severity.CRITICAL,
            file_path="test.txt",
            line=1,
            message="Test finding",
            pattern_id="TEST-001",
            remediation="Fix it",
        )
        output = formatter.format_finding(finding)
        assert not self.EMOJI_PATTERN.search(output)


class TestOptimisticMessaging:
    """Verify optimistic (Wiz-style) messaging."""

    def test_uses_items_to_review(
        self, formatter: OutputFormatter, sample_result: ValidationResult
    ):
        summary = formatter.format_summary(sample_result)

        assert "vulnerabilities" not in summary.lower()
        assert "item" in summary.lower()

    def test_uses_how_to_address(self, formatter: OutputFormatter):
        # With verbose=False (default), uses Plain English with "Fix:"
        finding = Finding(
            severity=Severity.HIGH,
            file_path="test.txt",
            line=1,
            message="Test",
            pattern_id="TEST-001",
            remediation="Do the thing",
        )
        output = formatter.format_finding(finding)

        # Plain English mode uses "Fix:" instead of "How to address:"
        assert "Fix:" in output or "Why it matters:" in output
        assert "Fix this vulnerability" not in output

    def test_encouraging_next_steps(
        self, formatter: OutputFormatter, sample_result: ValidationResult
    ):
        next_steps = formatter.format_next_steps(sample_result)

        # Should be encouraging, not scary
        # "fixes" is acceptable in optimistic context like "quick fixes"
        assert "critical failure" not in next_steps.lower()
        assert "vulnerability" not in next_steps.lower()
        # Should have encouraging or informative language
        assert any(word in next_steps.lower() for word in ["fix", "found", "item", "grade", "validated"])


class TestASCIISymbols:
    """Verify ASCII symbols are used."""

    def test_pass_symbol(self, formatter: OutputFormatter):
        output = formatter.format_file_pass("test.txt")
        assert "[PASS]" in output

    def test_fail_symbol(self, formatter: OutputFormatter):
        # In default (Plain English) mode, shows severity label
        # In verbose mode, shows [FAIL] symbol
        finding = Finding(
            severity=Severity.CRITICAL,
            file_path="test.txt",
            line=1,
            message="Test",
            pattern_id="TEST-001",
        )
        output = formatter.format_finding(finding)
        # Plain English mode shows [CRITICAL] instead of [FAIL]
        assert "[CRITICAL]" in output or "[FAIL]" in output

    def test_warn_symbol(self, formatter: OutputFormatter):
        # In default (Plain English) mode, shows severity label
        # In verbose mode, shows [WARN] symbol
        finding = Finding(
            severity=Severity.MEDIUM,
            file_path="test.txt",
            line=1,
            message="Test",
            pattern_id="TEST-001",
        )
        output = formatter.format_finding(finding)
        # Plain English mode shows [MEDIUM] instead of [WARN]
        assert "[MEDIUM]" in output or "[WARN]" in output


class TestNoColorSupport:
    """Verify NO_COLOR environment variable support."""

    def test_no_color_env_disables_color(self, monkeypatch):
        monkeypatch.setenv("NO_COLOR", "1")
        assert not supports_color()

    def test_deepsweep_no_color_env(self, monkeypatch):
        monkeypatch.setenv("DEEPSWEEP_NO_COLOR", "1")
        assert not supports_color()

    def test_no_ansi_when_color_disabled(self, formatter: OutputFormatter):
        output = formatter.format_file_pass("test.txt")
        assert "\033[" not in output


class TestPlainEnglishDefault:
    """Verify Plain English is the default output mode (v1.0.1 requirement)."""

    def test_default_output_is_plain_english(self):
        """Default output must NOT contain technical jargon."""
        config = OutputConfig(use_color=False, verbose=False)
        formatter = OutputFormatter(config)

        finding = Finding(
            severity=Severity.CRITICAL,
            file_path=".cursorrules",
            line=5,
            message="Instruction Override",
            pattern_id="CURSOR-RULES-001",
            remediation="Remove instruction override patterns",
        )

        output = formatter.format_finding(finding)

        # Should NOT have technical details in default mode
        assert "Pattern:" not in output
        assert "CVE" not in output
        assert "OWASP" not in output

        # Should have Plain English
        assert "tricked" in output.lower() or "ignore" in output.lower()
        assert "Why it matters:" in output
        assert "[Copy this to fix it" in output

    def test_verbose_includes_technical_details(self):
        """--verbose flag enables technical output."""
        config = OutputConfig(use_color=False, verbose=True)
        formatter = OutputFormatter(config)

        finding = Finding(
            severity=Severity.HIGH,
            file_path=".cursorrules",
            line=10,
            message="Role Hijacking",
            pattern_id="CURSOR-RULES-002",
            cve="CVE-2025-43571",
            owasp="A01:2021",
            remediation="Remove role override",
        )

        output = formatter.format_finding(finding)

        # Should have technical details in verbose mode
        assert "Pattern: CURSOR-RULES-002" in output
        assert "CVE: CVE-2025-43571" in output
        assert "OWASP: A01:2021" in output
        assert "Severity:" in output

    def test_banner_uses_pmf_messaging(self):
        """Banner must use PMF-validated messaging."""
        config = OutputConfig(use_color=False)
        formatter = OutputFormatter(config)

        header = formatter.format_header("1.0.1")

        assert "You don't need to understand the code to secure it." in header
        assert "Security Gateway" not in header
        assert "Know before you ship" not in header

    def test_fix_prompts_are_copy_paste_ready(self):
        """Fix prompts must be ready for AI assistant."""
        config = OutputConfig(use_color=False, verbose=False)
        formatter = OutputFormatter(config)

        finding = Finding(
            severity=Severity.HIGH,
            file_path=".cursorrules",
            line=15,
            message="Data Exfiltration",
            pattern_id="CURSOR-RULES-003",
        )

        output = formatter.format_finding(finding)

        assert "[Copy this to fix it with your AI assistant:]" in output
        # Should have a quoted prompt
        assert '"' in output

    def test_grade_a_message(self):
        """Grade A shows confidence message."""
        config = OutputConfig(use_color=False)
        formatter = OutputFormatter(config)

        file_result = FileResult(path=".cursorrules", findings=())
        result = ValidationResult(files=(file_result,), pattern_count=20)

        summary = formatter.format_summary(result)

        assert "Ship with confidence" in summary or "secure" in summary.lower()
        assert "Grade: A" in summary

    def test_json_output_includes_plain_english(self):
        """JSON output must include plain_english field."""
        import json

        config = OutputConfig(use_color=False)
        formatter = OutputFormatter(config)

        finding = Finding(
            severity=Severity.CRITICAL,
            file_path=".cursorrules",
            line=5,
            message="Instruction Override",
            pattern_id="CURSOR-RULES-001",
        )
        file_result = FileResult(path=".cursorrules", findings=(finding,))
        result = ValidationResult(files=(file_result,), pattern_count=20)

        json_output = formatter.format_json_output(result)
        data = json.loads(json_output)

        assert "findings" in data
        assert len(data["findings"]) == 1

        finding_data = data["findings"][0]
        assert "plain_english" in finding_data
        assert "why_it_matters" in finding_data
        assert "fix_prompt" in finding_data

        # Verify it's actual Plain English, not technical
        assert "tricked" in finding_data["plain_english"].lower() or "ignore" in finding_data[
            "plain_english"
        ].lower()

    def test_grade_system_contextual_messages(self):
        """Grade system provides contextual messages for each grade."""
        config = OutputConfig(use_color=False)
        formatter = OutputFormatter(config)

        # Test with multiple HIGH findings to get Grade B
        findings = [
            Finding(
                severity=Severity.HIGH,
                file_path="test.txt",
                line=i,
                message=f"Test {i}",
                pattern_id="TEST-001",
            )
            for i in range(1, 4)  # 3 HIGH findings
        ]
        file_result = FileResult(path="test.txt", findings=tuple(findings))
        result = ValidationResult(files=(file_result,), pattern_count=20)

        summary = formatter.format_summary(result)

        # Should have encouraging message
        assert "Grade:" in summary
        # With 3 HIGH findings, should get Grade B or lower
        assert any(word in summary.lower() for word in ["item", "attention", "ship", "secure"])

    def test_no_forbidden_messaging_in_output(self):
        """Verify no forbidden messaging appears in any output."""
        config = OutputConfig(use_color=False)
        formatter = OutputFormatter(config)

        finding = Finding(
            severity=Severity.HIGH,
            file_path="test.txt",
            line=1,
            message="Test",
            pattern_id="CURSOR-RULES-001",
        )
        file_result = FileResult(path="test.txt", findings=(finding,))
        result = ValidationResult(files=(file_result,), pattern_count=20)

        header = formatter.format_header("1.0.1")
        finding_output = formatter.format_finding(finding)
        summary = formatter.format_summary(result)
        next_steps = formatter.format_next_steps(result)

        all_output = header + finding_output + summary + next_steps

        # Forbidden messaging
        assert "Security Gateway for AI Coding Assistants" not in all_output
        assert "Know before you ship" not in all_output
