"""Unit tests for :class:`ClerkWebhookVerifier`.

Webhook tests reuse :meth:`MockWebhookVerifier.sign` (the algorithm is
the same — Svix's HMAC-SHA256 over ``<id>.<ts>.<body>`` with the shared
secret), so test fixtures can swap between the mock and the concrete
verifier without re-implementing signing.
"""

from __future__ import annotations

import json

import pytest

from mgf.auth import InvalidWebhookSignatureError, MockWebhookVerifier, WebhookVerifier
from mgf.auth.clerk_webhook import ClerkWebhookVerifier


def test_clerk_webhook_verifier_satisfies_protocol() -> None:
    verifier = ClerkWebhookVerifier(webhook_secret=b"whsec_test")
    assert isinstance(verifier, WebhookVerifier)


class TestVerifyWebhook:
    @pytest.mark.asyncio
    async def test_round_trip_via_mock_sign_helper(self) -> None:
        # The mock and the concrete verifier share the same Svix
        # algorithm, so the mock's ``sign()`` helper produces headers
        # the concrete verifier validates. This is a deliberate
        # symmetry that lets test fixtures swap between verifiers
        # without re-implementing signing.
        secret = b"whsec_round_trip"
        verifier = ClerkWebhookVerifier(webhook_secret=secret)
        body = json.dumps({"type": "user.created"}).encode()
        headers = MockWebhookVerifier.sign(secret=secret, body=body, event_id="msg_1")

        event = await verifier.verify_webhook(headers, body)
        assert event.event_type == "user.created"
        assert event.event_id == "msg_1"

    @pytest.mark.asyncio
    async def test_wrong_secret_rejected(self) -> None:
        verifier = ClerkWebhookVerifier(webhook_secret=b"whsec_real")
        body = b'{"type": "user.created"}'
        headers = MockWebhookVerifier.sign(secret=b"whsec_attacker", body=body, event_id="msg_x")
        with pytest.raises(InvalidWebhookSignatureError, match="HMAC mismatch"):
            await verifier.verify_webhook(headers, body)

    @pytest.mark.asyncio
    async def test_missing_svix_id_rejected(self) -> None:
        secret = b"whsec_x"
        verifier = ClerkWebhookVerifier(webhook_secret=secret)
        body = b'{"type": "user.created"}'
        headers = MockWebhookVerifier.sign(secret=secret, body=body, event_id="msg_y")
        del headers["svix-id"]
        with pytest.raises(InvalidWebhookSignatureError, match="svix-id"):
            await verifier.verify_webhook(headers, body)

    @pytest.mark.asyncio
    async def test_string_secret_accepted(self) -> None:
        # The constructor must accept a str secret as well as bytes.
        verifier = ClerkWebhookVerifier(webhook_secret="whsec_string_form")
        body = b'{"type": "user.created"}'
        headers = MockWebhookVerifier.sign(
            secret=b"whsec_string_form", body=body, event_id="msg_str"
        )
        event = await verifier.verify_webhook(headers, body)
        assert event.event_type == "user.created"


class TestSvixSecretDecoding:
    """F-AU-001: a real Clerk/Svix `whsec_<base64>` secret must be
    base64-decoded to the raw HMAC key — the old code used the literal
    string as the key, so real webhooks never verified."""

    def test_decode_strips_prefix_and_base64_decodes(self) -> None:
        import base64

        from mgf.auth._svix import _decode_svix_secret

        raw_key = b"clerk-signing-key-bytes"
        whsec = "whsec_" + base64.b64encode(raw_key).decode("ascii")
        assert _decode_svix_secret(whsec) == raw_key
        # bytes form decodes identically.
        assert _decode_svix_secret(whsec.encode()) == raw_key

    async def test_real_format_secret_round_trips(self) -> None:
        import base64

        from mgf.auth.mock import MockWebhookVerifier

        raw_key = b"another-real-key"
        whsec = "whsec_" + base64.b64encode(raw_key).decode("ascii")
        verifier = ClerkWebhookVerifier(webhook_secret=whsec)
        body = b'{"type":"user.created","data":{}}'
        headers = MockWebhookVerifier.sign(
            secret=whsec.encode(),
            body=body,
            event_id="msg_real",
        )
        event = await verifier.verify_webhook(headers, body)
        assert event.event_type == "user.created"
