"""Integration tests for SqlAlchemyWebhookRepository."""

from __future__ import annotations

from datetime import UTC, datetime
from typing import TYPE_CHECKING
from uuid import UUID, uuid4

import pytest

from noteflow.domain.webhooks import (
    DeliveryResult,
    WebhookConfig,
    WebhookDelivery,
    WebhookEventType,
)
from noteflow.infrastructure.persistence.models.identity.identity import WorkspaceModel
from noteflow.infrastructure.persistence.repositories.webhook_repo import (
    SqlAlchemyWebhookRepository,
)

if TYPE_CHECKING:
    from sqlalchemy.ext.asyncio import AsyncSession

# ============================================================================
# Test Constants
# ============================================================================

# Webhook configuration values
WEBHOOK_TIMEOUT_MS = 30000
WEBHOOK_UPDATED_TIMEOUT_MS = 20000

# HTTP status codes
HTTP_OK = 200
HTTP_CREATED = 201

# Delivery test values
TEST_ATTEMPT_COUNT = 2
TEST_DURATION_MS = 200

# Repository limits
DEFAULT_DELIVERIES_LIMIT = 50


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


@pytest.fixture
def workspace_id() -> UUID:
    """Generate workspace ID for testing."""
    return uuid4()


@pytest.fixture
async def persisted_workspace(session: AsyncSession, workspace_id: UUID) -> UUID:
    """Create and persist a workspace for foreign key constraints."""
    workspace = WorkspaceModel(
        id=workspace_id,
        name="Test Workspace",
    )
    session.add(workspace)
    await session.flush()
    return workspace_id


@pytest.fixture
def webhook_repo(session: AsyncSession) -> SqlAlchemyWebhookRepository:
    """Create webhook repository instance."""
    return SqlAlchemyWebhookRepository(session)


