"""Render MeshState as markdown text."""

from __future__ import annotations

from culture.formatting import relative_time as _relative_time

from .model import Agent, BotInfo, MeshState, Message, Room


def _escape_cell(text: str) -> str:
    """Escape pipe and newline characters for markdown table cells."""
    return text.replace("|", "\\|").replace("\n", " ")


def _agent_table(members: list[Agent]) -> str:
    """Render a markdown table of agents."""
    lines = [
        "| Agent | Status | Activity |",
        "|-------|--------|----------|",
    ]
    for a in members:
        activity = _escape_cell(a.activity) if a.is_local else ""
        lines.append(f"| {_escape_cell(a.nick)} | {a.status} | {activity} |")
    return "\n".join(lines)


def _message_list(messages: list[Message], limit: int) -> str:
    """Render recent messages as a markdown bullet list."""
    if not messages:
        return "No recent messages."
    shown = messages[:limit]
    lines = []
    for m in shown:
        lines.append(f"- {m.nick} ({_relative_time(m.timestamp)}): {m.text}")
    return "\n".join(lines)


def _render_room(room: Room, message_limit: int) -> str:
    """Render a single room section."""
    parts = [f"## {room.name}"]
    parts.append(f"Topic: {room.topic}" if room.topic else "Topic: (none)")
    if room.room_id:
        parts.append(f"Purpose: {room.purpose or ''}")
        parts.append(f"Tags: {', '.join(room.tags) if room.tags else 'none'}")
        meta_parts = []
        if room.owner:
            meta_parts.append(f"Owner: {room.owner}")
        if room.persistent:
            meta_parts.append("Persistent")
        if meta_parts:
            parts.append(" | ".join(meta_parts))
    parts.append("")
    parts.append(_agent_table(room.members))
    parts.append("")
    parts.append("### Recent messages")
    parts.append("")
    parts.append(_message_list(room.messages, message_limit))
    return "\n".join(parts)


def render_text(
    mesh: MeshState,
    *,
    room_filter: str | None = None,
    agent_filter: str | None = None,
    message_limit: int = 4,
) -> str:
    """Render a full mesh overview as markdown."""
    if agent_filter:
        return _render_agent_detail(mesh, agent_filter, message_limit)
    if room_filter:
        return _render_room_detail(mesh, room_filter, message_limit)
    return _render_default(mesh, message_limit)


def _summary_line(mesh: MeshState, fed_str: str) -> str:
    """Build the summary line with room/agent counts."""
    room_str = f"{len(mesh.rooms)} room{'s' if len(mesh.rooms) != 1 else ''}"
    stopped = [a for a in mesh.agents if a.status == "stopped"]
    if stopped:
        online = len(mesh.agents) - len(stopped)
        agent_str = f"{online} online, {len(stopped)} stopped ({len(mesh.agents)} total)"
    else:
        agent_str = f"{len(mesh.agents)} agent{'s' if len(mesh.agents) != 1 else ''}"
    return f"{room_str} | {agent_str} | {fed_str}"


def _bots_section(bots: list[BotInfo]) -> list[str]:
    """Render the bots table section."""
    parts = [
        "",
        "## Bots",
        "",
        "| Bot | Trigger | Channels | Owner |",
        "|-----|---------|----------|-------|",
    ]
    for bot in bots:
        channels = ", ".join(bot.channels) if bot.channels else "-"
        name = f"{bot.name} [archived]" if bot.archived else bot.name
        parts.append(f"| {name} | {bot.trigger_type} | {channels} | {bot.owner} |")
    return parts


def _render_default(mesh: MeshState, message_limit: int) -> str:
    """Render the full mesh overview."""
    fed_count = len(mesh.federation_links)
    fed_str = f"{fed_count} federation link{'s' if fed_count != 1 else ''}"
    if mesh.federation_links:
        fed_str += f" ({', '.join(mesh.federation_links)})"

    parts = [f"# {mesh.server_name} mesh", "", _summary_line(mesh, fed_str)]

    for room in mesh.rooms:
        parts.append("")
        parts.append(_render_room(room, message_limit))

    stopped_agents = [a for a in mesh.agents if a.status == "stopped"]
    if stopped_agents:
        parts.extend(["", "## Stopped agents", "", _agent_table(stopped_agents)])

    if mesh.bots:
        parts.extend(_bots_section(mesh.bots))

    return "\n".join(parts) + "\n"


