"""
Tests for Phase 32: Webhook System.
"""

import json
import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    webhooks = tmp_path / "webhooks"
    endpoints = webhooks / "endpoints"
    queue = webhooks / "queue"
    for d in (webhooks, endpoints, queue):
        d.mkdir(parents=True, exist_ok=True)

    import brynq.server as srv
    monkeypatch.setattr(srv, "BROKER_DIR", tmp_path)

    import brynq.webhooks as w
    monkeypatch.setattr(w, "WEBHOOKS_DIR", webhooks)
    monkeypatch.setattr(w, "ENDPOINTS_DIR", endpoints)
    monkeypatch.setattr(w, "QUEUE_DIR", queue)
    monkeypatch.setattr(w, "DELIVERIES_FILE", webhooks / "deliveries.jsonl")


class TestEndpoints:
    def test_register(self):
        from brynq.webhooks import register_endpoint
        ep = register_endpoint("https://example.com/hook", ["task.created"], name="Test Hook")
        assert ep["endpoint_id"]
        assert ep["url"] == "https://example.com/hook"
        assert ep["events"] == ["task.created"]

    def test_register_all_events(self):
        from brynq.webhooks import register_endpoint
        ep = register_endpoint("https://x.com/all", ["*"])
        assert ep["events"] == ["*"]

    def test_register_invalid_event(self):
        from brynq.webhooks import register_endpoint
        result = register_endpoint("https://x.com", ["fake.event"])
        assert "error" in result

    def test_register_no_url(self):
        from brynq.webhooks import register_endpoint
        result = register_endpoint("", ["task.created"])
        assert "error" in result

    def test_get_endpoint(self):
        from brynq.webhooks import register_endpoint, get_endpoint
        ep = register_endpoint("https://x.com", ["task.created"])
        result = get_endpoint(ep["endpoint_id"])
        assert result["url"] == "https://x.com"

    def test_list_endpoints(self):
        from brynq.webhooks import register_endpoint, list_endpoints
        register_endpoint("https://a.com", ["task.created"])
        register_endpoint("https://b.com", ["task.completed"])
        assert len(list_endpoints()) == 2

    def test_list_active_only(self):
        from brynq.webhooks import register_endpoint, list_endpoints
        register_endpoint("https://active.com", ["task.created"], active=True)
        register_endpoint("https://inactive.com", ["task.created"], active=False)
        active = list_endpoints(active_only=True)
        assert len(active) == 1

    def test_update_endpoint(self):
        from brynq.webhooks import register_endpoint, update_endpoint, get_endpoint
        ep = register_endpoint("https://x.com", ["task.created"])
        update_endpoint(ep["endpoint_id"], url="https://new.com", active=False)
        updated = get_endpoint(ep["endpoint_id"])
        assert updated["url"] == "https://new.com"
        assert updated["active"] is False

    def test_delete_endpoint(self):
        from brynq.webhooks import register_endpoint, delete_endpoint, get_endpoint
        ep = register_endpoint("https://x.com", ["task.created"])
        assert delete_endpoint(ep["endpoint_id"]) is True
        assert get_endpoint(ep["endpoint_id"]) is None

    def test_delete_nonexistent(self):
        from brynq.webhooks import delete_endpoint
        assert delete_endpoint("nope") is False


class TestFiring:
    def test_fire_event(self):
        from brynq.webhooks import register_endpoint, fire_event
        register_endpoint("https://x.com", ["task.created"])
        result = fire_event("task.created", {"task_id": "t1", "title": "Test"})
        assert result["endpoints_matched"] == 1
        assert result["deliveries_created"] == 1

    def test_fire_no_match(self):
        from brynq.webhooks import register_endpoint, fire_event
        register_endpoint("https://x.com", ["task.created"])
        result = fire_event("task.completed", {"task_id": "t1"})
        assert result["endpoints_matched"] == 0

    def test_fire_wildcard(self):
        from brynq.webhooks import register_endpoint, fire_event
        register_endpoint("https://x.com", ["*"])
        result = fire_event("task.completed", {"task_id": "t1"})
        assert result["endpoints_matched"] == 1

    def test_fire_invalid_event(self):
        from brynq.webhooks import fire_event
        result = fire_event("not.real", {})
        assert "error" in result

    def test_fire_multiple_endpoints(self):
        from brynq.webhooks import register_endpoint, fire_event
        register_endpoint("https://a.com", ["task.created"])
        register_endpoint("https://b.com", ["task.created"])
        register_endpoint("https://c.com", ["task.completed"])  # won't match
        result = fire_event("task.created", {"task_id": "t1"})
        assert result["endpoints_matched"] == 2


