"""Performance benchmarks for the secret scanner.

Cycle 17/25: Validates that 21 compiled regex patterns don't degrade
scan performance as content grows. Establishes baseline timings.
"""
import time

import pytest

from greymatter_core.scanner import SecretScanner, ScannerConfig


@pytest.fixture
def scanner():
    return SecretScanner()


def _time_scan(scanner: SecretScanner, text: str, iterations: int = 10) -> float:
    """Run scanner N times and return average time in seconds."""
    start = time.perf_counter()
    for _ in range(iterations):
        scanner.scan(text)
    return (time.perf_counter() - start) / iterations


# =========================================================================
# Throughput benchmarks
# =========================================================================


class TestScannerThroughput:

    def test_1kb_under_1ms(self, scanner):
        """1KB of mixed text should scan in under 1ms."""
        text = "Normal code line with variables and functions.\n" * 20  # ~1KB
        avg = _time_scan(scanner, text, iterations=50)
        assert avg < 0.001, f"1KB scan: {avg*1000:.2f}ms (expected <1ms)"

    def test_10kb_under_5ms(self, scanner):
        """10KB of text should scan in under 5ms."""
        text = "This is a typical log line with some data values.\n" * 200  # ~10KB
        avg = _time_scan(scanner, text, iterations=20)
        assert avg < 0.005, f"10KB scan: {avg*1000:.2f}ms (expected <5ms)"

    def test_100kb_under_50ms(self, scanner):
        """100KB (our input limit) should scan in under 50ms."""
        text = "x" * 100_000
        avg = _time_scan(scanner, text, iterations=10)
        assert avg < 0.050, f"100KB scan: {avg*1000:.2f}ms (expected <50ms)"

    def test_100kb_with_embedded_secrets(self, scanner):
        """100KB with secrets scattered throughout — still under 100ms."""
        lines = []
        for i in range(2000):
            if i % 200 == 0:
                lines.append(f"aws_key=AKIAIOSFODNN7EXAMPLE secret=abc123")
            elif i % 300 == 0:
                lines.append(f"SSN: 123-45-6789")
            else:
                lines.append(f"Line {i}: normal application log data x={i*3}")
        text = "\n".join(lines)
        avg = _time_scan(scanner, text, iterations=5)
        assert avg < 0.100, f"100KB+secrets scan: {avg*1000:.2f}ms (expected <100ms)"


# =========================================================================
# Pattern count scaling
# =========================================================================


class TestPatternScaling:

    def test_clean_text_no_backtracking(self, scanner):
        """Clean text shouldn't trigger catastrophic backtracking."""
        # Text designed to stress regex engines: lots of near-matches
        text = ("api_key= " * 100 + "secret= " * 100 + "password= " * 100)
        avg = _time_scan(scanner, text, iterations=10)
        assert avg < 0.050, f"Near-miss text: {avg*1000:.2f}ms (expected <50ms)"

    def test_many_detections_scales_linearly(self, scanner):
        """Text with many secrets should scale linearly, not quadratically."""
        # 50 real secrets
        secrets = [
            "AKIAIOSFODNN7EXAMPLE",
            "password=RealSecret123!",
            "ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef12",
        ]
        text = "\n".join(
            f"Line {i}: {secrets[i % len(secrets)]}" for i in range(150)
        )

        avg = _time_scan(scanner, text, iterations=5)
        assert avg < 0.100, f"50-secret scan: {avg*1000:.2f}ms (expected <100ms)"

    def test_overlap_resolution_not_quadratic(self, scanner):
        """Overlap resolution should be fast even with many detections."""
        # Dense text that triggers many overlapping patterns
        text = "\n".join(
            f"api_key=sk-{'a' * 40} password=Secret{i}! token=xoxb-1234-5678-{'x'*20}"
            for i in range(50)
        )
        avg = _time_scan(scanner, text, iterations=5)
        assert avg < 0.200, f"Dense overlap scan: {avg*1000:.2f}ms (expected <200ms)"


# =========================================================================
# Redaction performance
# =========================================================================


class TestRedactionPerformance:

    def test_redact_10_secrets_under_10ms(self, scanner):
        """Redacting 10 secrets in a document should be fast."""
        lines = [f"Line {i}: normal text" for i in range(100)]
        for i in range(0, 100, 10):
            lines[i] = f"Line {i}: AKIAIOSFODNN7EXAMPLE"
        text = "\n".join(lines)

        start = time.perf_counter()
        for _ in range(10):
            scanner.redact(text)
        avg = (time.perf_counter() - start) / 10

        assert avg < 0.010, f"Redact 10 secrets: {avg*1000:.2f}ms (expected <10ms)"

    def test_redact_idempotent(self, scanner):
        """Redacting already-redacted text should find no new secrets."""
        text = "password=SuperSecret123! key=AKIAIOSFODNN7EXAMPLE"
        redacted = scanner.redact(text)
        re_redacted = scanner.redact(redacted)
        assert redacted == re_redacted


# =========================================================================
# has_secrets() short-circuit
# =========================================================================


class TestHasSecretsShortCircuit:

    def test_has_secrets_on_clean_text_fast(self, scanner):
        """has_secrets on clean text should be as fast as a full scan."""
        text = "Normal text " * 1000
        start = time.perf_counter()
        for _ in range(50):
            scanner.has_secrets(text)
        avg = (time.perf_counter() - start) / 50
        assert avg < 0.010, f"has_secrets clean: {avg*1000:.2f}ms (expected <10ms)"
