# -*- coding: utf-8 -*-
"""SensitiveDataFilter 测试 — 16 模式密钥脱敏"""
import pytest

from abyss.security.sensitive_filter import SensitiveDataFilter


@pytest.fixture
def filter_obj():
    return SensitiveDataFilter()


# -------- 单模式命中测试（共 16 个） --------

def test_redact_openai_key(filter_obj):
    """1. OpenAI sk- key"""
    text = "export OPENAI_API_KEY=\"sk-" + "a" * 48 + "\""
    result = filter_obj.redact(text)
    assert "sk-" + "a" * 48 not in result
    assert "REDACTED" in result
    assert "OPENAI_KEY" in result or "sk-***REDACTED" in result


def test_redact_openai_proj_key(filter_obj):
    """2. OpenAI sk-proj- key"""
    text = "key: sk-proj-" + "b" * 48
    result = filter_obj.redact(text)
    assert "sk-proj-" + "b" * 48 not in result
    assert "REDACTED" in result


def test_redact_openai_ant_key(filter_obj):
    """3. Anthropic sk-ant- key"""
    text = "sk-ant-" + "c" * 48 + "-xyz"
    result = filter_obj.redact(text)
    assert "sk-ant-" + "c" * 48 not in result
    assert "REDACTED" in result


def test_redact_aws_akia(filter_obj):
    """4. AWS AKIA key"""
    text = "AWS_ACCESS_KEY_ID=AKIA" + "ABCDEFGH12345678"
    result = filter_obj.redact(text)
    assert "AKIA" + "ABCDEFGH12345678" not in result
    assert "REDACTED" in result


def test_redact_github_token(filter_obj):
    """5. GitHub ghp_ token"""
    text = "GITHUB_TOKEN=ghp_" + "d" * 36
    result = filter_obj.redact(text)
    assert "ghp_" + "d" * 36 not in result
    assert "REDACTED" in result


def test_redact_gitlab_token(filter_obj):
    """6. GitLab glpat- token"""
    text = "gitlab_token: glpat-" + "e" * 20
    result = filter_obj.redact(text)
    assert "glpat-" + "e" * 20 not in result
    assert "REDACTED" in result


def test_redact_slack_token(filter_obj):
    """7. Slack xox token"""
    text = "SLACK_API_TOKEN=xoxb-" + "f" * 24 + "-" + "f" * 24
    result = filter_obj.redact(text)
    assert "xoxb-" + "f" * 24 not in result
    assert "REDACTED" in result


def test_redact_huggingface_token(filter_obj):
    """8. HuggingFace hf_ token"""
    text = "HF_TOKEN=hf_" + "g" * 30
    result = filter_obj.redact(text)
    assert "hf_" + "g" * 30 not in result
    assert "REDACTED" in result


def test_redact_gcp_aiza_key(filter_obj):
    """9. GCP AIza key"""
    text = "GOOGLE_API_KEY=AIza" + "h" * 35
    result = filter_obj.redact(text)
    assert "AIza" + "h" * 35 not in result
    assert "REDACTED" in result


def test_redact_stripe_live_key(filter_obj):
    """10. Stripe sk_live_ key"""
    text = "STRIPE_SECRET_KEY=sk_live_" + "i" * 24
    result = filter_obj.redact(text)
    assert "sk_live_" + "i" * 24 not in result
    assert "REDACTED" in result


def test_redact_vercel_token(filter_obj):
    """11. Vercel vercel_ token"""
    text = "VERCEL_TOKEN=vercel_" + "j" * 24
    result = filter_obj.redact(text)
    assert "vercel_" + "j" * 24 not in result
    assert "REDACTED" in result


def test_redact_supabase_token(filter_obj):
    """12. Supabase sbp_ token"""
    text = "SUPABASE_KEY=sbp_" + "k" * 24
    result = filter_obj.redact(text)
    assert "sbp_" + "k" * 24 not in result
    assert "REDACTED" in result


def test_redact_jwt_token(filter_obj):
    """13. JWT eyJ... token"""
    text = "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
    result = filter_obj.redact(text)
    assert "eyJhbGciOiJIUzI1NiJ9.eyJzdWIi" not in result
    assert "REDACTED" in result


def test_redact_pem_private_key(filter_obj):
    """14. PEM private key"""
    text = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----"
    result = filter_obj.redact(text)
    assert "BEGIN RSA PRIVATE KEY" not in result
    assert "REDACTED" in result