class TestSignatures:
    def test_verify_valid(self):
        from brynq.webhooks import verify_signature
        body = '{"event":"task.created"}'
        import hashlib, hmac as _hmac
        sig = _hmac.new("secret".encode(), body.encode(), hashlib.sha256).hexdigest()
        assert verify_signature(body, sig, "secret") is True

    def test_verify_invalid(self):
        from brynq.webhooks import verify_signature
        assert verify_signature("body", "badsig", "secret") is False


class TestDeliveryQueue:
    def test_pending_deliveries(self):
        from brynq.webhooks import register_endpoint, fire_event, get_pending_deliveries
        register_endpoint("https://x.com", ["task.created"])
        fire_event("task.created", {"id": "t1"})
        pending = get_pending_deliveries()
        assert len(pending) >= 1

    def test_mark_delivered(self):
        from brynq.webhooks import register_endpoint, fire_event, get_pending_deliveries, mark_delivered
        register_endpoint("https://x.com", ["task.created"])
        fire_event("task.created", {"id": "t1"})
        pending = get_pending_deliveries()
        result = mark_delivered(pending[0]["delivery_id"])
        assert result["status"] == "delivered"

    def test_mark_failed_retries(self):
        from brynq.webhooks import register_endpoint, fire_event, get_pending_deliveries, mark_failed
        register_endpoint("https://x.com", ["task.created"])
        fire_event("task.created", {"id": "t1"})
        pending = get_pending_deliveries()
        did = pending[0]["delivery_id"]
        result = mark_failed(did, "Connection timeout")
        assert result["status"] == "queued"  # Still queued (attempt 1 of 3)

    def test_mark_failed_exhausted(self):
        from brynq.webhooks import register_endpoint, fire_event, get_pending_deliveries, mark_failed
        register_endpoint("https://x.com", ["task.created"])
        fire_event("task.created", {"id": "t1"})
        pending = get_pending_deliveries()
        did = pending[0]["delivery_id"]
        mark_failed(did, "err1")
        mark_failed(did, "err2")
        result = mark_failed(did, "err3")
        assert result["status"] == "failed"

    def test_clear_completed(self):
        from brynq.webhooks import register_endpoint, fire_event, get_pending_deliveries, mark_delivered, clear_completed
        register_endpoint("https://x.com", ["task.created"])
        fire_event("task.created", {"id": "t1"})
        pending = get_pending_deliveries()
        mark_delivered(pending[0]["delivery_id"])
        removed = clear_completed()
        assert removed == 1


class TestStats:
    def test_webhook_stats(self):
        from brynq.webhooks import register_endpoint, fire_event, get_webhook_stats
        register_endpoint("https://x.com", ["task.created"])
        fire_event("task.created", {"id": "t1"})
        stats = get_webhook_stats()
        assert stats["total_endpoints"] == 1
        assert stats["total_deliveries"] == 1

    def test_empty_stats(self):
        from brynq.webhooks import get_webhook_stats
        stats = get_webhook_stats()
        assert stats["total_endpoints"] == 0

    def test_delivery_history(self):
        from brynq.webhooks import register_endpoint, fire_event, get_delivery_history
        register_endpoint("https://x.com", ["task.created"])
        fire_event("task.created", {"id": "t1"})
        history = get_delivery_history()
        assert len(history) >= 1
