"""Tests for incoming webhooks module."""

from __future__ import annotations

import hashlib
import hmac
import json
from unittest.mock import AsyncMock, MagicMock

import pytest
from fastapi import FastAPI, HTTPException, Request
from httpx import AsyncClient

from oclawma.queue import JobPriority
from oclawma.webhooks import (
    WebhookEndpoint,
    WebhookManager,
    WebhookPayloadTransformer,
    WebhookVerifier,
    create_webhook_router,
    setup_webhooks,
)

# =============================================================================
# Fixtures
# =============================================================================


@pytest.fixture
def sample_webhook_endpoint() -> WebhookEndpoint:
    """Provide a sample webhook endpoint."""
    return WebhookEndpoint(
        path="test-webhook",
        secret="test-secret-key-12345",
        job_type="test_job",
        provider="generic",
        priority=JobPriority.HIGH,
    )


@pytest.fixture
def github_webhook_endpoint() -> WebhookEndpoint:
    """Provide a GitHub-style webhook endpoint."""
    return WebhookEndpoint(
        path="github",
        secret="github-webhook-secret",
        job_type="github_event",
        provider="github",
        priority=JobPriority.NORMAL,
    )


@pytest.fixture
def stripe_webhook_endpoint() -> WebhookEndpoint:
    """Provide a Stripe-style webhook endpoint."""
    return WebhookEndpoint(
        path="stripe",
        secret="stripe-webhook-secret",
        job_type="stripe_event",
        provider="stripe",
        priority=JobPriority.HIGH,
    )


@pytest.fixture
def mock_job_queue() -> MagicMock:
    """Provide a mock job queue."""
    queue = MagicMock()
    mock_job = MagicMock()
    mock_job.id = 123
    # Make enqueue actually return the mock job
    queue.enqueue.return_value = mock_job
    # Support both sync and async usage
    queue.enqueue.__await__ = MagicMock(return_value=iter([mock_job]))
    return queue


@pytest.fixture
def webhook_manager(mock_job_queue: MagicMock) -> WebhookManager:
    """Provide a webhook manager with mock queue."""
    return WebhookManager(job_queue=mock_job_queue)


@pytest.fixture
def sample_payload() -> dict:
    """Provide a sample webhook payload."""
    return {
        "event": "test.event",
        "data": {"key": "value"},
        "timestamp": "2024-01-15T10:30:00Z",
    }


# =============================================================================
# WebhookEndpoint Tests
# =============================================================================


class TestWebhookEndpoint:
    """Tests for WebhookEndpoint class."""

    def test_valid_endpoint_creation(self) -> None:
        """Test creating a valid webhook endpoint."""
        endpoint = WebhookEndpoint(
            path="test-path",
            secret="my-secret",
            job_type="my_job",
            provider="generic",
        )

        assert endpoint.path == "test-path"
        assert endpoint.secret == "my-secret"
        assert endpoint.job_type == "my_job"
        assert endpoint.provider == "generic"
        assert endpoint.priority == JobPriority.NORMAL
        assert endpoint.enabled is True

    def test_endpoint_validation_empty_path(self) -> None:
        """Test that empty path raises ValueError."""
        with pytest.raises(ValueError, match="Webhook path cannot be empty"):
            WebhookEndpoint(path="", secret="secret")

    def test_endpoint_validation_empty_secret(self) -> None:
        """Test that empty secret raises ValueError."""
        with pytest.raises(ValueError, match="Webhook secret cannot be empty"):
            WebhookEndpoint(path="path", secret="")

    def test_endpoint_validation_invalid_provider(self) -> None:
        """Test that invalid provider raises ValueError."""
        with pytest.raises(ValueError, match="Unsupported provider: invalid"):
            WebhookEndpoint(path="path", secret="secret", provider="invalid")

    def test_endpoint_to_dict(self) -> None:
        """Test converting endpoint to dictionary."""
        endpoint = WebhookEndpoint(
            path="test",
            secret="secret",
            job_type="job",
            provider="github",
            priority=JobPriority.HIGH,
            metadata={"key": "value"},
        )

        data = endpoint.to_dict()

        assert data["path"] == "test"
        assert data["secret"] == "secret"
        assert data["job_type"] == "job"
        assert data["provider"] == "github"
        assert data["priority"] == JobPriority.HIGH.value
        assert data["metadata"] == {"key": "value"}

    def test_endpoint_from_dict(self) -> None:
        """Test creating endpoint from dictionary."""
        data = {
            "path": "test",
            "secret": "secret",
            "job_type": "job",
            "provider": "github",
            "priority": 50,
            "enabled": False,
            "metadata": {"key": "value"},
        }

        endpoint = WebhookEndpoint.from_dict(data)

        assert endpoint.path == "test"
        assert endpoint.secret == "secret"
        assert endpoint.job_type == "job"
        assert endpoint.provider == "github"
        assert endpoint.priority == JobPriority.HIGH
        assert endpoint.enabled is False
        assert endpoint.metadata == {"key": "value"}

    def test_endpoint_from_dict_with_enum_priority(self) -> None:
        """Test creating endpoint from dict with JobPriority enum."""
        data = {
            "path": "test",
            "secret": "secret",
            "priority": JobPriority.CRITICAL,
        }

        endpoint = WebhookEndpoint.from_dict(data)
        assert endpoint.priority == JobPriority.CRITICAL


