"""query command — query sessions from the central database."""

from __future__ import annotations

import asyncio
from typing import Literal

import typer
from rich.console import Console
from rich.table import Table
from sqlalchemy.exc import OperationalError

from cli_framework import get_context
from session_hub_cli.cli import app
from session_hub_cli.commands._helpers import fmt_datetime, get_query_api, print_json
from session_hub_cli.context import SessionHubContext
from session_hub_core import (
    MessageSearchResult,
    SessionDetail,
    SessionNotFoundError,
    SessionRow,
)

query_app = typer.Typer(
    name="query",
    help="Query sessions from the central database (read-only, CLI output).",
    no_args_is_help=True,
    rich_markup_mode="rich",
    pretty_exceptions_enable=False,
)

app.add_typer(query_app, name="query")


@query_app.command("list")
def query_list(
    ctx: typer.Context,
    tool: str | None = typer.Option(None, "--tool", "-t", help="Filter by tool"),
    host: str | None = typer.Option(None, "--host", help="Filter by host"),
    directory: str | None = typer.Option(
        None, "--directory", "-d", help="Filter by working directory (substring)"
    ),
    limit: int = typer.Option(50, "--limit", "-l", help="Maximum results"),
    format: Literal["table", "json"] = typer.Option(
        "table", "--format", help="Output format: table, json"
    ),
) -> None:
    """List sessions with optional filters."""
    app_ctx = get_context(ctx)
    assert isinstance(app_ctx, SessionHubContext)
    console = app_ctx.console
    try:
        rows = asyncio.run(_list(console, tool, host, directory, limit))
    except OperationalError as exc:
        console.print(f"[red]Error:[/red] Cannot connect to PostgreSQL: {exc}")
        raise typer.Exit(1) from None
    if format == "json":
        print_json(console, [row.model_dump(mode="json") for row in rows])
        return
    if not rows:
        console.print("[dim]No sessions found[/dim]")
        return
    table = Table(title="Sessions")
    table.add_column("Host", style="dim")
    table.add_column("Tool", style="cyan")
    table.add_column("Session ID", style="green")
    table.add_column("Title")
    table.add_column("Last Activity", style="dim")
    table.add_column("Messages", justify="right")
    for row in rows:
        table.add_row(
            row.host,
            row.tool,
            row.session_id,
            row.title,
            fmt_datetime(row.last_activity_at),
            str(row.message_count),
        )
    console.print(table)


@query_app.command("search")
def query_search(
    ctx: typer.Context,
    query: str = typer.Argument(..., help="Search query"),
    tool: str | None = typer.Option(None, "--tool", "-t", help="Filter by tool"),
    host: str | None = typer.Option(None, "--host", help="Filter by host"),
    role: str | None = typer.Option(None, "--role", help="Filter by role (user, assistant)"),
    limit: int = typer.Option(50, "--limit", "-l", help="Maximum results"),
    format: Literal["table", "json"] = typer.Option(
        "table", "--format", help="Output format: table, json"
    ),
) -> None:
    """Full-text search across message content."""
    app_ctx = get_context(ctx)
    assert isinstance(app_ctx, SessionHubContext)
    console = app_ctx.console
    try:
        rows = asyncio.run(_search(console, query, tool, host, role, limit))
    except OperationalError as exc:
        console.print(f"[red]Error:[/red] Cannot connect to PostgreSQL: {exc}")
        raise typer.Exit(1) from None
    if format == "json":
        print_json(console, [row.model_dump(mode="json") for row in rows])
        return
    if not rows:
        console.print("[dim]No results found[/dim]")
        return
    table = Table(title="Search Results")
    table.add_column("Rank", justify="right", style="cyan")
    table.add_column("Host", style="dim")
    table.add_column("Tool", style="cyan")
    table.add_column("Session")
    table.add_column("Role", style="green")
    table.add_column("Content (truncated)")
    table.add_column("Created At", style="dim")
    for row in rows:
        table.add_row(
            f"{row.rank:.4f}",
            row.session_host,
            row.session_tool,
            row.session_title or row.message_id,
            row.role,
            _truncate(row.content, 60),
            fmt_datetime(row.created_at),
        )
    console.print(table)