def test_redact_generic_key_value(filter_obj):
    """15. 通用 key=value 凭证"""
    text = "api_key = \"mySecretPassword123\""
    result = filter_obj.redact(text)
    assert "mySecretPassword123" not in result
    assert "REDACTED" in result


def test_redact_db_connection_string(filter_obj):
    """16. 数据库连接串密码"""
    text = "DATABASE_URL=postgres://user:MySecretPass123@localhost:5432/db"
    result = filter_obj.redact(text)
    assert "MySecretPass123" not in result
    assert "REDACTED" in result


# -------- 边界条件测试 --------

def test_redact_empty_string(filter_obj):
    assert filter_obj.redact("") == ""


def test_redact_no_secrets(filter_obj):
    """无密钥的普通文本应原样返回"""
    text = "ls -la\nfile1.py\nfile2.py"
    assert filter_obj.redact(text) == text


def test_redact_multiple_secrets_in_one_text(filter_obj):
    """一段文本含多种密钥，应全部脱敏"""
    text = (
        f"OPENAI_KEY=sk-{'a' * 48}\n"
        f"GITHUB_TOKEN=ghp_{'d' * 36}\n"
        f"AWS_KEY=AKIA{'Z' * 16}\n"
    )
    result = filter_obj.redact(text)
    assert "sk-" + "a" * 48 not in result
    assert "ghp_" + "d" * 36 not in result
    assert "AKIA" + "Z" * 16 not in result
    assert result.count("REDACTED") >= 3


def test_redact_preserves_prefix_and_length(filter_obj):
    """脱敏后应保留前缀 + 长度信息便于调试识别"""
    text = "sk-" + "a" * 48
    result = filter_obj.redact(text)
    # 应保留 "sk-" 前缀
    assert result.startswith("sk-") or "sk-" in result
    # 应含长度信息
    assert "48" in result or "51" in result  # 48 字符或 51 总长


def test_redact_does_not_match_short_strings(filter_obj):
    """过短的字符串不应误判为密钥"""
    text = "sk-abc"  # 太短，不是合法 OpenAI key
    result = filter_obj.redact(text)
    assert result == text


def test_redact_preserves_non_secret_text_around_secrets(filter_obj):
    """脱敏不应破坏非密钥文本"""
    text = "运行命令: export KEY=sk-" + "a" * 48 + " && python script.py"
    result = filter_obj.redact(text)
    assert "运行命令:" in result
    assert "python script.py" in result
    assert "sk-" + "a" * 48 not in result


def test_redact_dict_value_returns_new_dict(filter_obj):
    """redact_dict 应递归处理 dict 中的字符串值"""
    data = {
        "stdout": "key=sk-" + "a" * 48,
        "stderr": "",
        "nested": {"token": "ghp_" + "d" * 36},
        "non_string": 12345,
    }
    result = filter_obj.redact_dict(data)
    assert "sk-" + "a" * 48 not in result["stdout"]
    assert "ghp_" + "d" * 36 not in result["nested"]["token"]
    assert result["non_string"] == 12345  # 非字符串原样保留


def test_redact_dict_preserves_structure(filter_obj):
    """redact_dict 应保持原 dict 结构"""
    data = {"a": {"b": {"c": "normal text"}}}
    result = filter_obj.redact_dict(data)
    assert result == data


def test_redact_dict_handles_list_values(filter_obj):
    """redact_dict 应处理 list 中的字符串"""
    data = {"items": ["sk-" + "a" * 48, "normal", "ghp_" + "d" * 36]}
    result = filter_obj.redact_dict(data)
    assert "sk-" + "a" * 48 not in result["items"][0]
    assert result["items"][1] == "normal"
    assert "ghp_" + "d" * 36 not in result["items"][2]


def test_singleton_instance():
    """默认实例应是单例（避免重复编译正则）"""
    from abyss.security.sensitive_filter import get_default_filter
    f1 = get_default_filter()
    f2 = get_default_filter()
    assert f1 is f2


def test_redact_idempotent(filter_obj):
    """脱敏后的结果再次脱敏应保持不变"""
    text = "sk-" + "a" * 48
    once = filter_obj.redact(text)
    twice = filter_obj.redact(once)
    assert once == twice
