"""Tests for the Scanner class (patterns-only mode — no model dependency)."""

from pathlib import Path

import pytest
from argus_nano.scanner import Scanner, _mask_value


@pytest.fixture
def scanner():
    return Scanner(patterns_only=True)


# ------------------------------------------------------------------
# scan_file
# ------------------------------------------------------------------


def test_scan_file_env(scanner, tmp_path):
    env_file = tmp_path / ".env"
    env_file.write_text(
        "DATABASE_URL=postgres://admin:s3cret@db.example.com:5432/prod\nAWS_KEY=AKIAIOSFODNN7EXAMPLE\nDEBUG=true\n",
        encoding="utf-8",
    )
    results = scanner.scan_file(env_file)
    ids = {r.pattern_id for r in results}
    assert "aws_access_key_id" in ids
    assert "postgres_connection_string" in ids


def test_scan_file_no_secrets(scanner, tmp_path):
    clean = tmp_path / "app.py"
    clean.write_text('x = 42\nprint("hello")\n', encoding="utf-8")
    results = scanner.scan_file(clean)
    # Might have some generic pattern hits — filter for high-specificity ones
    prefixed = [
        r
        for r in results
        if r.pattern_id
        in (
            "aws_access_key_id",
            "github_pat",
            "stripe_secret_key_live",
        )
    ]
    assert prefixed == []


def test_scan_file_benign_filtered(scanner, tmp_path):
    f = tmp_path / "ids.txt"
    f.write_text(
        "request_id = 550e8400-e29b-41d4-a716-446655440000\n",
        encoding="utf-8",
    )
    results = scanner.scan_file(f)
    # UUID is benign — should be filtered out even if a generic pattern matched
    for r in results:
        assert "550e8400" not in r.value_masked


def test_scan_file_nonexistent(scanner):
    results = scanner.scan_file(Path("/nonexistent/file.txt"))
    assert results == []


# ------------------------------------------------------------------
# scan_directory
# ------------------------------------------------------------------


def test_scan_directory(scanner, tmp_path):
    (tmp_path / "a.env").write_text("KEY=AKIAIOSFODNN7EXAMPLE\n", encoding="utf-8")
    sub = tmp_path / "sub"
    sub.mkdir()
    (sub / "b.yml").write_text(
        "token: ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij\n",
        encoding="utf-8",
    )
    results = scanner.scan_directory(tmp_path)
    ids = {r.pattern_id for r in results}
    assert "aws_access_key_id" in ids
    assert "github_pat" in ids


def test_scan_directory_exclude(scanner, tmp_path):
    (tmp_path / "keep.env").write_text("KEY=AKIAIOSFODNN7EXAMPLE\n", encoding="utf-8")
    (tmp_path / "skip.env").write_text("KEY=AKIAIOSFODNN7EXAMPLE\n", encoding="utf-8")
    results = scanner.scan_directory(tmp_path, exclude=["skip.env"])
    files = {r.file_path for r in results}
    assert not any("skip.env" in f for f in files)


# ------------------------------------------------------------------
# Binary file handling
# ------------------------------------------------------------------


def test_binary_file_skipped(scanner, tmp_path):
    img = tmp_path / "photo.png"
    img.write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 100)
    results = scanner.scan_file(img)
    assert results == []


def test_null_bytes_file_skipped(scanner, tmp_path):
    f = tmp_path / "data.bin"
    f.write_bytes(b"AKIAIOSFODNN7EXAMPLE\x00\x00\x00")
    results = scanner.scan_file(f)
    assert results == []


# ------------------------------------------------------------------
# check
# ------------------------------------------------------------------


def test_check_value(scanner):
    results = scanner.check("AKIAIOSFODNN7EXAMPLE")
    ids = {r.pattern_id for r in results}
    assert "aws_access_key_id" in ids


def test_check_no_match(scanner):
    results = scanner.check("hello world")
    # Filter out any extremely generic patterns
    specific = [
        r
        for r in results
        if r.pattern_id
        in (
            "aws_access_key_id",
            "github_pat",
        )
    ]
    assert specific == []


# ------------------------------------------------------------------
# Value masking
# ------------------------------------------------------------------


def test_mask_long_value():
    assert _mask_value("AKIAIOSFODNN7EXAMPLE") == "AKIA****MPLE"


def test_mask_short_value():
    assert _mask_value("abcde") == "ab****de"


def test_mask_very_short_value():
    assert _mask_value("abc") == "****"


# ------------------------------------------------------------------
# ScanResult fields
# ------------------------------------------------------------------


def test_result_fields(scanner, tmp_path):
    f = tmp_path / "config.py"
    f.write_text(
        'API_KEY = "sk_live_4eC39HqLyjWDarjtT1zdp7dc"\n',
        encoding="utf-8",
    )
    results = scanner.scan_file(f)
    stripe = [r for r in results if r.pattern_id == "stripe_secret_key_live"]
    assert len(stripe) >= 1
    r = stripe[0]
    assert r.severity == "critical"
    assert r.provider == "stripe"
    assert r.line_number == 1
    assert r.confidence == 1.0
    assert "****" in r.value_masked
    assert str(f) in r.file_path


# ------------------------------------------------------------------
# Results sorting
# ------------------------------------------------------------------


def test_results_sorted_by_severity(scanner, tmp_path):
    f = tmp_path / "multi.env"
    f.write_text(
        "STRIPE=sk_live_4eC39HqLyjWDarjtT1zdp7dc\n"  # critical
        "TOKEN=ghp_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij\n",  # high
        encoding="utf-8",
    )
    results = scanner.scan_file(f)
    if len(results) >= 2:
        severities = [r.severity for r in results]
        critical_idx = next((i for i, s in enumerate(severities) if s == "critical"), None)
        high_idx = next((i for i, s in enumerate(severities) if s == "high"), None)
        if critical_idx is not None and high_idx is not None:
            assert critical_idx < high_idx
