"""Tests for the PatternRegistry."""

import pytest
from argus_nano.patterns import PatternRegistry


@pytest.fixture(scope="module")
def registry():
    return PatternRegistry()


# ------------------------------------------------------------------
# Loading
# ------------------------------------------------------------------


def test_load_providers(registry):
    assert registry.pattern_count == 137


def test_load_benign(registry):
    assert len(registry.benign) == 26


def test_provider_count(registry):
    # 38 unique providers as per the pattern registry
    assert registry.provider_count >= 38


# ------------------------------------------------------------------
# scan_line — positive matches
# ------------------------------------------------------------------


def test_scan_line_aws_access_key(registry):
    line = 'AWS_KEY = "AKIAIOSFODNN7EXAMPLE"'
    matches = registry.scan_line(line, 1)
    ids = {m.pattern_id for m in matches}
    assert "aws_access_key_id" in ids


def test_scan_line_github_pat(registry):
    token = "ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij"
    line = f'GITHUB_TOKEN = "{token}"'
    matches = registry.scan_line(line, 5)
    ids = {m.pattern_id for m in matches}
    assert "github_pat" in ids
    assert any(m.value == token for m in matches)
    assert all(m.line_number == 5 for m in matches)


def test_scan_line_stripe_live(registry):
    line = 'stripe_key = "sk_live_4eC39HqLyjWDarjtT1zdp7dc"'
    matches = registry.scan_line(line, 10)
    ids = {m.pattern_id for m in matches}
    assert "stripe_secret_key_live" in ids


# ------------------------------------------------------------------
# scan_line — connection string password extraction
# ------------------------------------------------------------------


def test_scan_line_connection_string_at_in_password(registry):
    """Passwords containing @ must not split the match at the wrong boundary."""
    line = "postgresql://admin:p@ssw0rd@localhost:5432/db"
    matches = [m for m in registry.scan_line(line, 1) if m.pattern_id == "postgres_connection_string"]
    assert len(matches) == 1
    # Full URL is the value; the host must be localhost, not ssw0rd!@localhost
    assert "p@ssw0rd@localhost" in matches[0].value
    assert matches[0].value.endswith("/db")


def test_scan_line_connection_string_multiple_at_in_password(registry):
    line = "postgresql://admin:p@ss@w0rd!@db.internal:5432/db"
    matches = [m for m in registry.scan_line(line, 1) if m.pattern_id == "postgres_connection_string"]
    assert len(matches) == 1
    assert "p@ss@w0rd!@db.internal" in matches[0].value


def test_scan_line_connection_string_value_not_scheme(registry):
    """Regression guard: structural groups like (?:ql)? must not leak as the value."""
    line = "postgresql://admin:s3cret@db.internal:5432/production"
    matches = [m for m in registry.scan_line(line, 1) if m.pattern_id == "postgres_connection_string"]
    assert len(matches) == 1
    assert matches[0].value != "ql"
    assert "s3cret@db.internal" in matches[0].value

    line2 = "mongodb+srv://user:mypass@cluster.mongodb.net"
    matches2 = [m for m in registry.scan_line(line2, 1) if m.pattern_id == "mongodb_connection_string"]
    assert len(matches2) == 1
    assert matches2[0].value != "+srv"
    assert "mypass@cluster.mongodb.net" in matches2[0].value


# ------------------------------------------------------------------
# scan_line — no match
# ------------------------------------------------------------------


def test_scan_line_no_match(registry):
    line = "x = 42  # just a number"
    matches = registry.scan_line(line, 1)
    # May have incidental matches on generic patterns; that's OK.
    # The important thing is that no high-specificity prefixed pattern fires.
    prefixed = [m for m in matches if m.pattern_id == "aws_access_key_id"]
    assert prefixed == []


# ------------------------------------------------------------------
# is_benign
# ------------------------------------------------------------------


def test_is_benign_uuid(registry):
    assert registry.is_benign("550e8400-e29b-41d4-a716-446655440000")


def test_is_benign_sha256(registry):
    assert registry.is_benign("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")


def test_is_benign_placeholder(registry):
    assert registry.is_benign("YOUR_API_KEY")
    assert registry.is_benign("CHANGEME")
    assert registry.is_benign("example_key")


def test_is_benign_localhost(registry):
    assert registry.is_benign("postgres://user:pass@localhost:5432/devdb")


def test_is_not_benign_real_key(registry):
    assert not registry.is_benign("AKIAIOSFODNN7EXAMPLE")


def test_is_not_benign_github_pat(registry):
    assert not registry.is_benign("ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij")


# ------------------------------------------------------------------
# Provider test_cases validation
# ------------------------------------------------------------------


def test_provider_should_detect(registry):
    """Every should_detect example should match its pattern regex."""
    failures = []
    for pat in registry.providers:
        test_cases = pat.get("test_cases", {})
        for example in test_cases.get("should_detect", []):
            hits = pat["compiled"].findall(example)
            if not hits:
                failures.append(f"{pat['id']}: should_detect failed for {example!r}")
    assert failures == [], "\n".join(failures)


def test_provider_should_ignore(registry):
    """Every should_ignore example should NOT match its pattern regex."""
    failures = []
    for pat in registry.providers:
        test_cases = pat.get("test_cases", {})
        for example in test_cases.get("should_ignore", []):
            m = pat["compiled"].fullmatch(example)
            if m:
                failures.append(f"{pat['id']}: should_ignore matched for {example!r}")
    assert failures == [], "\n".join(failures)
