"""Tests for webhook utilities."""

import json

from hax.webhooks import WebhookEvent, parse_event, sign_payload, verify_signature


class TestVerifySignature:
    """Tests for signature verification."""

    def test_valid_signature(self) -> None:
        """Test that valid signatures are accepted."""
        secret = "whsec_test_secret"
        payload = b'{"type":"request.completed","data":{}}'

        # Generate a valid signature
        import hashlib
        import hmac

        expected = hmac.new(
            key=secret.encode("utf-8"),
            msg=payload,
            digestmod=hashlib.sha256,
        ).hexdigest()
        signature = f"sha256={expected}"

        assert verify_signature(payload, signature, secret) is True

    def test_invalid_signature(self) -> None:
        """Test that invalid signatures are rejected."""
        secret = "whsec_test_secret"
        payload = b'{"type":"request.completed"}'
        signature = "sha256=invalid_signature_here"

        assert verify_signature(payload, signature, secret) is False

    def test_wrong_prefix(self) -> None:
        """Test that signatures without sha256= prefix are rejected."""
        secret = "whsec_test_secret"
        payload = b'{"type":"request.completed"}'
        signature = "md5=some_hash"

        assert verify_signature(payload, signature, secret) is False

    def test_empty_signature(self) -> None:
        """Test that empty signatures are rejected."""
        secret = "whsec_test_secret"
        payload = b'{"type":"request.completed"}'
        signature = ""

        assert verify_signature(payload, signature, secret) is False

    def test_tampered_payload(self) -> None:
        """Test that tampered payloads are detected."""
        secret = "whsec_test_secret"
        original_payload = b'{"type":"request.completed","amount":100}'
        tampered_payload = b'{"type":"request.completed","amount":1000}'

        # Sign the original
        import hashlib
        import hmac

        sig_hash = hmac.new(
            key=secret.encode("utf-8"),
            msg=original_payload,
            digestmod=hashlib.sha256,
        ).hexdigest()
        signature = f"sha256={sig_hash}"

        # Try to verify with tampered payload
        assert verify_signature(tampered_payload, signature, secret) is False


class TestSignPayload:
    """Tests for payload signing."""

    def test_sign_payload(self) -> None:
        """Test that payloads are signed correctly."""
        secret = "whsec_test_secret"
        payload = {"type": "request.completed", "data": {"id": "123"}}

        signature = sign_payload(payload, secret)

        assert signature.startswith("sha256=")
        assert len(signature) == 7 + 64  # "sha256=" + 64 hex chars

    def test_sign_and_verify_roundtrip(self) -> None:
        """Test that signed payloads can be verified."""
        secret = "whsec_test_secret"
        payload = {"type": "request.completed", "data": {"id": "123"}}

        signature = sign_payload(payload, secret)

        # Convert to bytes the same way sign_payload does
        payload_bytes = json.dumps(payload, separators=(",", ":")).encode("utf-8")

        assert verify_signature(payload_bytes, signature, secret) is True

    def test_deterministic_signing(self) -> None:
        """Test that signing the same payload produces the same signature."""
        secret = "whsec_test_secret"
        payload = {"a": 1, "b": 2}

        sig1 = sign_payload(payload, secret)
        sig2 = sign_payload(payload, secret)

        assert sig1 == sig2


class TestParseEvent:
    """Tests for event parsing."""

    def test_parse_bytes(self, sample_webhook_payload: dict) -> None:
        """Test parsing event from bytes."""
        payload_bytes = json.dumps(sample_webhook_payload).encode("utf-8")

        event = parse_event(payload_bytes)

        assert event.type == "request.completed"
        assert event.timestamp == "2024-01-01T01:00:00Z"

    def test_parse_string(self, sample_webhook_payload: dict) -> None:
        """Test parsing event from string."""
        payload_str = json.dumps(sample_webhook_payload)

        event = parse_event(payload_str)

        assert event.type == "request.completed"


class TestWebhookEvent:
    """Tests for WebhookEvent model."""

    def test_event_type_property(self, sample_webhook_payload: dict) -> None:
        """Test event_type strips request. prefix."""
        event = WebhookEvent.model_validate(sample_webhook_payload)

        assert event.type == "request.completed"
        assert event.event_type == "completed"

    def test_event_type_without_prefix(self) -> None:
        """Test event_type when type doesn't have prefix."""
        event = WebhookEvent.model_validate(
            {
                "type": "custom_event",
                "timestamp": "2024-01-01T00:00:00Z",
                "data": {"id": "123"},
            }
        )

        assert event.event_type == "custom_event"

    def test_request_id_property(self, sample_webhook_payload: dict) -> None:
        """Test request_id extraction."""
        event = WebhookEvent.model_validate(sample_webhook_payload)

        assert event.request_id == "req_abc123"

    def test_request_property(self, sample_webhook_payload: dict) -> None:
        """Test request object parsing."""
        event = WebhookEvent.model_validate(sample_webhook_payload)

        request = event.request
        assert request.id == "req_abc123"
        assert request.type == "text-approval-v1"
        assert request.status.value == "completed"

    def test_metadata_property(self, sample_webhook_payload: dict) -> None:
        """Test metadata extraction."""
        event = WebhookEvent.model_validate(sample_webhook_payload)

        assert event.metadata == {"source": "test"}

    def test_response_property(self, sample_webhook_payload: dict) -> None:
        """Test response extraction."""
        event = WebhookEvent.model_validate(sample_webhook_payload)

        assert event.response == {"decision": "approve"}

    def test_response_none_for_non_completed(self) -> None:
        """Test response is None for non-completed events."""
        event = WebhookEvent.model_validate(
            {
                "type": "request.opened",
                "timestamp": "2024-01-01T00:00:00Z",
                "data": {
                    "id": "req_123",
                    "type": "text-approval-v1",
                    "status": "opened",
                },
            }
        )

        assert event.response is None

    def test_all_event_types(self) -> None:
        """Test parsing different event types."""
        event_types = ["request.sent", "request.opened", "request.completed", "request.expired"]

        for event_type in event_types:
            event = WebhookEvent.model_validate(
                {
                    "type": event_type,
                    "timestamp": "2024-01-01T00:00:00Z",
                    "data": {
                        "id": "req_123",
                        "type": "text-approval-v1",
                        "status": event_type.split(".")[-1],
                        "createdAt": "2024-01-01T00:00:00Z",
                        "updatedAt": "2024-01-01T00:00:00Z",
                        "payload": {},
                    },
                }
            )
            assert event.type == event_type