def _render_room_detail(mesh: MeshState, room_name: str, message_limit: int) -> str:
    """Render a single room drill-down."""
    room = None
    for r in mesh.rooms:
        if r.name == room_name:
            room = r
            break
    if room is None:
        return f"Room {room_name} not found.\n"

    fed_str = ", ".join(room.federation_servers) if room.federation_servers else "none"
    ops_str = ", ".join(room.operators) if room.operators else "none"

    parts = [f"# {room.name}"]
    parts.append("")
    parts.append(f"Topic: {room.topic}" if room.topic else "Topic: (none)")
    parts.append(f"Members: {len(room.members)} | Operators: {ops_str} | Federation: {fed_str}")
    parts.append("")
    parts.append(_agent_table(room.members))
    parts.append("")
    parts.append(f"## Recent messages (last {message_limit})")
    parts.append("")
    parts.append(_message_list(room.messages, message_limit))
    return "\n".join(parts) + "\n"


def _build_agent_metadata_rows(agent: Agent) -> list[tuple[str, str]]:
    rows = [
        ("Status", agent.status),
    ]
    if agent.backend:
        rows.append(("Backend", agent.backend))
    if agent.model:
        rows.append(("Model", agent.model))
    if agent.directory:
        rows.append(("Directory", agent.directory))
    rows.append(("Activity", agent.activity or "none"))
    if agent.turns is not None:
        rows.append(("Turns", str(agent.turns)))
    if agent.uptime:
        rows.append(("Uptime", agent.uptime))
    if agent.tags:
        rows.append(("Tags", ", ".join(agent.tags)))
    return rows


def _render_agent_channels(agent: Agent, rooms: list[Room]) -> list[str]:
    parts = []
    parts.append(f"## Channels ({len(agent.channels)})")
    parts.append("")
    parts.append("| Channel | Role | Last spoke |")
    parts.append("|---------|------|------------|")
    for ch_name in agent.channels:
        role = (
            "operator"
            if any(r.name == ch_name and agent.nick in r.operators for r in rooms)
            else "member"
        )
        last_spoke = _find_last_spoke(agent.nick, ch_name, rooms)
        parts.append(f"| {ch_name} | {role} | {last_spoke} |")
    return parts


def _find_last_spoke(nick: str, ch_name: str, rooms: list[Room]) -> str:
    for room in rooms:
        if room.name == ch_name:
            for msg in room.messages:
                if msg.nick == nick:
                    return _relative_time(msg.timestamp)
            break
    return "never"


def _render_agent_activity(agent: Agent, rooms: list[Room], message_limit: int) -> list[str]:
    all_msgs = []
    for room in rooms:
        for msg in room.messages:
            if msg.nick == agent.nick:
                all_msgs.append(msg)
    all_msgs.sort(key=lambda m: m.timestamp, reverse=True)

    parts = []
    parts.append(f"## Recent activity across channels (last {message_limit})")
    parts.append("")
    if all_msgs:
        for msg in all_msgs[:message_limit]:
            parts.append(f"- {msg.channel} ({_relative_time(msg.timestamp)}): {msg.text}")
    else:
        parts.append("No recent activity.")
    return parts


def _render_agent_bots(nick: str, mesh: MeshState) -> list[str]:
    owned_bots = [b for b in mesh.bots if b.owner == nick]
    if not owned_bots:
        return []
    parts = []
    parts.append(f"## Bots ({len(owned_bots)})")
    parts.append("")
    for bot in owned_bots:
        channels = ", ".join(bot.channels) if bot.channels else "-"
        status_str = f"{bot.status}, archived" if bot.archived else bot.status
        parts.append(f"- {bot.name} ({bot.trigger_type}, {channels}, {status_str})")
    return parts


def _render_agent_detail(mesh: MeshState, nick: str, message_limit: int) -> str:
    """Render a single agent drill-down."""
    agent = None
    for a in mesh.agents:
        if a.nick == nick:
            agent = a
            break
    if agent is None:
        return f"Agent {nick} not found.\n"

    parts = [f"# {agent.nick}"]
    parts.append("")

    # Metadata table
    rows = _build_agent_metadata_rows(agent)
    parts.append("| Field | Value |")
    parts.append("|-------|-------|")
    for field_name, value in rows:
        parts.append(f"| {field_name} | {_escape_cell(value)} |")

    # Channels table
    parts.append("")
    parts.extend(_render_agent_channels(agent, mesh.rooms))

    # Cross-channel recent activity
    parts.append("")
    parts.extend(_render_agent_activity(agent, mesh.rooms, message_limit))

    # Owned bots
    bot_parts = _render_agent_bots(nick, mesh)
    if bot_parts:
        parts.append("")
        parts.extend(bot_parts)

    return "\n".join(parts) + "\n"
