"""Tests for the webhooks sub-resource."""

from __future__ import annotations

import httpx
import pytest

import kanoniv
from kanoniv.exceptions import NotFoundError, ValidationError


class TestWebhooksCreate:
    def test_create_webhook(self, mock_api, client):
        mock_api.post("/v1/outbound-webhooks").mock(
            return_value=httpx.Response(
                201,
                json={
                    "id": "wh-abc",
                    "url": "https://example.com/hook",
                    "secret": "deadbeef" * 8,
                    "event_types": ["entity.merged"],
                    "active": True,
                    "description": "Merge alerts",
                    "created_at": "2026-03-01T10:00:00Z",
                    "updated_at": "2026-03-01T10:00:00Z",
                },
            )
        )
        result = client.webhooks.create(
            url="https://example.com/hook",
            event_types=["entity.merged"],
            description="Merge alerts",
        )
        assert result["id"] == "wh-abc"
        assert result["secret"] == "deadbeef" * 8
        assert result["event_types"] == ["entity.merged"]
        assert result["description"] == "Merge alerts"

    def test_create_webhook_without_description(self, mock_api, client):
        route = mock_api.post("/v1/outbound-webhooks").mock(
            return_value=httpx.Response(
                201,
                json={"id": "wh-123", "url": "https://x.com/hook", "event_types": ["entity.created"]},
            )
        )
        client.webhooks.create(
            url="https://x.com/hook",
            event_types=["entity.created"],
        )
        import json
        body = json.loads(route.calls[0].request.content)
        assert "description" not in body

    def test_create_webhook_validation_error(self, mock_api, client):
        mock_api.post("/v1/outbound-webhooks").mock(
            return_value=httpx.Response(
                400,
                json={"detail": "Webhook URL must use HTTPS"},
            )
        )
        with pytest.raises(ValidationError):
            client.webhooks.create(
                url="http://insecure.com/hook",
                event_types=["entity.created"],
            )


class TestWebhooksList:
    def test_list_webhooks(self, mock_api, client):
        mock_api.get("/v1/outbound-webhooks").mock(
            return_value=httpx.Response(
                200,
                json=[
                    {
                        "id": "wh-1",
                        "url": "https://a.com/hook",
                        "secret_last4": "...abcd",
                        "event_types": ["entity.merged"],
                        "active": True,
                    },
                    {
                        "id": "wh-2",
                        "url": "https://b.com/hook",
                        "secret_last4": "...efgh",
                        "event_types": ["reconciliation.completed"],
                        "active": True,
                    },
                ],
            )
        )
        result = client.webhooks.list()
        assert len(result) == 2
        assert result[0]["id"] == "wh-1"
        assert result[1]["secret_last4"] == "...efgh"

    def test_list_webhooks_empty(self, mock_api, client):
        mock_api.get("/v1/outbound-webhooks").mock(
            return_value=httpx.Response(200, json=[])
        )
        result = client.webhooks.list()
        assert result == []


class TestWebhooksGet:
    def test_get_webhook(self, mock_api, client):
        mock_api.get("/v1/outbound-webhooks/wh-1").mock(
            return_value=httpx.Response(
                200,
                json={
                    "id": "wh-1",
                    "url": "https://a.com/hook",
                    "secret_last4": "...abcd",
                    "event_types": ["entity.merged"],
                    "active": True,
                },
            )
        )
        result = client.webhooks.get("wh-1")
        assert result["id"] == "wh-1"
        assert result["url"] == "https://a.com/hook"

    def test_get_webhook_not_found(self, mock_api, client):
        mock_api.get("/v1/outbound-webhooks/nonexistent").mock(
            return_value=httpx.Response(404, json={"detail": "Webhook not found"})
        )
        with pytest.raises(NotFoundError):
            client.webhooks.get("nonexistent")


class TestWebhooksDelete:
    def test_delete_webhook(self, mock_api, client):
        mock_api.delete("/v1/outbound-webhooks/wh-1").mock(
            return_value=httpx.Response(204)
        )
        result = client.webhooks.delete("wh-1")
        assert result is None

    def test_delete_webhook_not_found(self, mock_api, client):
        mock_api.delete("/v1/outbound-webhooks/wh-bad").mock(
            return_value=httpx.Response(404, json={"detail": "Webhook not found"})
        )
        with pytest.raises(NotFoundError):
            client.webhooks.delete("wh-bad")


