"""
Threading support for Terminal Broker — reply-to and conversation threads.

Enables replying to specific messages, creating conversation threads.
Each reply includes a `reply_to` field pointing to the parent message's id.

Named threading_support to avoid conflict with stdlib threading module.

Usage:
    from brynq.threading_support import post_reply, get_thread, format_thread

    reply = post_reply("general", "abc123def456", "Running now, 40% done")
    thread = get_thread("general", "abc123def456")
    print(format_thread(thread))
"""

import uuid
from datetime import datetime, timezone

from brynq.server import (
    CHANNELS_DIR,
    PLATFORM,
    SESSION_ID,
    SESSION_NAME,
    _append_jsonl_sync,
    _read_jsonl_sync,
)


def _get_message_by_id(channel: str, message_id: str) -> dict | None:
    """Find a single message by id in a channel. Returns None if not found."""
    path = CHANNELS_DIR / f"{channel}.jsonl"
    entries = _read_jsonl_sync(path)
    for entry in entries:
        if entry.get("id") == message_id:
            return entry
    return None


def post_reply(
    channel: str,
    parent_id: str,
    message: str,
    msg_type: str = "info",
) -> dict:
    """Create a reply to an existing message.

    Args:
        channel: Channel name (e.g. 'general', 'gen21').
        parent_id: The id of the message being replied to.
        message: Reply text.
        msg_type: Message type (info, request, alert, etc.).

    Returns:
        The new message entry dict.

    Raises:
        ValueError: If parent_id does not exist in the channel.
    """
    parent = _get_message_by_id(channel, parent_id)
    if parent is None:
        raise ValueError(
            f"Parent message '{parent_id}' not found in #{channel}"
        )

    entry = {
        "id": uuid.uuid4().hex[:12],
        "reply_to": parent_id,
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "session_id": SESSION_ID,
        "session_name": SESSION_NAME,
        "platform": PLATFORM,
        "channel": channel,
        "msg_type": msg_type,
        "message": message,
    }

    path = CHANNELS_DIR / f"{channel}.jsonl"
    _append_jsonl_sync(path, entry)
    return entry


def get_thread(channel: str, message_id: str) -> list[dict]:
    """Get a full thread: the root message plus all replies (direct and nested).

    Walks the reply tree starting from message_id, collecting every message
    whose reply_to chain leads back to it. Results sorted chronologically.

    Args:
        channel: Channel name.
        message_id: The root message id to build the thread from.

    Returns:
        List of message dicts, root first, then replies in chronological order.
        Empty list if message_id is not found.
    """
    path = CHANNELS_DIR / f"{channel}.jsonl"
    all_entries = _read_jsonl_sync(path)

    # Index by id for fast lookup
    by_id: dict[str, dict] = {}
    for entry in all_entries:
        eid = entry.get("id")
        if eid:
            by_id[eid] = entry

    root = by_id.get(message_id)
    if root is None:
        return []

    # Collect all descendants via BFS on reply_to
    thread_ids: set[str] = {message_id}
    changed = True
    while changed:
        changed = False
        for entry in all_entries:
            eid = entry.get("id")
            parent = entry.get("reply_to")
            if eid and parent and parent in thread_ids and eid not in thread_ids:
                thread_ids.add(eid)
                changed = True

    # Gather and sort chronologically (JSONL is already append-order,
    # but sort by timestamp to be safe)
    thread = [e for e in all_entries if e.get("id") in thread_ids]
    thread.sort(key=lambda e: e.get("timestamp", ""))
    return thread


def find_replies(channel: str, message_id: str) -> list[dict]:
    """Find all direct replies to a specific message.

    Only returns messages whose reply_to equals message_id (one level deep).
    Results sorted chronologically.

    Args:
        channel: Channel name.
        message_id: The message id to find replies for.

    Returns:
        List of direct reply dicts, sorted by timestamp.
    """
    path = CHANNELS_DIR / f"{channel}.jsonl"
    all_entries = _read_jsonl_sync(path)

    replies = [
        e for e in all_entries
        if e.get("reply_to") == message_id
    ]
    replies.sort(key=lambda e: e.get("timestamp", ""))
    return replies


def format_thread(thread: list[dict]) -> str:
    """Pretty-print a thread with indentation showing reply depth.

    Output format:
        [10:30:15] claude-ops@claude: Can someone check the backfill?
          [10:31:02] gemini-gen21@gemini: Running now, 40% done
          [10:35:44] gemini-gen21@gemini: Complete! 1322 parquets upgraded
            [10:36:01] claude-ops@claude: Verified, all dims present

    Args:
        thread: List of message dicts as returned by get_thread().

    Returns:
        Formatted string with indented replies.
    """
    if not thread:
        return "(empty thread)"

    # Build parent->children map for depth calculation
    children: dict[str | None, list[str]] = {}
    by_id: dict[str, dict] = {}
    for entry in thread:
        eid = entry.get("id", "")
        parent = entry.get("reply_to")
        by_id[eid] = entry
        children.setdefault(parent, []).append(eid)

    # Calculate depth for each message via BFS from root
    root_id = thread[0].get("id", "")
    depths: dict[str, int] = {root_id: 0}

    # BFS to assign depths
    queue = [root_id]
    while queue:
        current = queue.pop(0)
        current_depth = depths[current]
        for child_id in children.get(current, []):
            if child_id not in depths:
                depths[child_id] = current_depth + 1
                queue.append(child_id)

    # Format each message with indentation
    lines = []
    for entry in thread:
        eid = entry.get("id", "")
        depth = depths.get(eid, 0)
        indent = "  " * depth

        ts = entry.get("timestamp", "")
        # Extract HH:MM:SS from ISO timestamp
        time_part = ts[11:19] if len(ts) >= 19 else ts
        name = entry.get("session_name", "unknown")
        plat = entry.get("platform", "?")
        msg = entry.get("message", "")

        lines.append(f"{indent}[{time_part}] {name}@{plat}: {msg}")

    return "\n".join(lines)