@query_app.command("get")
def query_get(
    ctx: typer.Context,
    session_id: str = typer.Argument(..., help="Session ID"),
    host: str = typer.Option(..., "--host", help="Host"),
    tool: str = typer.Option(..., "--tool", "-t", help="Tool"),
    format: Literal["text", "json", "markdown"] = typer.Option(
        "text", "--format", help="Output format: text, json, markdown"
    ),
) -> None:
    """Get a single session with all messages."""
    app_ctx = get_context(ctx)
    assert isinstance(app_ctx, SessionHubContext)
    console = app_ctx.console
    try:
        detail = asyncio.run(_get(console, host, tool, session_id))
    except SessionNotFoundError as exc:
        console.print(f"[red]Error:[/red] {exc}")
        raise typer.Exit(1) from None
    except OperationalError as exc:
        console.print(f"[red]Error:[/red] Cannot connect to PostgreSQL: {exc}")
        raise typer.Exit(1) from None
    if format == "json":
        print_json(console, detail.model_dump(mode="json"))
    elif format == "markdown":
        _print_markdown(console, detail)
    else:
        _print_text(console, detail)


async def _list(
    console: Console,
    tool: str | None,
    host: str | None,
    directory: str | None,
    limit: int,
) -> list[SessionRow]:
    api = get_query_api(console)
    try:
        return await api.list_sessions(
            tool=tool, host=host, working_directory=directory, limit=limit
        )
    finally:
        await api.close()


async def _search(
    console: Console,
    query: str,
    tool: str | None,
    host: str | None,
    role: str | None,
    limit: int,
) -> list[MessageSearchResult]:
    api = get_query_api(console)
    try:
        return await api.search_messages(
            query, tool=tool, host=host, role=role, limit=limit, mode="fts"
        )
    finally:
        await api.close()


async def _get(console: Console, host: str, tool: str, session_id: str) -> SessionDetail:
    api = get_query_api(console)
    try:
        return await api.get_session(host, tool, session_id)
    finally:
        await api.close()


def _truncate(content: str, length: int) -> str:
    preview = content[:length].replace("\n", " ")
    if len(content) > length:
        preview += "..."
    return preview


def _print_text(console: Console, detail: SessionDetail) -> None:
    console.print(f"Host: {detail.host}")
    console.print(f"Tool: {detail.tool}")
    console.print(f"Session ID: {detail.session_id}")
    console.print(f"Title: {detail.title}")
    console.print(f"Working Directory: {detail.working_directory}")
    if detail.model_name:
        console.print(f"Model: {detail.model_name}")
    console.print(f"Created: {fmt_datetime(detail.created_at)}")
    console.print(f"Last Activity: {fmt_datetime(detail.last_activity_at)}")
    console.print(f"Messages: {len(detail.messages)}")
    console.print()
    for message in detail.messages:
        console.print(
            f"[green]{message.role}[/green] {fmt_datetime(message.created_at)}: {message.content}"
        )


def _print_markdown(console: Console, detail: SessionDetail) -> None:
    console.print(f"# {detail.title}")
    console.print()
    console.print(f"- Host: {detail.host}")
    console.print(f"- Tool: {detail.tool}")
    console.print(f"- Session ID: {detail.session_id}")
    console.print(f"- Working Directory: {detail.working_directory}")
    console.print(f"- Created: {fmt_datetime(detail.created_at)}")
    console.print(f"- Last Activity: {fmt_datetime(detail.last_activity_at)}")
    console.print(f"- Messages: {len(detail.messages)}")
    console.print()
    console.print("## Messages")
    console.print()
    for message in detail.messages:
        console.print(f"**{message.role}** ({fmt_datetime(message.created_at)}):")
        console.print()
        console.print(message.content)
        console.print()