class TestWebhooksTest:
    def test_send_test_event(self, mock_api, client):
        mock_api.post("/v1/outbound-webhooks/wh-1/test").mock(
            return_value=httpx.Response(
                200,
                json={
                    "success": True,
                    "http_status": 200,
                    "delivery_id": "del-abc",
                },
            )
        )
        result = client.webhooks.test("wh-1")
        assert result["success"] is True
        assert result["http_status"] == 200

    def test_send_test_event_endpoint_down(self, mock_api, client):
        mock_api.post("/v1/outbound-webhooks/wh-1/test").mock(
            return_value=httpx.Response(
                200,
                json={
                    "success": False,
                    "error": "Connection refused",
                    "delivery_id": "del-xyz",
                },
            )
        )
        result = client.webhooks.test("wh-1")
        assert result["success"] is False
        assert "Connection refused" in result["error"]


class TestWebhooksDeliveries:
    def test_list_deliveries(self, mock_api, client):
        mock_api.get("/v1/outbound-webhooks/wh-1/deliveries").mock(
            return_value=httpx.Response(
                200,
                json=[
                    {
                        "id": "del-1",
                        "webhook_id": "wh-1",
                        "event_type": "entity.merged",
                        "status": "delivered",
                        "http_status": 200,
                        "attempts": 1,
                        "max_attempts": 3,
                    },
                    {
                        "id": "del-2",
                        "webhook_id": "wh-1",
                        "event_type": "entity.created",
                        "status": "failed",
                        "http_status": 500,
                        "attempts": 3,
                        "max_attempts": 3,
                        "last_error": "HTTP 500",
                    },
                ],
            )
        )
        result = client.webhooks.deliveries("wh-1")
        assert len(result) == 2
        assert result[0]["status"] == "delivered"
        assert result[1]["status"] == "failed"
        assert result[1]["last_error"] == "HTTP 500"

    def test_list_deliveries_with_pagination(self, mock_api, client):
        route = mock_api.get("/v1/outbound-webhooks/wh-1/deliveries").mock(
            return_value=httpx.Response(200, json=[])
        )
        client.webhooks.deliveries("wh-1", limit=10, offset=20)
        req_url = str(route.calls[0].request.url)
        assert "limit=10" in req_url
        assert "offset=20" in req_url


class TestWebhooksAsync:
    @pytest.mark.asyncio
    async def test_async_create(self, mock_api, async_client):
        mock_api.post("/v1/outbound-webhooks").mock(
            return_value=httpx.Response(
                201,
                json={"id": "wh-async", "url": "https://a.com/hook", "event_types": ["entity.merged"]},
            )
        )
        result = await async_client.webhooks.create(
            url="https://a.com/hook",
            event_types=["entity.merged"],
        )
        assert result["id"] == "wh-async"
        await async_client.close()

    @pytest.mark.asyncio
    async def test_async_list(self, mock_api, async_client):
        mock_api.get("/v1/outbound-webhooks").mock(
            return_value=httpx.Response(200, json=[{"id": "wh-1"}])
        )
        result = await async_client.webhooks.list()
        assert len(result) == 1
        await async_client.close()

    @pytest.mark.asyncio
    async def test_async_delete(self, mock_api, async_client):
        mock_api.delete("/v1/outbound-webhooks/wh-1").mock(
            return_value=httpx.Response(204)
        )
        result = await async_client.webhooks.delete("wh-1")
        assert result is None
        await async_client.close()

    @pytest.mark.asyncio
    async def test_async_deliveries(self, mock_api, async_client):
        mock_api.get("/v1/outbound-webhooks/wh-1/deliveries").mock(
            return_value=httpx.Response(200, json=[{"id": "del-1", "status": "delivered"}])
        )
        result = await async_client.webhooks.deliveries("wh-1")
        assert result[0]["status"] == "delivered"
        await async_client.close()
