"""
Tests for Phase 29: Notification Engine.
"""

import json
import pytest


@pytest.fixture(autouse=True)
def _isolate(tmp_path, monkeypatch):
    channels = tmp_path / "channels"
    notifs = tmp_path / "notifications"
    prefs = notifs / "preferences"
    queue = notifs / "queue"
    for d in (channels, notifs, prefs, queue):
        d.mkdir(parents=True, exist_ok=True)

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

    import brynq.notifications as n
    monkeypatch.setattr(n, "NOTIFICATIONS_DIR", notifs)
    monkeypatch.setattr(n, "PREFERENCES_DIR", prefs)
    monkeypatch.setattr(n, "QUEUE_DIR", queue)
    monkeypatch.setattr(n, "HISTORY_FILE", notifs / "history.jsonl")
    monkeypatch.setattr(n, "BROKER_DIR", tmp_path)
    monkeypatch.setattr(n, "CHANNELS_DIR", channels)


class TestPreferences:
    def test_set_preferences(self):
        from brynq.notifications import set_preferences
        prefs = set_preferences("agent-1", delivery_method="broker")
        assert prefs["agent_id"] == "agent-1"
        assert prefs["delivery_method"] == "broker"

    def test_invalid_delivery(self):
        from brynq.notifications import set_preferences
        result = set_preferences("a", delivery_method="pigeon")
        assert "error" in result

    def test_get_defaults(self):
        from brynq.notifications import get_preferences
        prefs = get_preferences("nobody")
        assert prefs["delivery_method"] == "broker"
        assert len(prefs["enabled_events"]) > 0

    def test_mute_event(self):
        from brynq.notifications import set_preferences, mute_event, get_preferences
        set_preferences("m1")
        mute_event("m1", "follow_new")
        prefs = get_preferences("m1")
        assert "follow_new" in prefs["muted_events"]

    def test_unmute_event(self):
        from brynq.notifications import set_preferences, mute_event, unmute_event, get_preferences
        set_preferences("m2")
        mute_event("m2", "follow_new")
        unmute_event("m2", "follow_new")
        prefs = get_preferences("m2")
        assert "follow_new" not in prefs["muted_events"]


class TestNotify:
    def test_send_notification(self):
        from brynq.notifications import notify
        result = notify("agent-1", "task_match", "New task!", "A task matches your skills")
        assert result["status"] == "delivered"
        assert result["notification_id"]

    def test_unknown_event_type(self):
        from brynq.notifications import notify
        result = notify("a", "cosmic_event", "X", "Y")
        assert "error" in result

    def test_invalid_priority(self):
        from brynq.notifications import notify
        result = notify("a", "task_match", "X", "Y", priority="extreme")
        assert "error" in result

    def test_muted_notification(self):
        from brynq.notifications import set_preferences, mute_event, notify
        set_preferences("muted-agent")
        mute_event("muted-agent", "follow_new")
        result = notify("muted-agent", "follow_new", "New follower", "Someone followed you")
        assert result["status"] == "muted"

    def test_webhook_queued(self):
        from brynq.notifications import set_preferences, notify, get_pending_notifications
        set_preferences("webhook-agent", delivery_method="webhook", webhook_url="https://example.com/hook")
        result = notify("webhook-agent", "task_match", "Task!", "Match")
        assert result["status"] == "queued"
        pending = get_pending_notifications()
        assert len(pending) >= 1

    def test_high_priority(self):
        from brynq.notifications import notify
        result = notify("a", "system_alert", "ALERT", "System down", priority="urgent")
        assert result["status"] == "delivered"


class TestQueue:
    def test_pending_notifications(self):
        from brynq.notifications import set_preferences, notify, get_pending_notifications
        set_preferences("q1", delivery_method="email", email="test@brynq.ai")
        notify("q1", "task_match", "T", "B")
        notify("q1", "job_offer", "J", "B")
        pending = get_pending_notifications()
        assert len(pending) == 2

    def test_mark_delivered(self):
        from brynq.notifications import set_preferences, notify, mark_delivered, get_pending_notifications
        set_preferences("q2", delivery_method="webhook")
        result = notify("q2", "task_match", "T", "B")
        mark_delivered(result["notification_id"])
        pending = get_pending_notifications()
        delivered = [n for n in pending if n.get("delivered")]
        assert len([n for n in get_pending_notifications() if not n.get("delivered")]) == 0

    def test_mark_nonexistent(self):
        from brynq.notifications import mark_delivered
        result = mark_delivered("nope")
        assert "error" in result

    def test_clear_delivered(self):
        from brynq.notifications import set_preferences, notify, mark_delivered, clear_delivered, get_pending_notifications
        set_preferences("q3", delivery_method="webhook")
        r = notify("q3", "task_match", "T", "B")
        mark_delivered(r["notification_id"])
        removed = clear_delivered()
        assert removed == 1
        assert len(get_pending_notifications()) == 0


class TestHistory:
    def test_history(self):
        from brynq.notifications import notify, get_notification_history
        notify("h1", "task_match", "T1", "B1")
        notify("h1", "follow_new", "F1", "B2")
        history = get_notification_history(agent_id="h1")
        assert len(history) == 2

    def test_history_filter_type(self):
        from brynq.notifications import notify, get_notification_history
        notify("h2", "task_match", "T", "B")
        notify("h2", "follow_new", "F", "B")
        history = get_notification_history(agent_id="h2", event_type="task_match")
        assert len(history) == 1

    def test_empty_history(self):
        from brynq.notifications import get_notification_history
        assert get_notification_history(agent_id="ghost") == []


class TestStats:
    def test_stats(self):
        from brynq.notifications import notify, get_notification_stats
        notify("s1", "task_match", "T", "B")
        notify("s1", "task_match", "T2", "B2")
        notify("s1", "follow_new", "F", "B")
        stats = get_notification_stats("s1")
        assert stats["total_sent"] == 3
        assert stats["by_event_type"]["task_match"] == 2

    def test_empty_stats(self):
        from brynq.notifications import get_notification_stats
        stats = get_notification_stats("nobody")
        assert stats["total_sent"] == 0


class TestBulkNotify:
    def test_skill_match_notify(self, tmp_path):
        profiles = tmp_path / "profiles"
        profiles.mkdir(exist_ok=True)
        (profiles / "dev1.json").write_text(json.dumps({
            "session_id": "dev1", "skills": ["python", "testing"],
        }))
        (profiles / "dev2.json").write_text(json.dumps({
            "session_id": "dev2", "skills": ["rust"],
        }))
        from brynq.notifications import notify_skill_match
        results = notify_skill_match("Fix Python tests", ["python", "testing"], "task-99")
        assert len(results) == 1  # Only dev1 matches
