"""Unit tests for the notification feed server."""

from __future__ import annotations

import json
import sqlite3
import time
import urllib.request
from pathlib import Path

import pytest

from vastian.tasks.feed import NotificationFeed, AGENT_META, CLI_COMMANDS, FEED_HTML
from vastian.tasks.queue import TaskQueue


@pytest.fixture
def feed_db(temp_dir: Path) -> Path:
    """Create a tasks.db with tables so the feed can read from it."""
    db_path = temp_dir / "tasks.db"
    tq = TaskQueue(db_path)
    tq.initialize()
    tq.close()
    return db_path


class TestFeedHTML:
    """Test the static HTML template structure."""

    def test_html_contains_placeholders(self) -> None:
        assert "__AGENT_OPTIONS__" in FEED_HTML
        assert "__TOPIC_PILLS__" in FEED_HTML
        assert "__CMD_LIST__" in FEED_HTML
        assert "__AGENT_LIST__" in FEED_HTML

    def test_html_has_board_tabs_placeholder(self) -> None:
        assert "__BOARD_TABS__" in FEED_HTML

    def test_html_has_drawer(self) -> None:
        assert "helpDrawer" in FEED_HTML
        assert "drawer-toggle" in FEED_HTML
        assert "Command Reference" in FEED_HTML

    def test_html_has_filter_bar(self) -> None:
        assert "agentFilter" in FEED_HTML
        assert "statusFilter" in FEED_HTML

    def test_html_uses_js_fetch_not_meta_refresh(self) -> None:
        # Should NOT have meta refresh (we use JS polling now)
        assert 'meta http-equiv="refresh"' not in FEED_HTML
        # Should have fetch-based polling
        assert "fetch('/api/notifications')" in FEED_HTML

    def test_html_has_board_switching_js(self) -> None:
        assert "switchBoard" in FEED_HTML
        assert "activeBoard" in FEED_HTML
        assert "BOARD_LABELS" in FEED_HTML


class TestAgentMeta:
    """Test that agent metadata is complete."""

    def test_all_agents_present(self) -> None:
        ids = {a["id"] for a in AGENT_META}
        expected = {
            "charles",
            "dominic",
            "liam",
            "henry",
            "finley",
            "theo",
            "elias",
            "victor",
            "adrian",
            "concord",
            "jake",
            "orion",
            "rowan",
            "arjuna",
            "felix",
            "kiran",
            "maria",
            "maya",
            "noah",
            "sage",
        }
        assert ids == expected

    def test_agents_have_topics(self) -> None:
        for agent in AGENT_META:
            assert len(agent["topics"]) > 0, f"{agent['id']} has no topics"


class TestCLICommands:
    """Test the CLI command metadata."""

    def test_commands_include_report(self) -> None:
        cmds = {c["cmd"] for c in CLI_COMMANDS}
        assert "/report <id>" in cmds

    def test_commands_include_inbox(self) -> None:
        cmds = {c["cmd"] for c in CLI_COMMANDS}
        assert "/inbox" in cmds

    def test_all_commands_have_topics(self) -> None:
        for cmd in CLI_COMMANDS:
            assert cmd.get("topic"), f"Command {cmd['cmd']} missing topic"


class TestNotificationFeedServer:
    """Integration tests for the HTTP feed server."""

    def test_start_and_stop(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14545)
        url = feed.start()
        assert url == "http://127.0.0.1:14545"
        feed.stop()

    def test_serve_html(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14546)
        url = feed.start()
        try:
            time.sleep(0.2)
            resp = urllib.request.urlopen(url, timeout=3)
            html = resp.read().decode("utf-8")
            assert "Vastian Network" in html
            assert "helpDrawer" in html
            assert resp.status == 200
        finally:
            feed.stop()

    def test_serve_json_api(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14547)
        url = feed.start()
        try:
            time.sleep(0.2)
            resp = urllib.request.urlopen(f"{url}/api/notifications", timeout=3)
            data = json.loads(resp.read())
            assert "tasks" in data
            assert "notifications" in data
            assert isinstance(data["tasks"], list)
            assert isinstance(data["notifications"], list)
        finally:
            feed.stop()

    def test_serve_agents_api(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14548)
        url = feed.start()
        try:
            time.sleep(0.2)
            resp = urllib.request.urlopen(f"{url}/api/agents", timeout=3)
            data = json.loads(resp.read())
            assert isinstance(data, list)
            assert len(data) == 20
        finally:
            feed.stop()

    def test_json_api_with_data(self, feed_db: Path) -> None:
        """Test that notifications appear in the API response."""
        tq = TaskQueue(feed_db)
        tq.initialize()
        tq.enqueue("delegation", "liam", "Test task from feed test")
        tq.add_notification("liam", "Working on it", "running")

        feed = NotificationFeed(db_path=feed_db, port=14549)
        url = feed.start()
        try:
            time.sleep(0.2)
            resp = urllib.request.urlopen(f"{url}/api/notifications", timeout=3)
            data = json.loads(resp.read())
            assert len(data["tasks"]) >= 1
            assert len(data["notifications"]) >= 1
        finally:
            feed.stop()
            tq.close()

    def test_start_returns_url(self, feed_db: Path) -> None:
        """Start should return a valid URL."""
        feed = NotificationFeed(db_path=feed_db, port=14550)
        url = feed.start()
        assert url.startswith("http://")
        assert "14550" in url
        feed.stop()