# =============================================================================
# WebhookVerifier Tests
# =============================================================================


class TestWebhookVerifier:
    """Tests for WebhookVerifier class."""

    def test_verify_github_signature_valid(self) -> None:
        """Test valid GitHub signature verification."""
        payload = b'{"action": "opened"}'
        secret = "my-secret"
        expected_sig = hmac.new(
            secret.encode(),
            payload,
            hashlib.sha256,
        ).hexdigest()

        # With sha256= prefix
        signature = f"sha256={expected_sig}"
        assert WebhookVerifier.verify_github(payload, signature, secret) is True

        # Without prefix
        assert WebhookVerifier.verify_github(payload, expected_sig, secret) is True

    def test_verify_github_signature_invalid(self) -> None:
        """Test invalid GitHub signature verification."""
        payload = b'{"action": "opened"}'
        secret = "my-secret"
        wrong_sig = "invalid-signature"

        assert WebhookVerifier.verify_github(payload, wrong_sig, secret) is False

    def test_verify_github_signature_missing(self) -> None:
        """Test missing GitHub signature."""
        payload = b'{"action": "opened"}'
        secret = "my-secret"

        assert WebhookVerifier.verify_github(payload, "", secret) is False
        assert WebhookVerifier.verify_github(payload, None, secret) is False

    def test_verify_stripe_signature_valid(self) -> None:
        """Test valid Stripe signature verification."""
        payload = b'{"type": "charge.succeeded"}'
        secret = "whsec_test_secret"
        timestamp = "1705315200"

        signed_payload = f"{timestamp}.{payload.decode()}"
        expected_sig = hmac.new(
            secret.encode(),
            signed_payload.encode(),
            hashlib.sha256,
        ).hexdigest()

        signature = f"t={timestamp},v1={expected_sig}"
        assert WebhookVerifier.verify_stripe(payload, signature, secret) is True

    def test_verify_stripe_signature_multiple_v1(self) -> None:
        """Test Stripe signature with multiple v1 signatures."""
        payload = b'{"type": "charge.succeeded"}'
        secret = "whsec_test_secret"
        timestamp = "1705315200"

        signed_payload = f"{timestamp}.{payload.decode()}"
        expected_sig = hmac.new(
            secret.encode(),
            signed_payload.encode(),
            hashlib.sha256,
        ).hexdigest()

        # Multiple signatures, one valid
        signature = f"t={timestamp},v1=invalid,v1={expected_sig}"
        assert WebhookVerifier.verify_stripe(payload, signature, secret) is True

    def test_verify_stripe_signature_invalid_format(self) -> None:
        """Test Stripe signature with invalid format."""
        payload = b'{"type": "charge.succeeded"}'
        secret = "whsec_test_secret"

        assert WebhookVerifier.verify_stripe(payload, "invalid-format", secret) is False
        assert WebhookVerifier.verify_stripe(payload, "", secret) is False

    def test_verify_hmac_signature_valid(self) -> None:
        """Test valid HMAC signature verification."""
        payload = b'{"data": "test"}'
        secret = "my-secret"
        expected_sig = hmac.new(
            secret.encode(),
            payload,
            hashlib.sha256,
        ).hexdigest()

        assert WebhookVerifier.verify_hmac(payload, expected_sig, secret) is True

    def test_verify_hmac_signature_base64(self) -> None:
        """Test HMAC signature with base64 encoding."""
        import base64

        payload = b'{"data": "test"}'
        secret = "my-secret"
        expected_sig = hmac.new(
            secret.encode(),
            payload,
            hashlib.sha256,
        ).digest()
        base64_sig = base64.b64encode(expected_sig).decode()

        assert WebhookVerifier.verify_hmac(payload, base64_sig, secret) is True

    def test_verify_hmac_signature_invalid(self) -> None:
        """Test invalid HMAC signature."""
        payload = b'{"data": "test"}'
        secret = "my-secret"

        assert WebhookVerifier.verify_hmac(payload, "wrong-sig", secret) is False

    def test_verify_hmac_custom_algorithm(self) -> None:
        """Test HMAC with custom algorithm."""
        payload = b'{"data": "test"}'
        secret = "my-secret"
        expected_sig = hmac.new(
            secret.encode(),
            payload,
            hashlib.sha512,
        ).hexdigest()

        assert WebhookVerifier.verify_hmac(payload, expected_sig, secret, "sha512") is True

    def test_verify_hmac_unsupported_algorithm(self) -> None:
        """Test HMAC with unsupported algorithm."""
        payload = b'{"data": "test"}'
        secret = "my-secret"

        assert WebhookVerifier.verify_hmac(payload, "sig", secret, "invalid") is False


