"""Tests for the WebhookChannel adapter."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

import pytest

from openjarvis.channels._stubs import ChannelStatus
from openjarvis.channels.webhook import WebhookChannel
from openjarvis.core.events import EventBus, EventType
from openjarvis.core.registry import ChannelRegistry


@pytest.fixture(autouse=True)
def _register_webhook():
    """Re-register after any registry clear."""
    if not ChannelRegistry.contains("webhook"):
        ChannelRegistry.register_value("webhook", WebhookChannel)


class TestRegistration:
    def test_registry_key(self):
        assert ChannelRegistry.contains("webhook")

    def test_channel_id(self):
        ch = WebhookChannel(url="https://example.com/hook")
        assert ch.channel_id == "webhook"


class TestInit:
    def test_defaults(self):
        ch = WebhookChannel()
        assert ch._url == ""
        assert ch._secret == ""
        assert ch._method == "POST"
        assert ch._status == ChannelStatus.DISCONNECTED

    def test_constructor_params(self):
        ch = WebhookChannel(
            url="https://example.com/hook",
            secret="s3cr3t",
            method="PUT",
        )
        assert ch._url == "https://example.com/hook"
        assert ch._secret == "s3cr3t"
        assert ch._method == "PUT"


class TestConnect:
    def test_connect_with_url(self):
        ch = WebhookChannel(url="https://example.com/hook")
        ch.connect()
        assert ch.status() == ChannelStatus.CONNECTED

    def test_connect_no_url(self):
        ch = WebhookChannel()
        ch.connect()
        assert ch.status() == ChannelStatus.ERROR


class TestSend:
    def test_send_success(self):
        ch = WebhookChannel(url="https://example.com/hook")

        mock_response = MagicMock()
        mock_response.status_code = 200

        with patch("httpx.request", return_value=mock_response) as mock_req:
            result = ch.send("target", "Hello!")
            assert result is True
            mock_req.assert_called_once()
            call_args = mock_req.call_args
            assert call_args[0][0] == "POST"
            assert call_args[0][1] == "https://example.com/hook"
            payload = call_args[1]["json"]
            assert payload["channel"] == "target"
            assert payload["content"] == "Hello!"

    def test_send_put_method(self):
        ch = WebhookChannel(url="https://example.com/hook", method="PUT")

        mock_response = MagicMock()
        mock_response.status_code = 200

        with patch("httpx.request", return_value=mock_response) as mock_req:
            ch.send("target", "Hello!")
            assert mock_req.call_args[0][0] == "PUT"

    def test_send_with_secret(self):
        ch = WebhookChannel(url="https://example.com/hook", secret="s3cr3t")

        mock_response = MagicMock()
        mock_response.status_code = 200

        with patch("httpx.request", return_value=mock_response) as mock_req:
            ch.send("target", "Hello!")
            headers = mock_req.call_args[1]["headers"]
            assert headers["X-Webhook-Secret"] == "s3cr3t"

    def test_send_without_secret(self):
        ch = WebhookChannel(url="https://example.com/hook")

        mock_response = MagicMock()
        mock_response.status_code = 200

        with patch("httpx.request", return_value=mock_response) as mock_req:
            ch.send("target", "Hello!")
            headers = mock_req.call_args[1]["headers"]
            assert "X-Webhook-Secret" not in headers

    def test_send_with_metadata(self):
        ch = WebhookChannel(url="https://example.com/hook")

        mock_response = MagicMock()
        mock_response.status_code = 200

        with patch("httpx.request", return_value=mock_response) as mock_req:
            ch.send("target", "Hello!", metadata={"key": "value"})
            payload = mock_req.call_args[1]["json"]
            assert payload["metadata"] == {"key": "value"}

    def test_send_failure(self):
        ch = WebhookChannel(url="https://example.com/hook")

        mock_response = MagicMock()
        mock_response.status_code = 500

        with patch("httpx.request", return_value=mock_response):
            result = ch.send("target", "Hello!")
            assert result is False

    def test_send_exception(self):
        ch = WebhookChannel(url="https://example.com/hook")

        with patch("httpx.request", side_effect=ConnectionError("refused")):
            result = ch.send("target", "Hello!")
            assert result is False

    def test_send_no_url(self):
        ch = WebhookChannel()
        result = ch.send("target", "Hello!")
        assert result is False

    def test_send_publishes_event(self):
        bus = EventBus(record_history=True)
        ch = WebhookChannel(url="https://example.com/hook", bus=bus)

        mock_response = MagicMock()
        mock_response.status_code = 200

        with patch("httpx.request", return_value=mock_response):
            ch.send("target", "Hello!")

        event_types = [e.event_type for e in bus.history]
        assert EventType.CHANNEL_MESSAGE_SENT in event_types


class TestListChannels:
    def test_list_channels(self):
        ch = WebhookChannel(url="https://example.com/hook")
        assert ch.list_channels() == ["webhook"]


class TestStatus:
    def test_disconnected_initially(self):
        ch = WebhookChannel(url="https://example.com/hook")
        assert ch.status() == ChannelStatus.DISCONNECTED


class TestOnMessage:
    def test_on_message(self):
        ch = WebhookChannel(url="https://example.com/hook")
        handler = MagicMock()
        ch.on_message(handler)
        assert handler in ch._handlers


class TestDisconnect:
    def test_disconnect(self):
        ch = WebhookChannel(url="https://example.com/hook")
        ch._status = ChannelStatus.CONNECTED
        ch.disconnect()
        assert ch.status() == ChannelStatus.DISCONNECTED
