"""
Unit tests for the secret handling module.
"""

import pytest

from agent_orchestrator.secrets.redactor import (
    SecretRedactor,
    redact_dict,
)
from agent_orchestrator.secrets.guard import (
    SecretFileGuard,
    NEVER_STORE_PATTERNS,
    get_action_risk_level,
)
from agent_orchestrator.adapters.base import RiskLevel


class TestSecretRedactor:
    """Tests for SecretRedactor class."""

    def test_redact_anthropic_key(self):
        """Test redacting Anthropic API keys."""
        text = "Using key sk-ant-api03-abc123xyz456def789ghi"
        redacted = SecretRedactor.redact(text)

        assert "sk-ant-" not in redacted
        assert "[REDACTED_ANTHROPIC_KEY]" in redacted

    def test_redact_openai_key(self):
        """Test redacting OpenAI API keys."""
        text = "OPENAI_API_KEY=sk-proj-abc123def456ghi789jkl012mno345pqr"
        redacted = SecretRedactor.redact(text)

        assert "sk-proj-" not in redacted
        # Could match either OpenAI or generic pattern

    def test_redact_google_key(self):
        """Test redacting Google API keys."""
        text = "google_api_key: AIzaSyA-abc123DEF456_ghi789JKL-mno"
        redacted = SecretRedactor.redact(text)

        assert "AIza" not in redacted
        assert "[REDACTED_GOOGLE_KEY]" in redacted

    def test_redact_github_pat(self):
        """Test redacting GitHub personal access tokens."""
        text = "GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        redacted = SecretRedactor.redact(text)

        assert "ghp_" not in redacted
        assert "[REDACTED_GITHUB_PAT]" in redacted

    def test_redact_aws_key(self):
        """Test redacting AWS access keys."""
        text = "AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE"
        redacted = SecretRedactor.redact(text)

        assert "AKIA" not in redacted
        assert "[REDACTED_AWS_ACCESS_KEY]" in redacted

    def test_redact_bearer_token(self):
        """Test redacting bearer tokens."""
        text = 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIi'
        redacted = SecretRedactor.redact(text)

        assert "eyJhbGciOi" not in redacted
        assert "Bearer [REDACTED]" in redacted

    def test_redact_password_in_url(self):
        """Test redacting passwords in URLs."""
        text = "postgres://admin:supersecret123@localhost:5432/db"
        redacted = SecretRedactor.redact(text)

        assert "supersecret123" not in redacted
        assert "[REDACTED]" in redacted

    def test_redact_password_config(self):
        """Test redacting passwords in config."""
        text = 'password = "my_secret_password_123"'
        redacted = SecretRedactor.redact(text)

        assert "my_secret_password_123" not in redacted
        assert "password=[REDACTED]" in redacted

    def test_redact_secret_config(self):
        """Test redacting secrets in config."""
        text = "SECRET_KEY=django-insecure-abc123"
        redacted = SecretRedactor.redact(text)

        assert "django-insecure" not in redacted

    def test_redact_private_key(self):
        """Test redacting private keys."""
        text = """-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA0Z3VS5JJcds3xfn
-----END RSA PRIVATE KEY-----"""
        redacted = SecretRedactor.redact(text)

        assert "MIIEpQ" not in redacted
        assert "[REDACTED_PRIVATE_KEY]" in redacted

    def test_redact_preserves_safe_text(self):
        """Test that safe text is preserved."""
        text = "This is a normal log message with no secrets."
        redacted = SecretRedactor.redact(text)

        assert redacted == text

    def test_redact_empty_string(self):
        """Test redacting empty string."""
        assert SecretRedactor.redact("") == ""
        assert SecretRedactor.redact(None) is None

    def test_is_sensitive(self):
        """Test checking if text is sensitive."""
        assert SecretRedactor.is_sensitive("sk-ant-xxx") is True
        assert SecretRedactor.is_sensitive("normal text") is False
        assert SecretRedactor.is_sensitive("password=secret") is True

    def test_find_sensitive_patterns(self):
        """Test finding sensitive patterns."""
        text = "sk-ant-xxx123 and ghp_abcdef"
        patterns = SecretRedactor.find_sensitive_patterns(text)

        assert len(patterns) >= 2
        assert any("Anthropic" in p for p in patterns)
        assert any("GitHub" in p for p in patterns)

    def test_wrap_logger(self):
        """Test wrapping a logger function."""
        logged = []

        def test_logger(msg):
            logged.append(msg)

        safe_logger = SecretRedactor.wrap_logger(test_logger)
        safe_logger("API key is sk-ant-test123456789abcdef")

        assert len(logged) == 1
        assert "sk-ant-" not in logged[0]
        assert "[REDACTED_ANTHROPIC_KEY]" in logged[0]

    def test_add_custom_pattern(self):
        """Test adding custom patterns."""
        original_len = len(SecretRedactor.REDACTION_PATTERNS)

        SecretRedactor.add_pattern(r"CUSTOM_SECRET_\d+", "[CUSTOM_REDACTED]")

        assert len(SecretRedactor.REDACTION_PATTERNS) == original_len + 1

        text = "Using CUSTOM_SECRET_12345"
        redacted = SecretRedactor.redact(text)
        assert "[CUSTOM_REDACTED]" in redacted