# =============================================================================
# WebhookPayloadTransformer Tests
# =============================================================================


class TestWebhookPayloadTransformer:
    """Tests for WebhookPayloadTransformer class."""

    def test_transform_generic_payload(self) -> None:
        """Test transforming generic payload."""
        endpoint = WebhookEndpoint(
            path="generic",
            secret="secret",
            provider="generic",
        )
        payload = {"key": "value", "timestamp": "2024-01-15T10:30:00Z"}

        result = WebhookPayloadTransformer.transform(payload, endpoint)

        assert result["webhook"]["path"] == "generic"
        assert result["webhook"]["provider"] == "generic"
        assert result["data"] == payload

    def test_transform_github_payload(self) -> None:
        """Test transforming GitHub payload."""
        endpoint = WebhookEndpoint(
            path="github",
            secret="secret",
            provider="github",
        )
        payload = {
            "action": "opened",
            "repository": {"full_name": "user/repo"},
            "sender": {"login": "username"},
        }

        result = WebhookPayloadTransformer.transform(payload, endpoint)

        assert result["webhook"]["path"] == "github"
        assert result["webhook"]["provider"] == "github"
        assert result["webhook"]["event"] == "opened"
        assert result["webhook"]["repository"] == "user/repo"
        assert result["webhook"]["sender"] == "username"

    def test_transform_stripe_payload(self) -> None:
        """Test transforming Stripe payload."""
        endpoint = WebhookEndpoint(
            path="stripe",
            secret="secret",
            provider="stripe",
        )
        payload = {
            "type": "charge.succeeded",
            "data": {"object": {"id": "ch_12345"}},
        }

        result = WebhookPayloadTransformer.transform(payload, endpoint)

        assert result["webhook"]["path"] == "stripe"
        assert result["webhook"]["provider"] == "stripe"
        assert result["webhook"]["event"] == "charge.succeeded"
        assert result["webhook"]["object_id"] == "ch_12345"

    def test_transform_with_metadata(self) -> None:
        """Test transforming payload with endpoint metadata."""
        endpoint = WebhookEndpoint(
            path="test",
            secret="secret",
            metadata={"source": "external", "version": "1.0"},
        )
        payload = {"key": "value"}

        result = WebhookPayloadTransformer.transform(payload, endpoint)

        assert result["webhook"]["metadata"] == {"source": "external", "version": "1.0"}


# =============================================================================
# WebhookManager Tests
# =============================================================================


