"""Test secrets detection Pro rules."""

import tempfile
from pathlib import Path

from apiposture.core.models.endpoint import Endpoint
from apiposture.core.models.enums import EndpointType, Framework, HttpMethod

from apiposture_pro.rules.secrets.ap014_hardcoded_secrets import AP014HardcodedSecrets
from apiposture_pro.rules.secrets.ap015_api_key_exposure import AP015APIKeyExposure
from apiposture_pro.rules.secrets.patterns import is_placeholder


def test_ap014_detects_aws_key() -> None:
    """Test AP014 detects hardcoded AWS keys."""
    rule = AP014HardcodedSecrets()

    # Create a temp file with a hardcoded AWS key
    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("aws_access_key = 'AKIAIOSFODNN7EXAMPLE'\n")
        f.write("def my_endpoint():\n")
        f.write("    return {}\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=2,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        assert len(findings) > 0
        assert any("AWS" in f.message for f in findings)
    finally:
        temp_file.unlink()
        rule._scanned_files.clear()


def test_ap014_detects_password() -> None:
    """Test AP014 detects hardcoded passwords."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("password = 'super_secret_password_123'\n")
        f.write("def my_endpoint():\n")
        f.write("    return {}\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=2,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        assert len(findings) > 0
        assert any("password" in f.message.lower() for f in findings)
    finally:
        temp_file.unlink()
        rule._scanned_files.clear()


def test_ap014_ignores_placeholders() -> None:
    """Test AP014 ignores known placeholders."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("password = 'YOUR_PASSWORD_HERE'\n")
        f.write("api_key = 'your-api-key-here'\n")
        f.write("def my_endpoint():\n")
        f.write("    return {}\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=3,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        # Should not detect placeholders
        assert len(findings) == 0
    finally:
        temp_file.unlink()
        rule._scanned_files.clear()


def test_ap014_instance_state_not_shared() -> None:
    """Test AP014 uses instance state, not class-level state."""
    rule1 = AP014HardcodedSecrets()
    rule2 = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("password = 'super_secret_password_123'\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        # Scan with rule1
        findings1 = list(rule1.evaluate(endpoint))
        assert len(findings1) > 0

        # rule2 should still detect (separate instance, separate _scanned_files)
        findings2 = list(rule2.evaluate(endpoint))
        assert len(findings2) > 0
    finally:
        temp_file.unlink()


def test_ap014_detects_npm_token() -> None:
    """Test AP014 detects npm tokens."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("npm_token = 'npm_AbCdEfGhIjKlMnOpQrStUvWxYz1234567890'\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        assert any("npm" in f.message.lower() for f in findings)
    finally:
        temp_file.unlink()


def test_ap014_detects_pypi_token() -> None:
    """Test AP014 detects PyPI tokens."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("pypi_token = 'pypi-AgEIcHlwaS5vcmcCJGY1MzRjZDA'\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        assert any("PyPI" in f.message for f in findings)
    finally:
        temp_file.unlink()


def test_ap014_detects_postgres_connection() -> None:
    """Test AP014 detects postgres connection strings with passwords."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("db_url = 'postgresql://user:s3cretP4ss@localhost:5432/mydb'\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        assert any("Postgres" in f.message or "Database" in f.message for f in findings)
    finally:
        temp_file.unlink()


def test_ap014_detects_django_secret_key() -> None:
    """Test AP014 detects Django SECRET_KEY."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("SECRET_KEY = 'django-insecure-abc123def456ghi789jkl012mno'\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.DJANGO_DRF,
            endpoint_type=EndpointType.FUNCTION,
            function_name="my_view",
        )

        findings = list(rule.evaluate(endpoint))
        assert any("Django" in f.message or "SECRET_KEY" in f.message for f in findings)
    finally:
        temp_file.unlink()


def test_ap014_detects_flask_secret_key() -> None:
    """Test AP014 detects Flask secret_key."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("app.secret_key = 'my-super-secret-flask-key'\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.FLASK,
            endpoint_type=EndpointType.FUNCTION,
            function_name="my_view",
        )

        findings = list(rule.evaluate(endpoint))
        assert any("Flask" in f.message for f in findings)
    finally:
        temp_file.unlink()


def test_ap014_detects_bearer_token() -> None:
    """Test AP014 detects hardcoded bearer tokens."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("headers = {'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9abcd'}\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        assert any("Bearer" in f.message or "JWT" in f.message for f in findings)
    finally:
        temp_file.unlink()


def test_ap014_detects_redis_connection() -> None:
    """Test AP014 detects redis connection strings."""
    rule = AP014HardcodedSecrets()

    with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
        f.write("redis_url = 'redis://:mysecretpassword@redis-host:6379/0'\n")
        temp_file = Path(f.name)

    try:
        endpoint = Endpoint(
            route="/api/test",
            methods=[HttpMethod.GET],
            file_path=temp_file,
            line_number=1,
            framework=Framework.FASTAPI,
            endpoint_type=EndpointType.ROUTER,
            function_name="my_endpoint",
        )

        findings = list(rule.evaluate(endpoint))
        assert any("Redis" in f.message for f in findings)
    finally:
        temp_file.unlink()


# --- Placeholder detection tests ---


def test_placeholder_todo() -> None:
    """Test is_placeholder detects TODO."""
    assert is_placeholder("TODO_replace_this")


def test_placeholder_fixme() -> None:
    """Test is_placeholder detects FIXME."""
    assert is_placeholder("FIXME_set_real_key")


def test_placeholder_test() -> None:
    """Test is_placeholder detects 'test' keyword."""
    assert is_placeholder("test_api_key_value")


def test_placeholder_dummy() -> None:
    """Test is_placeholder detects 'dummy'."""
    assert is_placeholder("dummy_secret_value")


def test_placeholder_sample() -> None:
    """Test is_placeholder detects 'sample'."""
    assert is_placeholder("sample_password_here")


def test_placeholder_all_same_char() -> None:
    """Test is_placeholder detects values that are all the same character."""
    assert is_placeholder("aaaaaaaaaa")
    assert is_placeholder("BBBBBBBB")


def test_placeholder_env_var_reference() -> None:
    """Test is_placeholder detects environment variable references."""
    assert is_placeholder("os.environ['SECRET_KEY']")
    assert is_placeholder("${DATABASE_PASSWORD}")
    assert is_placeholder("os.getenv('API_KEY')")


def test_placeholder_real_value_not_flagged() -> None:
    """Test is_placeholder does NOT flag real-looking values."""
    assert not is_placeholder("sk_live_4eC39HqLyjWDarjtT1zdp7dc")
    assert not is_placeholder("AKIAIOSFODNN7EXAMPLE")


# --- AP015 tests ---


def test_ap015_api_key_in_url() -> None:
    """Test AP015 detects API keys in URL parameters."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/data/{api_key}",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="get_data",
    )

    findings = list(rule.evaluate(endpoint))
    assert len(findings) > 0
    assert any("api key" in f.message.lower() or "token" in f.message.lower() for f in findings)


def test_ap015_auth_with_get() -> None:
    """Test AP015 detects authentication via GET."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/login",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=20,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="login",
    )

    findings = list(rule.evaluate(endpoint))
    assert len(findings) > 0
    assert any("GET" in f.message for f in findings)


def test_ap015_password_with_get() -> None:
    """Test AP015 detects password handling via GET."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/change",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=30,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="change_password",
    )

    findings = list(rule.evaluate(endpoint))
    assert len(findings) > 0
    assert any("secret" in f.message.lower() or "password" in f.message.lower() for f in findings)


def test_ap015_query_param_api_key() -> None:
    """Test AP015 detects API keys in query parameters."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/data?api_key=value",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="get_data",
    )

    findings = list(rule.evaluate(endpoint))
    assert any("query parameter" in f.message.lower() for f in findings)