# ============================================================================
# TestCreate
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryCreate:
    """Tests for create() method."""

    async def test_creates_webhook_with_generated_id(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Create persists webhook and returns entity with ID."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
            name="Test Webhook",
        )

        created = await webhook_repo.create(config)
        await session.commit()

        assert created.id == config.id, "ID should be preserved"
        assert created.url == "https://example.com/webhook", "URL should be preserved"
        assert created.name == "Test Webhook", "Name should be preserved"

    async def test_creates_webhook_with_multiple_events(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Create persists webhook with multiple event subscriptions."""
        events = [
            WebhookEventType.MEETING_COMPLETED,
            WebhookEventType.SUMMARY_GENERATED,
            WebhookEventType.RECORDING_STARTED,
        ]
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/multi",
            events=events,
        )

        created = await webhook_repo.create(config)
        await session.commit()

        assert len(created.events) == 3, "Should have 3 events"
        assert (
            WebhookEventType.MEETING_COMPLETED in created.events
        ), "MEETING_COMPLETED event should be in events"
        assert (
            WebhookEventType.SUMMARY_GENERATED in created.events
        ), "SUMMARY_GENERATED event should be in events"
        assert (
            WebhookEventType.RECORDING_STARTED in created.events
        ), "RECORDING_STARTED event should be in events"

    async def test_creates_webhook_with_optional_fields(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Create preserves optional secret, timeout, and retries."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/secure",
            events=[WebhookEventType.MEETING_COMPLETED],
            secret="super-secret-key",
            timeout_ms=WEBHOOK_TIMEOUT_MS,
            max_retries=5,
        )

        created = await webhook_repo.create(config)
        await session.commit()

        assert created.secret == "super-secret-key", "Secret should be preserved"
        assert created.timeout_ms == WEBHOOK_TIMEOUT_MS, "Timeout should be preserved"
        assert created.max_retries == 5, "Max retries should be preserved"

    async def test_creates_webhook_with_none_secret(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Create handles None secret correctly."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/nosecret",
            events=[WebhookEventType.MEETING_COMPLETED],
            secret=None,
        )

        created = await webhook_repo.create(config)
        await session.commit()

        assert created.secret is None, "Secret should be None"


# ============================================================================
# TestGetById
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryGetById:
    """Tests for get_by_id() method."""

    async def test_retrieves_existing_webhook(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get by ID returns webhook when found."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        retrieved = await webhook_repo.get_by_id(config.id)

        assert retrieved is not None, "Should find webhook"
        assert retrieved.id == config.id, "ID should match"
        assert retrieved.url == config.url, "URL should match"

    async def test_returns_none_for_nonexistent_webhook(
        self,
        webhook_repo: SqlAlchemyWebhookRepository,
    ) -> None:
        """Get by ID returns None when webhook does not exist."""
        nonexistent_id = uuid4()

        retrieved = await webhook_repo.get_by_id(nonexistent_id)

        assert retrieved is None, "Should return None for nonexistent webhook"

    async def test_retrieves_webhook_with_event_conversion(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get by ID converts event strings to enum frozenset."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/events",
            events=[
                WebhookEventType.MEETING_COMPLETED,
                WebhookEventType.RECORDING_STOPPED,
            ],
        )
        await webhook_repo.create(config)
        await session.commit()

        retrieved = await webhook_repo.get_by_id(config.id)

        assert retrieved is not None, "Should retrieve webhook by ID"
        assert isinstance(retrieved.events, frozenset), "Events should be frozenset"
        assert (
            WebhookEventType.MEETING_COMPLETED in retrieved.events
        ), "MEETING_COMPLETED event should be in retrieved events"
        assert (
            WebhookEventType.RECORDING_STOPPED in retrieved.events
        ), "RECORDING_STOPPED event should be in retrieved events"


# ============================================================================
# TestGetAll
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryGetAll:
    """Tests for get_all() method."""

    async def test_returns_empty_when_no_webhooks(
        self,
        webhook_repo: SqlAlchemyWebhookRepository,
    ) -> None:
        """Get all returns empty sequence when no webhooks exist."""
        result = await webhook_repo.get_all()

        assert result == [], "Should return empty list"

    async def test_get_all_returns_all_webhooks(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get all returns all webhooks regardless of enabled status."""
        config1 = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/hook1",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        config2 = WebhookConfig(
            id=uuid4(),
            workspace_id=persisted_workspace,
            url="https://example.com/hook2",
            events=frozenset([WebhookEventType.SUMMARY_GENERATED]),
            enabled=False,  # Disabled webhook
        )
        await webhook_repo.create(config1)
        await webhook_repo.create(config2)
        await session.commit()

        result = await webhook_repo.get_all()

        assert len(result) == 2, "Should return both webhooks"
        result_urls = (result[0].url, result[1].url)
        assert (
            "https://example.com/hook1" in result_urls
        ), "hook1 URL should be in results"
        assert (
            "https://example.com/hook2" in result_urls
        ), "hook2 URL should be in results"

    async def test_filters_by_workspace(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
    ) -> None:
        """Get all filters by workspace_id when provided."""
        workspace1 = uuid4()
        workspace2 = uuid4()

        # Create workspaces
        session.add(WorkspaceModel(id=workspace1, name="Workspace 1"))
        session.add(WorkspaceModel(id=workspace2, name="Workspace 2"))
        await session.flush()

        config1 = WebhookConfig.create(
            workspace_id=workspace1,
            url="https://workspace1.com/hook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        config2 = WebhookConfig.create(
            workspace_id=workspace2,
            url="https://workspace2.com/hook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config1)
        await webhook_repo.create(config2)
        await session.commit()

        result = await webhook_repo.get_all(workspace_id=workspace1)

        assert len(result) == 1, "Should return only workspace1 webhooks"
        assert (
            result[0].url == "https://workspace1.com/hook"
        ), f"URL should be workspace1 hook, got {result[0].url}"


# ============================================================================
# TestGetAllEnabled
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryGetAllEnabled:
    """Tests for get_all_enabled() method."""

    async def test_get_all_enabled_filters_disabled(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get all enabled filters out disabled webhooks."""
        enabled_config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/enabled",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        disabled_config = WebhookConfig(
            id=uuid4(),
            workspace_id=persisted_workspace,
            url="https://example.com/disabled",
            events=frozenset([WebhookEventType.MEETING_COMPLETED]),
            enabled=False,
        )
        await webhook_repo.create(enabled_config)
        await webhook_repo.create(disabled_config)
        await session.commit()

        result = await webhook_repo.get_all_enabled()

        assert len(result) == 1, "Should return only enabled webhook"
        assert (
            result[0].url == "https://example.com/enabled"
        ), f"URL should be enabled webhook, got {result[0].url}"
        assert result[0].enabled is True, "Webhook should be enabled"

    async def test_filters_enabled_by_workspace(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
    ) -> None:
        """Get all enabled filters by workspace_id when provided."""
        workspace1 = uuid4()
        workspace2 = uuid4()

        session.add(WorkspaceModel(id=workspace1, name="Workspace 1"))
        session.add(WorkspaceModel(id=workspace2, name="Workspace 2"))
        await session.flush()

        config1 = WebhookConfig.create(
            workspace_id=workspace1,
            url="https://ws1.com/hook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        config2 = WebhookConfig.create(
            workspace_id=workspace2,
            url="https://ws2.com/hook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config1)
        await webhook_repo.create(config2)
        await session.commit()

        result = await webhook_repo.get_all_enabled(workspace_id=workspace1)

        assert len(result) == 1, "Should return only workspace1 enabled webhooks"
        assert (
            result[0].workspace_id == workspace1
        ), f"Workspace ID should be {workspace1}, got {result[0].workspace_id}"

    async def test_returns_empty_when_all_disabled(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get all enabled returns empty when all webhooks are disabled."""
        disabled_config = WebhookConfig(
            id=uuid4(),
            workspace_id=persisted_workspace,
            url="https://example.com/disabled",
            events=frozenset([WebhookEventType.MEETING_COMPLETED]),
            enabled=False,
        )
        await webhook_repo.create(disabled_config)
        await session.commit()

        result = await webhook_repo.get_all_enabled()

        assert result == [], "Should return empty list"


# ============================================================================
# TestUpdate
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryUpdate:
    """Tests for update() method."""

    async def test_update_modifies_single_field(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Update modifies single field correctly."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/original",
            events=[WebhookEventType.MEETING_COMPLETED],
            name="Original Name",
        )
        await webhook_repo.create(config)
        await session.commit()

        # Create updated config with new name
        updated = WebhookConfig(
            id=config.id,
            workspace_id=persisted_workspace,
            url="https://example.com/original",
            events=frozenset([WebhookEventType.MEETING_COMPLETED]),
            name="Updated Name",
        )
        result = await webhook_repo.update(updated)
        await session.commit()

        assert result.name == "Updated Name", "Name should be updated"
        assert result.url == "https://example.com/original", "URL unchanged"

    async def test_update_repo_modifies_multiple_fields(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Update modifies multiple fields correctly."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/original",
            events=[WebhookEventType.MEETING_COMPLETED],
            timeout_ms=10000,
            max_retries=3,
        )
        await webhook_repo.create(config)
        await session.commit()

        updated = WebhookConfig(
            id=config.id,
            workspace_id=persisted_workspace,
            url="https://example.com/new-url",
            events=frozenset([WebhookEventType.SUMMARY_GENERATED]),
            name="New Name",
            timeout_ms=WEBHOOK_UPDATED_TIMEOUT_MS,
            max_retries=5,
        )
        result = await webhook_repo.update(updated)
        await session.commit()

        assert result.url == "https://example.com/new-url", "URL updated"
        assert result.name == "New Name", "Name updated"
        assert result.timeout_ms == WEBHOOK_UPDATED_TIMEOUT_MS, "Timeout updated"
        assert result.max_retries == 5, "Max retries updated"
        assert WebhookEventType.SUMMARY_GENERATED in result.events, "Events updated"

    async def test_update_replaces_events(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Update replaces event subscriptions entirely."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/events",
            events=[
                WebhookEventType.MEETING_COMPLETED,
                WebhookEventType.SUMMARY_GENERATED,
            ],
        )
        await webhook_repo.create(config)
        await session.commit()

        updated = WebhookConfig(
            id=config.id,
            workspace_id=persisted_workspace,
            url="https://example.com/events",
            events=frozenset([WebhookEventType.RECORDING_STARTED]),
        )
        result = await webhook_repo.update(updated)
        await session.commit()

        assert len(result.events) == 1, "Should have only 1 event"
        assert (
            WebhookEventType.RECORDING_STARTED in result.events
        ), "RECORDING_STARTED should be in updated events"
        assert (
            WebhookEventType.MEETING_COMPLETED not in result.events
        ), "MEETING_COMPLETED should not be in updated events"

    async def test_update_raises_for_nonexistent_webhook(
        self,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Update raises ValueError when webhook does not exist."""
        nonexistent = WebhookConfig(
            id=uuid4(),
            workspace_id=persisted_workspace,
            url="https://example.com/notfound",
            events=frozenset([WebhookEventType.MEETING_COMPLETED]),
        )

        with pytest.raises(ValueError, match="not found"):
            await webhook_repo.update(nonexistent)


# ============================================================================
# TestDelete
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryDelete:
    """Tests for delete() method."""

    async def test_delete_repo_removes_existing_webhook(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Delete removes webhook and returns True."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/delete-me",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        result = await webhook_repo.delete(config.id)
        await session.commit()

        assert result is True, "Should return True for deleted webhook"

        retrieved = await webhook_repo.get_by_id(config.id)
        assert retrieved is None, "Webhook should no longer exist"

    async def test_delete_repo_returns_false_for_nonexistent(
        self,
        webhook_repo: SqlAlchemyWebhookRepository,
    ) -> None:
        """Delete returns False when webhook does not exist."""
        nonexistent_id = uuid4()

        result = await webhook_repo.delete(nonexistent_id)

        assert result is False, "Should return False for nonexistent webhook"

    async def test_delete_cascades_to_deliveries(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Delete removes associated delivery records (cascade)."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/with-deliveries",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)

        result = DeliveryResult(status_code=200, response_body='{"ok": true}', duration_ms=150)
        delivery = WebhookDelivery.create(
            webhook_id=config.id,
            event_type=WebhookEventType.MEETING_COMPLETED,
            payload={"event": "meeting.completed"},
            result=result,
        )
        await webhook_repo.add_delivery(delivery)
        await session.commit()

        # Verify delivery exists
        deliveries_before = await webhook_repo.get_deliveries(config.id)
        assert len(deliveries_before) == 1, "Should have 1 delivery before delete"

        await webhook_repo.delete(config.id)
        await session.commit()

        # Deliveries should be cascaded
        deliveries_after = await webhook_repo.get_deliveries(config.id)
        assert deliveries_after == [], "Deliveries should be cascade deleted"


# ============================================================================
# TestAddDelivery
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryAddDelivery:
    """Tests for add_delivery() method."""

    async def test_adds_successful_delivery(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Add delivery persists successful delivery record."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        result = DeliveryResult(
            status_code=200,
            response_body='{"status": "ok"}',
            attempt_count=1,
            duration_ms=150,
        )
        delivery = WebhookDelivery.create(
            webhook_id=config.id,
            event_type=WebhookEventType.MEETING_COMPLETED,
            payload={"event": "meeting.completed", "meeting_id": str(uuid4())},
            result=result,
        )

        created = await webhook_repo.add_delivery(delivery)
        await session.commit()

        assert created.id == delivery.id, "ID should be preserved"
        assert created.status_code == HTTP_OK, "Status code should be preserved"
        assert created.succeeded is True, "Delivery should be successful"

    async def test_adds_failed_delivery(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Add delivery persists failed delivery record."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        result = DeliveryResult(
            status_code=None,
            error_message="Connection timeout",
            attempt_count=3,
            duration_ms=None,
        )
        delivery = WebhookDelivery.create(
            webhook_id=config.id,
            event_type=WebhookEventType.MEETING_COMPLETED,
            payload={"event": "meeting.completed"},
            result=result,
        )

        created = await webhook_repo.add_delivery(delivery)
        await session.commit()

        assert created.status_code is None, "Status code should be None"
        assert created.error_message == "Connection timeout", "Error message preserved"
        assert created.succeeded is False, "Delivery should be failed"


# ============================================================================
# TestGetDeliveries
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryGetDeliveries:
    """Tests for get_deliveries() method."""

    async def test_get_deliveries_returns_empty_list(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get deliveries returns empty sequence when none exist."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        result = await webhook_repo.get_deliveries(config.id)

        assert result == [], "Should return empty list"

    async def test_returns_deliveries_newest_first(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get deliveries orders by delivered_at descending (newest first)."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        # Create deliveries with explicit timestamps
        older = WebhookDelivery(
            id=uuid4(),
            webhook_id=config.id,
            event_type=WebhookEventType.MEETING_COMPLETED,
            payload={"order": "first"},
            status_code=200,
            response_body=None,
            error_message=None,
            attempt_count=1,
            duration_ms=100,
            delivered_at=datetime(2024, 1, 15, 10, 0, 0, tzinfo=UTC),
        )
        newer = WebhookDelivery(
            id=uuid4(),
            webhook_id=config.id,
            event_type=WebhookEventType.MEETING_COMPLETED,
            payload={"order": "second"},
            status_code=200,
            response_body=None,
            error_message=None,
            attempt_count=1,
            duration_ms=100,
            delivered_at=datetime(2024, 1, 15, 12, 0, 0, tzinfo=UTC),
        )
        await webhook_repo.add_delivery(older)
        await webhook_repo.add_delivery(newer)
        await session.commit()

        result = await webhook_repo.get_deliveries(config.id)

        assert len(result) == 2, "Should return 2 deliveries"
        assert result[0].payload["order"] == "second", "Newest should be first"
        assert result[1].payload["order"] == "first", "Oldest should be last"

    async def test_respects_limit_parameter(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get deliveries respects limit parameter."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        # Create 5 deliveries
        for i in range(5):
            result_data = DeliveryResult(status_code=HTTP_OK)
            delivery = WebhookDelivery.create(
                webhook_id=config.id,
                event_type=WebhookEventType.MEETING_COMPLETED,
                payload={"index": i},
                result=result_data,
            )
            await webhook_repo.add_delivery(delivery)
        await session.commit()

        result = await webhook_repo.get_deliveries(config.id, limit=3)

        assert len(result) == 3, "Should return only 3 deliveries"

    async def test_default_limit_is_50(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Get deliveries default limit is 50."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        # Create 55 deliveries
        for i in range(55):
            result_data = DeliveryResult(status_code=HTTP_OK)
            delivery = WebhookDelivery.create(
                webhook_id=config.id,
                event_type=WebhookEventType.MEETING_COMPLETED,
                payload={"index": i},
                result=result_data,
            )
            await webhook_repo.add_delivery(delivery)
        await session.commit()

        result = await webhook_repo.get_deliveries(config.id)

        assert len(result) == DEFAULT_DELIVERIES_LIMIT, "Default limit should be 50"


# ============================================================================
# TestRoundTrip
# ============================================================================


@pytest.mark.integration
class TestWebhookRepositoryRoundTrip:
    """Tests for round-trip conversion fidelity."""

    async def test_config_round_trip_preserves_all_fields(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Create and retrieve preserves all WebhookConfig fields."""
        original = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/round-trip",
            events=[
                WebhookEventType.MEETING_COMPLETED,
                WebhookEventType.SUMMARY_GENERATED,
            ],
            name="Round Trip Webhook",
            secret="round-trip-secret",
            timeout_ms=25000,
            max_retries=7,
        )

        await webhook_repo.create(original)
        await session.commit()

        retrieved = await webhook_repo.get_by_id(original.id)

        assert retrieved is not None, "Should retrieve webhook"
        assert retrieved.id == original.id, "ID preserved"
        assert retrieved.workspace_id == original.workspace_id, "Workspace ID preserved"
        assert retrieved.url == original.url, "URL preserved"
        assert retrieved.events == original.events, "Events preserved"
        assert retrieved.name == original.name, "Name preserved"
        assert retrieved.secret == original.secret, "Secret preserved"
        assert retrieved.enabled == original.enabled, "Enabled preserved"
        assert retrieved.timeout_ms == original.timeout_ms, "Timeout preserved"
        assert retrieved.max_retries == original.max_retries, "Max retries preserved"

    async def test_delivery_round_trip_preserves_all_fields(
        self,
        session: AsyncSession,
        webhook_repo: SqlAlchemyWebhookRepository,
        persisted_workspace: UUID,
    ) -> None:
        """Add and retrieve preserves all WebhookDelivery fields."""
        config = WebhookConfig.create(
            workspace_id=persisted_workspace,
            url="https://example.com/webhook",
            events=[WebhookEventType.MEETING_COMPLETED],
        )
        await webhook_repo.create(config)
        await session.commit()

        result = DeliveryResult(
            status_code=HTTP_CREATED,
            response_body='{"received": true}',
            error_message=None,
            attempt_count=TEST_ATTEMPT_COUNT,
            duration_ms=TEST_DURATION_MS,
        )
        original = WebhookDelivery.create(
            webhook_id=config.id,
            event_type=WebhookEventType.MEETING_COMPLETED,
            payload={"event": "meeting.completed", "title": "Test Meeting"},
            result=result,
        )

        await webhook_repo.add_delivery(original)
        await session.commit()

        deliveries = await webhook_repo.get_deliveries(config.id)

        assert len(deliveries) == 1, "Should have 1 delivery"
        retrieved = deliveries[0]
        assert retrieved.id == original.id, "ID preserved"
        assert retrieved.webhook_id == original.webhook_id, "Webhook ID preserved"
        assert retrieved.event_type == original.event_type, "Event type preserved"
        assert retrieved.payload == original.payload, "Payload preserved"
        assert retrieved.status_code == original.status_code, "Status code preserved"
        assert retrieved.response_body == original.response_body, "Response preserved"
        assert retrieved.attempt_count == original.attempt_count, "Attempt count preserved"
        assert retrieved.duration_ms == original.duration_ms, "Duration preserved"