class TestWebhookManager:
    """Tests for WebhookManager class."""

    def test_register_endpoint(self, webhook_manager: WebhookManager) -> None:
        """Test registering an endpoint."""
        endpoint = WebhookEndpoint(path="test", secret="secret")

        webhook_manager.register_endpoint(endpoint)

        assert "test" in webhook_manager.endpoints
        assert webhook_manager.endpoints["test"] == endpoint

    def test_register_duplicate_endpoint(self, webhook_manager: WebhookManager) -> None:
        """Test registering duplicate endpoint raises error."""
        endpoint = WebhookEndpoint(path="test", secret="secret")
        webhook_manager.register_endpoint(endpoint)

        with pytest.raises(ValueError, match="Endpoint 'test' is already registered"):
            webhook_manager.register_endpoint(endpoint)

    def test_unregister_endpoint(self, webhook_manager: WebhookManager) -> None:
        """Test unregistering an endpoint."""
        endpoint = WebhookEndpoint(path="test", secret="secret")
        webhook_manager.register_endpoint(endpoint)

        result = webhook_manager.unregister_endpoint("test")

        assert result is True
        assert "test" not in webhook_manager.endpoints

    def test_unregister_nonexistent_endpoint(self, webhook_manager: WebhookManager) -> None:
        """Test unregistering nonexistent endpoint."""
        result = webhook_manager.unregister_endpoint("nonexistent")
        assert result is False

    def test_get_endpoint(self, webhook_manager: WebhookManager) -> None:
        """Test getting an endpoint."""
        endpoint = WebhookEndpoint(path="test", secret="secret")
        webhook_manager.register_endpoint(endpoint)

        result = webhook_manager.get_endpoint("test")

        assert result == endpoint

    def test_get_endpoint_not_found(self, webhook_manager: WebhookManager) -> None:
        """Test getting nonexistent endpoint returns None."""
        result = webhook_manager.get_endpoint("nonexistent")
        assert result is None

    def test_list_endpoints(self, webhook_manager: WebhookManager) -> None:
        """Test listing endpoints."""
        endpoint1 = WebhookEndpoint(path="test1", secret="secret")
        endpoint2 = WebhookEndpoint(path="test2", secret="secret")
        webhook_manager.register_endpoint(endpoint1)
        webhook_manager.register_endpoint(endpoint2)

        result = webhook_manager.list_endpoints()

        assert len(result) == 2
        assert endpoint1 in result
        assert endpoint2 in result

    def test_verify_signature_generic(self, webhook_manager: WebhookManager) -> None:
        """Test verifying generic HMAC signature."""
        endpoint = WebhookEndpoint(path="test", secret="secret", provider="generic")
        payload = b'{"data": "test"}'
        expected_sig = hmac.new(
            b"secret",
            payload,
            hashlib.sha256,
        ).hexdigest()

        result = webhook_manager.verify_signature(endpoint, payload, expected_sig)

        assert result is True

    def test_verify_signature_github(self, webhook_manager: WebhookManager) -> None:
        """Test verifying GitHub signature."""
        endpoint = WebhookEndpoint(path="github", secret="secret", provider="github")
        payload = b'{"action": "opened"}'
        expected_sig = hmac.new(
            b"secret",
            payload,
            hashlib.sha256,
        ).hexdigest()

        result = webhook_manager.verify_signature(endpoint, payload, f"sha256={expected_sig}")

        assert result is True

    @pytest.mark.asyncio
    async def test_handle_webhook_success(
        self,
        webhook_manager: WebhookManager,
        sample_webhook_endpoint: WebhookEndpoint,
    ) -> None:
        """Test successful webhook handling."""
        webhook_manager.register_endpoint(sample_webhook_endpoint)

        payload = {"event": "test", "data": "value"}
        payload_bytes = json.dumps(payload).encode()
        signature = hmac.new(
            sample_webhook_endpoint.secret.encode(),
            payload_bytes,
            hashlib.sha256,
        ).hexdigest()

        # Mock request with headers dict that supports lowercase lookup
        mock_request = MagicMock(spec=Request)
        mock_request.body = AsyncMock(return_value=payload_bytes)
        mock_request.headers = {"x-webhook-signature": signature}

        result = await webhook_manager.handle_webhook("test-webhook", mock_request)

        assert result["status"] == "accepted"
        assert result["path"] == "test-webhook"
        assert result["job_id"] == 123

        # Verify job was enqueued
        webhook_manager.job_queue.enqueue.assert_called_once()
        call_args = webhook_manager.job_queue.enqueue.call_args
        assert call_args.kwargs["priority"] == JobPriority.HIGH
        assert call_args.kwargs["job_type"] == "test_job"

    @pytest.mark.asyncio
    async def test_handle_webhook_endpoint_not_found(
        self,
        webhook_manager: WebhookManager,
    ) -> None:
        """Test handling webhook for nonexistent endpoint."""
        mock_request = MagicMock(spec=Request)
        mock_request.headers = {}

        with pytest.raises(HTTPException) as exc_info:
            await webhook_manager.handle_webhook("nonexistent", mock_request)

        assert exc_info.value.status_code == 404

    @pytest.mark.asyncio
    async def test_handle_webhook_disabled_endpoint(
        self,
        webhook_manager: WebhookManager,
    ) -> None:
        """Test handling webhook for disabled endpoint."""
        endpoint = WebhookEndpoint(path="disabled", secret="secret", enabled=False)
        webhook_manager.register_endpoint(endpoint)

        mock_request = MagicMock(spec=Request)
        mock_request.headers = {}

        with pytest.raises(HTTPException) as exc_info:
            await webhook_manager.handle_webhook("disabled", mock_request)

        assert exc_info.value.status_code == 503

    @pytest.mark.asyncio
    async def test_handle_webhook_invalid_signature(
        self,
        webhook_manager: WebhookManager,
        sample_webhook_endpoint: WebhookEndpoint,
    ) -> None:
        """Test handling webhook with invalid signature."""
        webhook_manager.register_endpoint(sample_webhook_endpoint)

        payload = {"event": "test"}
        payload_bytes = json.dumps(payload).encode()

        mock_request = MagicMock(spec=Request)
        mock_request.body = AsyncMock(return_value=payload_bytes)
        mock_request.headers = {"x-webhook-signature": "invalid-signature"}

        with pytest.raises(HTTPException) as exc_info:
            await webhook_manager.handle_webhook("test-webhook", mock_request)

        assert exc_info.value.status_code == 401

    @pytest.mark.asyncio
    async def test_handle_webhook_invalid_json(
        self,
        webhook_manager: WebhookManager,
        sample_webhook_endpoint: WebhookEndpoint,
    ) -> None:
        """Test handling webhook with invalid JSON payload."""
        webhook_manager.register_endpoint(sample_webhook_endpoint)

        # Create valid signature for invalid JSON
        payload_bytes = b"not valid json"
        signature = hmac.new(
            sample_webhook_endpoint.secret.encode(),
            payload_bytes,
            hashlib.sha256,
        ).hexdigest()

        mock_request = MagicMock(spec=Request)
        mock_request.body = AsyncMock(return_value=payload_bytes)
        mock_request.headers = {"x-webhook-signature": signature}

        with pytest.raises(HTTPException) as exc_info:
            await webhook_manager.handle_webhook("test-webhook", mock_request)

        assert exc_info.value.status_code == 400

    @pytest.mark.asyncio
    async def test_handle_webhook_no_queue(
        self,
        sample_webhook_endpoint: WebhookEndpoint,
    ) -> None:
        """Test handling webhook without job queue."""
        manager = WebhookManager(job_queue=None)
        manager.register_endpoint(sample_webhook_endpoint)

        payload = {"event": "test"}
        payload_bytes = json.dumps(payload).encode()
        signature = hmac.new(
            sample_webhook_endpoint.secret.encode(),
            payload_bytes,
            hashlib.sha256,
        ).hexdigest()

        mock_request = MagicMock(spec=Request)
        mock_request.body = AsyncMock(return_value=payload_bytes)
        mock_request.headers = {"x-webhook-signature": signature}

        result = await manager.handle_webhook("test-webhook", mock_request)

        assert result["status"] == "accepted"
        assert result["job_id"] is None
        assert "message" in result


