"""Tests for the alerting system."""

import time
import pytest
from unittest.mock import MagicMock, patch

from horizon.alerts import (
    Alert,
    AlertManager,
    AlertType,
    DiscordChannel,
    LogChannel,
    TelegramChannel,
    WebhookChannel,
)


class TestAlert:
    def test_alert_creation(self):
        a = Alert(alert_type=AlertType.FILL, message="Filled 10 @ 0.55")
        assert a.alert_type == AlertType.FILL
        assert a.message == "Filled 10 @ 0.55"
        assert a.timestamp > 0
        assert a.data == {}

    def test_alert_with_data(self):
        a = Alert(
            alert_type=AlertType.RISK_TRIGGER,
            message="Kill switch activated",
            data={"reason": "drawdown"},
        )
        assert a.data["reason"] == "drawdown"

    def test_alert_to_dict(self):
        a = Alert(
            alert_type=AlertType.FEED_STALE,
            message="Feed stale",
            timestamp=1000.0,
            data={"feed": "btc"},
        )
        d = a.to_dict()
        assert d["type"] == "feed_stale"
        assert d["message"] == "Feed stale"
        assert d["timestamp"] == 1000.0
        assert d["data"]["feed"] == "btc"


class TestAlertType:
    def test_all_types(self):
        types = [
            AlertType.FILL,
            AlertType.RISK_TRIGGER,
            AlertType.MARKET_RESOLUTION,
            AlertType.FEED_STALE,
            AlertType.ARB_WINDOW,
            AlertType.PRICE_DEVIATION,
            AlertType.LIFECYCLE,
            AlertType.CUSTOM,
        ]
        assert len(types) == 8

    def test_value(self):
        assert AlertType.FILL.value == "fill"
        assert AlertType.CUSTOM.value == "custom"


class TestLogChannel:
    def test_send_returns_true(self):
        ch = LogChannel()
        a = Alert(alert_type=AlertType.FILL, message="test")
        assert ch.send(a) is True


class TestWebhookChannel:
    @patch("horizon.alerts.requests")
    def test_send_success(self, mock_requests):
        mock_resp = MagicMock()
        mock_resp.ok = True
        mock_requests.post.return_value = mock_resp

        ch = WebhookChannel(url="https://example.com/webhook")
        a = Alert(alert_type=AlertType.FILL, message="test")
        # WebhookChannel uses requests inside send()
        assert ch.send(a) is True

    @patch("horizon.alerts.requests")
    def test_send_failure(self, mock_requests):
        mock_requests.post.side_effect = Exception("Connection refused")
        ch = WebhookChannel(url="https://example.com/webhook")
        a = Alert(alert_type=AlertType.FILL, message="test")
        assert ch.send(a) is False


class TestTelegramChannel:
    @patch("horizon.alerts.requests")
    def test_send_success(self, mock_requests):
        mock_resp = MagicMock()
        mock_resp.ok = True
        mock_requests.post.return_value = mock_resp

        ch = TelegramChannel(bot_token="123:abc", chat_id="-100123")
        a = Alert(alert_type=AlertType.RISK_TRIGGER, message="Kill switch!")
        assert ch.send(a) is True
        mock_requests.post.assert_called_once()

    @patch("horizon.alerts.requests")
    def test_send_failure(self, mock_requests):
        mock_requests.post.side_effect = Exception("Timeout")
        ch = TelegramChannel(bot_token="123:abc", chat_id="-100123")
        a = Alert(alert_type=AlertType.FILL, message="test")
        assert ch.send(a) is False


class TestDiscordChannel:
    @patch("horizon.alerts.requests")
    def test_send_success(self, mock_requests):
        mock_resp = MagicMock()
        mock_resp.ok = True
        mock_requests.post.return_value = mock_resp

        ch = DiscordChannel(webhook_url="https://discord.com/api/webhooks/123/abc")
        a = Alert(alert_type=AlertType.FILL, message="Filled!")
        assert ch.send(a) is True

    @patch("horizon.alerts.requests")
    def test_send_failure(self, mock_requests):
        mock_requests.post.side_effect = Exception("Bad gateway")
        ch = DiscordChannel(webhook_url="https://discord.com/api/webhooks/123/abc")
        a = Alert(alert_type=AlertType.FILL, message="test")
        assert ch.send(a) is False


class TestAlertManager:
    def test_send_alert(self):
        mock_ch = MagicMock()
        mock_ch.send.return_value = True
        mgr = AlertManager(channels=[mock_ch])
        result = mgr.alert(AlertType.FILL, "Filled 10 @ 0.55")
        assert result is True
        mock_ch.send.assert_called_once()

    def test_default_log_channel(self):
        mgr = AlertManager()
        assert len(mgr.channels) == 1
        assert isinstance(mgr.channels[0], LogChannel)

    def test_throttling(self):
        mock_ch = MagicMock()
        mock_ch.send.return_value = True
        mgr = AlertManager(
            channels=[mock_ch],
            max_alerts_per_window=3,
            window_secs=60.0,
        )
        # First 3 should succeed
        assert mgr.alert(AlertType.FILL, "1") is True
        assert mgr.alert(AlertType.FILL, "2") is True
        assert mgr.alert(AlertType.FILL, "3") is True
        # 4th should be throttled
        assert mgr.alert(AlertType.FILL, "4") is False
        assert mock_ch.send.call_count == 3

    def test_recent_alerts(self):
        mgr = AlertManager(channels=[LogChannel()])
        mgr.alert(AlertType.FILL, "msg1")
        mgr.alert(AlertType.RISK_TRIGGER, "msg2")
        recent = mgr.recent_alerts
        assert len(recent) == 2
        assert recent[0].message == "msg1"
        assert recent[1].message == "msg2"

    def test_clear_history(self):
        mgr = AlertManager(channels=[LogChannel()])
        mgr.alert(AlertType.FILL, "msg1")
        assert len(mgr.recent_alerts) == 1
        mgr.clear_history()
        assert len(mgr.recent_alerts) == 0

    def test_history_cap(self):
        mgr = AlertManager(
            channels=[LogChannel()],
            max_alerts_per_window=2000,
        )
        for i in range(1100):
            mgr.alert(AlertType.CUSTOM, f"msg{i}")
        # History is capped at 500 after exceeding 1000
        assert len(mgr._history) <= 600

    def test_channel_exception_handled(self):
        bad_ch = MagicMock()
        bad_ch.send.side_effect = RuntimeError("boom")
        mgr = AlertManager(channels=[bad_ch])
        # Should not raise
        result = mgr.alert(AlertType.FILL, "test")
        assert result is True

    def test_multi_channel(self):
        ch1 = MagicMock()
        ch1.send.return_value = True
        ch2 = MagicMock()
        ch2.send.return_value = True
        mgr = AlertManager(channels=[ch1, ch2])
        mgr.alert(AlertType.FILL, "test")
        ch1.send.assert_called_once()
        ch2.send.assert_called_once()