class TestRedactDict:
    """Tests for redact_dict function."""

    def test_redact_simple_dict(self):
        """Test redacting a simple dictionary."""
        data = {
            "username": "admin",
            "password": "secret123",
            "api_key": "sk-ant-xxx",
        }
        redacted = redact_dict(data)

        assert redacted["username"] == "admin"
        assert redacted["password"] == "[REDACTED]"
        assert redacted["api_key"] == "[REDACTED]"

    def test_redact_nested_dict(self):
        """Test redacting nested dictionaries."""
        data = {
            "config": {
                "database": {
                    "host": "localhost",
                    "password": "dbpass",
                },
            },
        }
        redacted = redact_dict(data)

        assert redacted["config"]["database"]["host"] == "localhost"
        assert redacted["config"]["database"]["password"] == "[REDACTED]"

    def test_redact_list_in_dict(self):
        """Test redacting lists in dictionaries."""
        data = {
            "tokens": ["sk-ant-xxx", "normal-value"],
        }
        redacted = redact_dict(data)

        assert "[REDACTED_ANTHROPIC_KEY]" in redacted["tokens"][0]
        assert redacted["tokens"][1] == "normal-value"


class TestSecretFileGuard:
    """Tests for SecretFileGuard class."""

    def test_is_secret_file_env(self):
        """Test detecting .env files."""
        assert SecretFileGuard.is_secret_file(".env") is True
        assert SecretFileGuard.is_secret_file(".env.local") is True
        assert SecretFileGuard.is_secret_file(".env.production") is True
        assert SecretFileGuard.is_secret_file("/path/to/.env") is True

    def test_is_secret_file_keys(self):
        """Test detecting key files."""
        assert SecretFileGuard.is_secret_file("server.pem") is True
        assert SecretFileGuard.is_secret_file("private.key") is True
        assert SecretFileGuard.is_secret_file("id_rsa") is True
        assert SecretFileGuard.is_secret_file("id_ed25519") is True

    def test_is_secret_file_credentials(self):
        """Test detecting credential files."""
        assert SecretFileGuard.is_secret_file("credentials.json") is True
        assert SecretFileGuard.is_secret_file("secrets.yaml") is True
        assert SecretFileGuard.is_secret_file("secrets.yml") is True

    def test_is_secret_file_cloud(self):
        """Test detecting cloud credential files."""
        assert SecretFileGuard.is_secret_file(".aws/credentials") is True
        assert SecretFileGuard.is_secret_file(".kube/config") is True

    def test_is_secret_file_ssh(self):
        """Test detecting SSH files."""
        assert SecretFileGuard.is_secret_file(".ssh/id_rsa") is True
        assert SecretFileGuard.is_secret_file(".ssh/known_hosts") is True

    def test_is_not_secret_file(self):
        """Test that normal files are not flagged."""
        assert SecretFileGuard.is_secret_file("main.py") is False
        assert SecretFileGuard.is_secret_file("README.md") is False
        assert SecretFileGuard.is_secret_file("package.json") is False
        assert SecretFileGuard.is_secret_file("src/config.py") is False

    def test_check_read_blocked(self):
        """Test check_read for blocked files."""
        allowed, reason = SecretFileGuard.check_read(".env")
        assert allowed is False
        assert "BLOCKED" in reason

    def test_check_read_allowed(self):
        """Test check_read for allowed files."""
        allowed, reason = SecretFileGuard.check_read("main.py")
        assert allowed is True
        assert reason == "allowed"

    def test_check_write_blocked(self):
        """Test check_write for blocked files."""
        allowed, reason = SecretFileGuard.check_write("secrets.yaml")
        assert allowed is False
        assert "BLOCKED" in reason

    def test_classify_file_risk(self):
        """Test file risk classification."""
        assert SecretFileGuard.classify_file_risk(".env") == RiskLevel.CRITICAL
        assert SecretFileGuard.classify_file_risk("id_rsa") == RiskLevel.CRITICAL
        assert SecretFileGuard.classify_file_risk(".github/workflows/ci.yml") == RiskLevel.HIGH
        assert SecretFileGuard.classify_file_risk("Dockerfile") == RiskLevel.HIGH
        assert SecretFileGuard.classify_file_risk("package.json") == RiskLevel.MEDIUM
        assert SecretFileGuard.classify_file_risk("main.py") == RiskLevel.LOW

    def test_get_matching_pattern(self):
        """Test getting matching pattern."""
        pattern = SecretFileGuard.get_matching_pattern(".env.local")
        assert pattern is not None
        assert ".env" in pattern

        no_pattern = SecretFileGuard.get_matching_pattern("main.py")
        assert no_pattern is None

    def test_filter_paths(self):
        """Test filtering paths."""
        paths = [
            "main.py",
            ".env",
            "src/config.py",
            "secrets.yaml",
            "README.md",
        ]
        allowed, blocked = SecretFileGuard.filter_paths(paths)

        assert len(allowed) == 3
        assert len(blocked) == 2
        assert ".env" in blocked
        assert "secrets.yaml" in blocked


class TestGetActionRiskLevel:
    """Tests for get_action_risk_level function."""

    def test_read_secret_file(self):
        """Test reading a secret file."""
        risk = get_action_risk_level("read", ".env")
        assert risk == RiskLevel.HIGH

    def test_write_secret_file(self):
        """Test writing a secret file."""
        risk = get_action_risk_level("write", ".env")
        assert risk == RiskLevel.CRITICAL

    def test_delete_secret_file(self):
        """Test deleting a secret file."""
        risk = get_action_risk_level("delete", "credentials.json")
        assert risk == RiskLevel.CRITICAL

    def test_read_normal_file(self):
        """Test reading a normal file."""
        risk = get_action_risk_level("read", "main.py")
        assert risk == RiskLevel.LOW

    def test_write_config_file(self):
        """Test writing a config file."""
        risk = get_action_risk_level("write", "config.json")
        assert risk == RiskLevel.MEDIUM