# =============================================================================
# Integration Tests
# =============================================================================


@pytest.fixture
def app(mock_job_queue: MagicMock) -> FastAPI:
    """Create a FastAPI app with webhook router."""
    app = FastAPI()
    manager = WebhookManager(job_queue=mock_job_queue)

    # Register some test endpoints
    manager.register_endpoint(
        WebhookEndpoint(path="generic", secret="generic-secret", provider="generic")
    )
    manager.register_endpoint(
        WebhookEndpoint(path="github", secret="github-secret", provider="github")
    )

    router = create_webhook_router(manager)
    app.include_router(router)

    return app


@pytest.mark.asyncio
async def test_webhook_endpoint_integration(app: FastAPI) -> None:
    """Test webhook endpoint through FastAPI."""
    from httpx import ASGITransport

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        payload = {"event": "test"}
        payload_bytes = json.dumps(payload, separators=(",", ":")).encode()
        signature = hmac.new(
            b"generic-secret",
            payload_bytes,
            hashlib.sha256,
        ).hexdigest()

        response = await client.post(
            "/webhooks/generic",
            content=payload_bytes,
            headers={
                "Content-Type": "application/json",
                "X-Webhook-Signature": signature,
            },
        )

        assert response.status_code == 202
        data = response.json()
        assert data["status"] == "accepted"
        assert data["path"] == "generic"


