"""Peer commands: list, inspect, card, chat, search, create, metadata, representation."""

from __future__ import annotations

import json
from typing import Optional

import typer

from honcho.api_types import PeerConfig

from honcho_cli.commands.workspace import _config_to_dict, _handle_error, _raw_list
from honcho_cli.output import print_error, print_result, use_json
from honcho_cli.validation import validate_resource_id

from honcho_cli._help import HonchoTyperGroup
from honcho_cli.common import add_common_options, get_client, get_resolved_config, handle_cmd_flags

app = typer.Typer(cls=HonchoTyperGroup, help="List, create, chat with, search, and manage peers and their representations.")
add_common_options(app)


def _get_peer_id(peer_id: str | None) -> str:

    config = get_resolved_config()
    pid = peer_id or config.peer_id
    if not pid:
        if not config.workspace_id:
            print_error(
                "NO_SCOPE",
                "No peer or workspace scoped. Pass --peer/-p and --workspace/-w, or set HONCHO_PEER_ID and HONCHO_WORKSPACE_ID.",
            )
        else:
            print_error("NO_PEER", "No peer ID provided. Pass --peer/-p or set HONCHO_PEER_ID.")
        raise typer.Exit(1)
    return validate_resource_id(pid, "peer")


@app.command("list")
def list_peers(
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """List all peers in the workspace."""
    handle_cmd_flags(json_output=json_output, workspace=workspace)
    client, config = get_client()

    try:
        raw_peers = _raw_list(client.peers())
        items = [
            {
                "id": p.id,
                "metadata": p.metadata,
                "configuration": _config_to_dict(p.configuration) if p.configuration else None,
                "created_at": str(p.created_at),
            }
            for p in raw_peers
        ]
        print_result(items, columns=["id", "metadata", "created_at"], title="Peers")
    except Exception as e:
        _handle_error(e, "peer", "list")


@app.command()
def inspect(
    peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Inspect a peer: card, session count, recent conclusions."""

    handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
    pid = _get_peer_id(peer_id)
    client, config = get_client()
    p = client.peer(pid)

    try:
        card = p.get_card()
        peer_config = p.get_configuration()
        # First page only; SyncPage.total (when the server supplies it) is
        # authoritative for counts without walking every page.
        session_page = p.sessions()
        conclusion_page = p.conclusions.list(size=10)

        session_items = session_page.items
        conclusion_items = conclusion_page.items

        result = {
            "id": pid,
            "card": card,
            "configuration": _config_to_dict(peer_config) if peer_config else None,
            "session_count": session_page.total,
            "conclusion_count": conclusion_page.total,
            "recent_conclusions": [
                {"id": c.id, "content": c.content if use_json() else c.content[:200], "created_at": str(c.created_at)}
                for c in conclusion_items
            ],
            "sessions": [{"id": s.id} for s in session_items[:10]],
        }
        print_result(result)
    except Exception as e:
        _handle_error(e, "peer", pid)


@app.command()
def card(
    peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
    target: Optional[str] = typer.Option(None, help="Target peer for relationship card"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Get raw peer card content."""

    handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
    pid = _get_peer_id(peer_id)
    client, config = get_client()
    p = client.peer(pid)

    try:
        result = p.get_card(target=target)
        print_result({"peer_id": pid, "target": target, "card": result})
    except Exception as e:
        _handle_error(e, "peer", pid)


@app.command()
def chat(
    query: str = typer.Argument(help="Question to ask about the peer"),
    target: Optional[str] = typer.Option(None, help="Target peer for perspective"),
    reasoning: Optional[str] = typer.Option(None, "--reasoning", "-r", help="Reasoning level: minimal, low, medium, high, max"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
    session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Query the dialectic about a peer."""

    _REASONING_LEVELS = ("minimal", "low", "medium", "high", "max")
    if reasoning and reasoning not in _REASONING_LEVELS:
        from honcho_cli.output import print_error
        print_error("INVALID_REASONING", f"--reasoning must be one of: {', '.join(_REASONING_LEVELS)}")
        raise typer.Exit(1)

    handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer, session=session)
    pid = _get_peer_id(None)
    client, config = get_client()
    p = client.peer(pid)

    try:
        response = p.chat(
            query,
            target=target,
            session=config.session_id or None,
            reasoning_level=reasoning or None,
        )
        print_result({"peer_id": pid, "query": query, "response": response})
    except Exception as e:
        _handle_error(e, "peer", pid)


@app.command()
def search(
    query: str = typer.Argument(help="Search query"),
    limit: int = typer.Option(10, help="Max results"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Search a peer's messages."""

    handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
    pid = _get_peer_id(None)
    client, config = get_client()
    p = client.peer(pid)

    try:
        results = p.search(query, limit=limit)
        items = [
            {
                "id": m.id,
                "content": m.content if use_json() else m.content[:200],
                "session_id": m.session_id,
                "created_at": str(m.created_at),
            }
            for m in results
        ]
        print_result(items, columns=["id", "session_id", "content", "created_at"], title=f"Peer search: {query}")
    except Exception as e:
        _handle_error(e, "peer", pid)


@app.command("create")
def create_peer(
    peer_id: str = typer.Argument(help="Peer ID to create or get"),
    observe_me: Optional[bool] = typer.Option(None, "--observe-me/--no-observe-me", help="Whether Honcho will form a representation of this peer"),
    metadata: Optional[str] = typer.Option(None, "--metadata", help="JSON metadata to associate with the peer"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Create or get a peer."""
    handle_cmd_flags(json_output=json_output, workspace=workspace)
    pid = validate_resource_id(peer_id, "peer")
    client, config = get_client()

    parsed_metadata = None
    if metadata:
        try:
            parsed_metadata = json.loads(metadata)
        except json.JSONDecodeError as e:
            print_error("INVALID_JSON", f"--metadata must be valid JSON: {e}", {})
            raise typer.Exit(1)

    peer_config = PeerConfig(observe_me=observe_me) if observe_me is not None else None

    try:
        p = client.peer(pid, configuration=peer_config, metadata=parsed_metadata)
        # Only round-trip to the server when the caller passed config or
        # metadata — in that case get-or-create may have returned a
        # pre-existing peer and the echoed output would lie. When no input
        # was passed, skip the two extra API calls entirely.
        result: dict[str, object] = {"peer_id": p.id}
        if peer_config is not None or parsed_metadata is not None:
            result["metadata"] = p.get_metadata()
            result["configuration"] = _config_to_dict(p.get_configuration())
        print_result(result)
    except Exception as e:
        _handle_error(e, "peer", pid)


@app.command("get-metadata")
def get_metadata(
    peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Get metadata for a peer."""

    handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
    pid = _get_peer_id(peer_id)
    client, config = get_client()
    p = client.peer(pid)

    try:
        result = p.get_metadata()
        print_result({"peer_id": pid, "metadata": result})
    except Exception as e:
        _handle_error(e, "peer", pid)


@app.command("set-metadata")
def set_metadata(
    metadata: str = typer.Argument(help="JSON metadata to set (e.g. '{\"key\": \"value\"}')"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Peer ID (uses default if omitted)"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Set metadata for a peer."""

    handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer)
    pid = _get_peer_id(None)
    client, config = get_client()

    try:
        parsed = json.loads(metadata)
    except json.JSONDecodeError as e:
        print_error("INVALID_JSON", f"metadata must be valid JSON: {e}", {})
        raise typer.Exit(1)

    p = client.peer(pid)

    try:
        p.set_metadata(parsed)
        print_result({"peer_id": pid, "metadata": parsed})
    except Exception as e:
        _handle_error(e, "peer", pid)


@app.command()
def representation(
    peer_id: Optional[str] = typer.Argument(None, help="Peer ID (uses default if omitted)"),
    target: Optional[str] = typer.Option(None, help="Target peer to get representation about"),
    search_query: Optional[str] = typer.Option(None, help="Semantic search query to filter conclusions"),
    max_conclusions: Optional[int] = typer.Option(None, help="Maximum number of conclusions to include"),
    workspace: Optional[str] = typer.Option(None, "--workspace", "-w", help="Override workspace ID"),
    peer: Optional[str] = typer.Option(None, "--peer", "-p", help="Override peer ID"),
    session: Optional[str] = typer.Option(None, "--session", "-s", help="Override session ID"),
    json_output: bool = typer.Option(False, "--json", help="Force JSON output"),
) -> None:
    """Get the formatted representation for a peer."""

    handle_cmd_flags(json_output=json_output, workspace=workspace, peer=peer, session=session)
    pid = _get_peer_id(peer_id)
    client, config = get_client()
    p = client.peer(pid)

    try:
        result = p.representation(
            target=target,
            session=config.session_id or None,
            search_query=search_query,
            max_conclusions=max_conclusions,
        )
        print_result({"peer_id": pid, "target": target, "representation": result})
    except Exception as e:
        _handle_error(e, "peer", pid)