class TestFeedRendering:
    """Test HTML rendering with data."""

    def test_render_html_replaces_placeholders(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14551)
        html = feed._render_html()
        assert "__AGENT_OPTIONS__" not in html
        assert "__TOPIC_PILLS__" not in html
        assert "__CMD_LIST__" not in html
        assert "__AGENT_LIST__" not in html

    def test_render_includes_agent_cards(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14552)
        html = feed._render_html()
        assert "Charles" in html
        assert "finley" in html
        assert "agent-card-mini" in html

    def test_render_includes_command_list(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14553)
        html = feed._render_html()
        assert "/report" in html
        assert "/inbox" in html
        assert "/onboard" in html

    def test_render_replaces_board_tabs(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14554, user_tier="admin")
        html = feed._render_html()
        assert "__BOARD_TABS__" not in html
        # Admin should see all 4 board tab buttons
        assert 'data-board="user"' in html
        assert 'data-board="agent"' in html
        assert 'data-board="comms"' in html
        assert 'data-board="company"' in html

    def test_render_open_tier_shows_only_user_board(self, feed_db: Path) -> None:
        feed = NotificationFeed(db_path=feed_db, port=14555, user_tier="open")
        html = feed._render_html()
        # Open tier should have user board tab button
        assert 'data-board="user"' in html


class TestKanbanBoardFilter:
    """Test the Kanban board filtering via _get_kanban_data."""

    def test_no_filter_returns_all(self, feed_db: Path, temp_dir: Path) -> None:
        tq = TaskQueue(feed_db, state_dir=temp_dir)
        tq.initialize()
        tq.suggest_kanban_task("a", "User task", "D", board="user")
        tq.suggest_kanban_task("a", "Agent task", "D", board="agent")

        feed = NotificationFeed(db_path=feed_db, port=14556, state_dir=temp_dir)
        data = feed._get_kanban_data()
        assert len(data["tasks"]) == 2
        tq.close()

    def test_filter_by_board(self, feed_db: Path, temp_dir: Path) -> None:
        tq = TaskQueue(feed_db, state_dir=temp_dir)
        tq.initialize()
        tq.suggest_kanban_task("a", "User task", "D", board="user")
        tq.suggest_kanban_task("a", "Agent task", "D", board="agent")
        tq.suggest_kanban_task("a", "Comms task", "D", board="comms")

        feed = NotificationFeed(db_path=feed_db, port=14557, state_dir=temp_dir)
        user_data = feed._get_kanban_data(board="user")
        assert len(user_data["tasks"]) == 1
        assert user_data["tasks"][0]["board"] == "user"

        agent_data = feed._get_kanban_data(board="agent")
        assert len(agent_data["tasks"]) == 1
        assert agent_data["tasks"][0]["board"] == "agent"
        tq.close()

    def test_tasks_have_ticket_id(self, feed_db: Path, temp_dir: Path) -> None:
        tq = TaskQueue(feed_db, state_dir=temp_dir)
        tq.initialize()
        tq.suggest_kanban_task("a", "Test", "D", board="comms")

        feed = NotificationFeed(db_path=feed_db, port=14558, state_dir=temp_dir)
        data = feed._get_kanban_data()
        task = data["tasks"][0]
        assert "ticket_id" in task
        assert task["ticket_id"].startswith("MSG-")
        tq.close()