@pytest.mark.asyncio
async def test_webhook_github_integration(app: FastAPI) -> None:
    """Test GitHub webhook endpoint through FastAPI."""
    from httpx import ASGITransport

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        payload = {"action": "opened", "repository": {"full_name": "user/repo"}}
        payload_bytes = json.dumps(payload, separators=(",", ":")).encode()
        signature = hmac.new(
            b"github-secret",
            payload_bytes,
            hashlib.sha256,
        ).hexdigest()

        response = await client.post(
            "/webhooks/github",
            content=payload_bytes,
            headers={
                "Content-Type": "application/json",
                "X-Hub-Signature-256": f"sha256={signature}",
            },
        )

        assert response.status_code == 202


@pytest.mark.asyncio
async def test_webhook_list_endpoints(app: FastAPI) -> None:
    """Test listing webhook endpoints."""
    from httpx import ASGITransport

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.get("/webhooks/")

        assert response.status_code == 200
        data = response.json()
        assert "endpoints" in data
        assert len(data["endpoints"]) == 2


@pytest.mark.asyncio
async def test_webhook_invalid_signature_integration(app: FastAPI) -> None:
    """Test webhook with invalid signature."""
    from httpx import ASGITransport

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.post(
            "/webhooks/generic",
            json={"event": "test"},
            headers={"X-Webhook-Signature": "invalid"},
        )

        assert response.status_code == 401


@pytest.mark.asyncio
async def test_webhook_not_found_integration(app: FastAPI) -> None:
    """Test webhook for nonexistent endpoint."""
    from httpx import ASGITransport

    async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as client:
        response = await client.post(
            "/webhooks/nonexistent",
            json={"event": "test"},
            headers={"X-Webhook-Signature": "sig"},
        )

        assert response.status_code == 404


# =============================================================================
# Setup Webhooks Tests
# =============================================================================


class TestSetupWebhooks:
    """Tests for setup_webhooks function."""

    def test_setup_webhooks(self, mock_job_queue: MagicMock) -> None:
        """Test setting up webhooks with endpoints."""
        app = FastAPI()
        endpoints = [
            WebhookEndpoint(path="test1", secret="secret1"),
            WebhookEndpoint(path="test2", secret="secret2"),
        ]

        manager = setup_webhooks(app, job_queue=mock_job_queue, endpoints=endpoints)

        assert len(manager.endpoints) == 2
        assert "test1" in manager.endpoints
        assert "test2" in manager.endpoints

    def test_setup_webhooks_empty_list(self, mock_job_queue: MagicMock) -> None:
        """Test setting up webhooks with empty endpoint list."""
        app = FastAPI()

        manager = setup_webhooks(app, job_queue=mock_job_queue, endpoints=[])

        assert len(manager.endpoints) == 0

    def test_setup_webhooks_duplicate_paths(
        self,
        mock_job_queue: MagicMock,
        caplog,
    ) -> None:
        """Test setting up webhooks with duplicate paths logs warning."""
        import logging

        app = FastAPI()
        endpoints = [
            WebhookEndpoint(path="test", secret="secret1"),
            WebhookEndpoint(path="test", secret="secret2"),  # Duplicate
        ]

        with caplog.at_level(logging.WARNING):
            manager = setup_webhooks(app, job_queue=mock_job_queue, endpoints=endpoints)

        # Should have registered only the first one
        assert len(manager.endpoints) == 1
        assert "Failed to register endpoint" in caplog.text

    def test_setup_webhooks_no_endpoints(self, mock_job_queue: MagicMock) -> None:
        """Test setting up webhooks without endpoints."""
        app = FastAPI()

        manager = setup_webhooks(app, job_queue=mock_job_queue)

        assert len(manager.endpoints) == 0

    def test_setup_webhooks_no_queue(self) -> None:
        """Test setting up webhooks without job queue."""
        app = FastAPI()
        endpoints = [WebhookEndpoint(path="test", secret="secret")]

        manager = setup_webhooks(app, job_queue=None, endpoints=endpoints)

        assert len(manager.endpoints) == 1
        assert manager.job_queue is None
