"""Tests for the session-hub query CLI commands."""

import json
from datetime import UTC, datetime
from unittest.mock import AsyncMock, MagicMock, patch

from typer.testing import CliRunner

from session_hub_cli.cli import app
from session_hub_core import (
    MessageRow,
    MessageSearchResult,
    SessionDetail,
    SessionNotFoundError,
    SessionRow,
)

runner = CliRunner()

NOW = datetime(2026, 8, 1, 10, 0, 0, tzinfo=UTC)


def make_session_row(**kwargs) -> SessionRow:
    defaults = dict(
        host="vps9",
        tool="devin",
        session_id="sess-abc",
        title="Fix auth module",
        working_directory="/tmp/test",
        model_name=None,
        created_at=NOW,
        last_activity_at=NOW,
        message_count=5,
        hidden=False,
        synced_at=NOW,
    )
    defaults.update(kwargs)
    return SessionRow(**defaults)


def make_message_row(**kwargs) -> MessageRow:
    defaults = dict(
        node_id="node-1",
        parent_node_id=None,
        message_id="msg-1",
        role="user",
        content="How do I fix the auth module?",
        created_at=NOW,
        metadata={},
    )
    defaults.update(kwargs)
    return MessageRow(**defaults)


def make_search_result(**kwargs) -> MessageSearchResult:
    defaults = dict(
        node_id="node-1",
        parent_node_id=None,
        message_id="msg-1",
        role="user",
        content="How do I fix the auth module?",
        created_at=NOW,
        metadata={},
        session_id="session-1",
        session_title="Fix auth module",
        session_host="vps9",
        session_tool="devin",
        session_working_directory="/tmp/test",
        rank=0.5123,
    )
    defaults.update(kwargs)
    return MessageSearchResult(**defaults)


def make_detail(**kwargs) -> SessionDetail:
    defaults = dict(
        host="vps9",
        tool="devin",
        session_id="sess-abc",
        title="Fix auth module",
        working_directory="/tmp/test",
        model_name=None,
        created_at=NOW,
        last_activity_at=NOW,
        message_count=2,
        hidden=False,
        synced_at=NOW,
        messages=[
            make_message_row(),
            make_message_row(
                node_id="node-2", message_id="msg-2", role="assistant", content="Done."
            ),
        ],
        metadata={},
    )
    defaults.update(kwargs)
    return SessionDetail(**defaults)


def make_api() -> MagicMock:
    api = AsyncMock()
    api.close = AsyncMock()
    return api


@patch("session_hub_cli.commands.query.get_query_api")
def test_list_table(mock_get_api):
    api = make_api()
    api.list_sessions.return_value = [
        make_session_row(session_id="sess-abc", title="Fix auth module"),
        make_session_row(session_id="sess-def", title="Refactor CLI", tool="opencode"),
    ]
    mock_get_api.return_value = api

    result = runner.invoke(app, ["query", "list"])

    assert result.exit_code == 0
    assert "vps9" in result.stdout
    assert "devin" in result.stdout
    assert "opencode" in result.stdout
    assert "sess-abc" in result.stdout
    assert "Fix auth module" in result.stdout
    assert "Messages" in result.stdout
    api.list_sessions.assert_awaited_once_with(
        tool=None, host=None, working_directory=None, limit=50
    )
    api.close.assert_awaited_once()


@patch("session_hub_cli.commands.query.get_query_api")
def test_list_json(mock_get_api):
    api = make_api()
    api.list_sessions.return_value = [
        make_session_row(session_id="sess-abc", title="Fix auth module")
    ]
    mock_get_api.return_value = api

    result = runner.invoke(app, ["query", "list", "--format", "json"])

    assert result.exit_code == 0
    data = json.loads(result.stdout)
    assert isinstance(data, list)
    assert data[0]["session_id"] == "sess-abc"
    assert data[0]["title"] == "Fix auth module"
    assert data[0]["host"] == "vps9"


@patch("session_hub_cli.commands.query.get_query_api")
def test_list_empty(mock_get_api):
    api = make_api()
    api.list_sessions.return_value = []
    mock_get_api.return_value = api

    result = runner.invoke(app, ["query", "list"])

    assert result.exit_code == 0
    assert "No sessions found" in result.stdout


@patch("session_hub_cli.commands.query.get_query_api")
def test_search_table(mock_get_api):
    api = make_api()
    api.search_messages.return_value = [make_search_result(session_title="Auth fix")]
    mock_get_api.return_value = api

    result = runner.invoke(app, ["query", "search", "fix auth"])

    assert result.exit_code == 0
    assert "0.5123" in result.stdout
    assert "vps9" in result.stdout
    assert "devin" in result.stdout
    assert "Auth fix" in result.stdout
    assert "user" in result.stdout
    assert "module?" in result.stdout
    api.search_messages.assert_awaited_once_with(
        "fix auth", tool=None, host=None, role=None, limit=50, mode="fts"
    )


@patch("session_hub_cli.commands.query.get_query_api")
def test_search_json(mock_get_api):
    api = make_api()
    api.search_messages.return_value = [make_search_result(rank=0.5123)]
    mock_get_api.return_value = api

    result = runner.invoke(app, ["query", "search", "fix auth", "--format", "json"])

    assert result.exit_code == 0
    data = json.loads(result.stdout)
    assert isinstance(data, list)
    assert data[0]["session_title"] == "Fix auth module"
    assert data[0]["rank"] == 0.5123


@patch("session_hub_cli.commands.query.get_query_api")
def test_get_text(mock_get_api):
    api = make_api()
    api.get_session.return_value = make_detail()
    mock_get_api.return_value = api

    result = runner.invoke(app, ["query", "get", "sess-abc", "--host", "vps9", "--tool", "devin"])

    assert result.exit_code == 0
    assert "Host: vps9" in result.stdout
    assert "Tool: devin" in result.stdout
    assert "Session ID: sess-abc" in result.stdout
    assert "Title: Fix auth module" in result.stdout
    assert "How do I fix the auth module?" in result.stdout
    api.get_session.assert_awaited_once_with("vps9", "devin", "sess-abc")


@patch("session_hub_cli.commands.query.get_query_api")
def test_get_json(mock_get_api):
    api = make_api()
    api.get_session.return_value = make_detail()
    mock_get_api.return_value = api

    result = runner.invoke(
        app, ["query", "get", "sess-abc", "--host", "vps9", "--tool", "devin", "--format", "json"]
    )

    assert result.exit_code == 0
    data = json.loads(result.stdout)
    assert data["session_id"] == "sess-abc"
    assert len(data["messages"]) == 2
    assert data["messages"][0]["role"] == "user"


@patch("session_hub_cli.commands.query.get_query_api")
def test_get_markdown(mock_get_api):
    api = make_api()
    api.get_session.return_value = make_detail()
    mock_get_api.return_value = api

    result = runner.invoke(
        app,
        ["query", "get", "sess-abc", "--host", "vps9", "--tool", "devin", "--format", "markdown"],
    )

    assert result.exit_code == 0
    assert "# Fix auth module" in result.stdout
    assert "## Messages" in result.stdout
    assert "**user**" in result.stdout


@patch("session_hub_cli.commands.query.get_query_api")
def test_get_not_found(mock_get_api):
    api = make_api()
    api.get_session.side_effect = SessionNotFoundError("Session vps9/devin/sess-abc not found")
    mock_get_api.return_value = api

    result = runner.invoke(app, ["query", "get", "sess-abc", "--host", "vps9", "--tool", "devin"])

    assert result.exit_code == 1
    assert "not found" in result.stdout