def test_ap015_query_param_token() -> None:
    """Test AP015 detects tokens in query parameters."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/data?token=abc123",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="get_data",
    )

    findings = list(rule.evaluate(endpoint))
    assert any("query parameter" in f.message.lower() for f in findings)


# ======================================================================
# New tests for false positive fixes discovered via real-world scanning
# ======================================================================


def test_ap015_password_reset_token_not_flagged() -> None:
    """Test AP015 does NOT flag /reset/<token> (password reset is standard practice)."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/reset/<token>",
        methods=[HttpMethod.GET, HttpMethod.POST],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FLASK,
        endpoint_type=EndpointType.FUNCTION,
        function_name="reset_form",
    )

    findings = list(rule.evaluate(endpoint))
    # Should NOT flag token-in-URL for password reset routes
    assert not any(
        "api key or token as url parameter" in f.message.lower()
        for f in findings
    )


def test_ap015_verify_email_token_not_flagged() -> None:
    """Test AP015 does NOT flag /verify/<token> (email verification is standard)."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/verify/<token>",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FLASK,
        endpoint_type=EndpointType.FUNCTION,
        function_name="verify_email",
    )

    findings = list(rule.evaluate(endpoint))
    assert not any(
        "api key or token as url parameter" in f.message.lower()
        for f in findings
    )


def test_ap015_monkey_patch_no_secret_fp() -> None:
    """Test AP015 does NOT flag 'monkey_patch' as secret handling."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/patch",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="monkey_patch",
    )

    findings = list(rule.evaluate(endpoint))
    assert not any("secrets" in f.message.lower() or "credentials" in f.message.lower() for f in findings)


def test_ap015_rotate_key_still_triggers() -> None:
    """Test AP015 still flags 'rotate_key' as secret handling."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/keys",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="rotate_key",
    )

    findings = list(rule.evaluate(endpoint))
    assert any("secrets" in f.message.lower() or "credentials" in f.message.lower() for f in findings)


def test_ap015_still_flags_api_token_in_url() -> None:
    """Test AP015 still flags /api/data/{token} (non-reset context)."""
    rule = AP015APIKeyExposure()

    endpoint = Endpoint(
        route="/api/data/{token}",
        methods=[HttpMethod.GET],
        file_path=Path("api.py"),
        line_number=10,
        framework=Framework.FASTAPI,
        endpoint_type=EndpointType.ROUTER,
        function_name="get_data",
    )

    findings = list(rule.evaluate(endpoint))
    assert any(
        "api key or token as url parameter" in f.message.lower()
        for f in findings
    )
