"""Result formatting functions for CI tools.

These functions convert raw search/retrieval results into human-readable
markdown text suitable for LLM consumption.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from open_agent_kit.features.team.constants import (
    CI_FORMAT_DATE_DISPLAY,
    CI_FORMAT_PREVIEW_LENGTH,
    CI_FORMAT_TITLE_MAX_LENGTH,
)

if TYPE_CHECKING:
    from open_agent_kit.features.team.retrieval.engine import SearchResult


def format_code_results(
    results: list[dict[str, Any]], max_preview: int = CI_FORMAT_PREVIEW_LENGTH
) -> str:
    """Format code search results for agent consumption.

    Args:
        results: Code search results from RetrievalEngine.
        max_preview: Maximum characters for content preview.

    Returns:
        Formatted markdown string with code results.
    """
    if not results:
        return "No code results found."

    lines = [f"Found {len(results)} code chunks:\n"]
    for i, r in enumerate(results, 1):
        filepath = r.get("filepath", "unknown")
        chunk_type = r.get("chunk_type", "unknown")
        name = r.get("name", "")
        start_line = r.get("start_line", 0)
        end_line = r.get("end_line", 0)
        confidence = r.get("confidence", "medium")
        relevance = r.get("relevance")
        content = r.get("content", "")

        # Build header with location and metadata
        header = f"{i}. **{filepath}:{start_line}-{end_line}**"
        if name:
            header += f" ({chunk_type}: {name})"
        if relevance is not None:
            header += f" [relevance: {round(relevance, 2)}]"
        elif confidence:
            header += f" [{confidence}]"

        lines.append(header)
        if content:
            preview = content[:max_preview]
            if len(content) > max_preview:
                preview += "..."
            lines.append(f"```\n{preview}\n```\n")

    return "\n".join(lines)


def format_memory_results(results: list[dict[str, Any]]) -> str:
    """Format memory search results for agent consumption.

    Args:
        results: Memory search results from RetrievalEngine.

    Returns:
        Formatted markdown string with memory results.
    """
    if not results:
        return "No memories found."

    # Emoji mapping for memory types
    emoji_map = {
        "gotcha": "⚠️",
        "bug_fix": "🐛",
        "decision": "📋",
        "discovery": "💡",
        "trade_off": "⚖️",
    }

    lines = [f"Found {len(results)} memories:\n"]
    for i, r in enumerate(results, 1):
        memory_type = r.get("memory_type", "discovery")
        observation = r.get("observation", r.get("summary", ""))
        confidence = r.get("confidence", "medium")
        context = r.get("context", "")
        emoji = emoji_map.get(memory_type, "📝")

        header = f"{i}. {emoji} [{memory_type}]"
        if confidence != "medium":
            header += f" ({confidence})"

        lines.append(header)
        lines.append(f"   {observation}")
        if context:
            lines.append(f"   (context: {context})")
        lines.append("")

    return "\n".join(lines)


def format_plan_results(results: list[dict[str, Any]]) -> str:
    """Format plan search results for agent consumption.

    Plans are Software Design Documents (SDDs) that capture design intent,
    requirements, and implementation approach for features.

    Args:
        results: Plan search results from RetrievalEngine.

    Returns:
        Formatted markdown string with plan results.
    """
    if not results:
        return "No plans found."

    lines = [f"Found {len(results)} plans:\n"]
    for i, r in enumerate(results, 1):
        title = r.get("title", "Untitled Plan")
        confidence = r.get("confidence", "medium")
        preview = r.get("preview", "")
        created_at = r.get("created_at", "")
        plan_id = r.get("id", "unknown")

        lines.append(f"{i}. **{title}** [{confidence}]")
        if created_at:
            lines.append(f"   Created: {created_at}")
        lines.append(f"   ID: {plan_id}")
        if preview:
            # Indent preview lines (first 5 lines max)
            preview_lines = preview.split("\n")[:5]
            for line in preview_lines:
                lines.append(f"   {line}")
            if len(preview.split("\n")) > 5:
                lines.append("   ...")
        lines.append("")

    return "\n".join(lines)


def format_session_results(sessions: list[dict[str, Any]]) -> str:
    """Format session list for agent consumption.

    Args:
        sessions: Session records from ActivityStore.

    Returns:
        Formatted markdown string with session summaries.
    """
    if not sessions:
        return "No sessions found."

    lines = [f"Found {len(sessions)} sessions:\n"]
    for i, s in enumerate(sessions, 1):
        session_id = s.get("id", "unknown")
        title = s.get("title") or s.get("first_prompt_preview", "Untitled")
        if title and len(title) > CI_FORMAT_TITLE_MAX_LENGTH:
            title = title[: CI_FORMAT_TITLE_MAX_LENGTH - 3] + "..."
        status = s.get("status", "unknown")
        started_at = s.get("started_at", "")
        summary = s.get("summary", "")

        lines.append(f"{i}. {title}")
        lines.append(f"   ID: {session_id} | Status: {status} | Started: {started_at}")
        if summary:
            preview = (
                summary[:CI_FORMAT_PREVIEW_LENGTH] + "..."
                if len(summary) > CI_FORMAT_PREVIEW_LENGTH
                else summary
            )
            lines.append(f"   Summary: {preview}")
        lines.append("")

    return "\n".join(lines)


def format_session_search_results(sessions: list[dict[str, Any]]) -> str:
    """Format session search results for agent consumption.

    Sessions are searched via embedded summaries in ChromaDB.

    Args:
        sessions: Session search results from RetrievalEngine.

    Returns:
        Formatted markdown string with session results.
    """
    if not sessions:
        return "No sessions found."

    lines = [f"Found {len(sessions)} sessions:\n"]
    for i, s in enumerate(sessions, 1):
        session_id = s.get("id", "unknown")
        title = s.get("title") or "Untitled"
        if title and len(title) > CI_FORMAT_TITLE_MAX_LENGTH:
            title = title[: CI_FORMAT_TITLE_MAX_LENGTH - 3] + "..."
        confidence = s.get("confidence", "medium")
        preview = s.get("preview", "")
        parent_session_id = s.get("parent_session_id")
        chain_position = s.get("chain_position")
        created_at_epoch = s.get("created_at_epoch")

        lines.append(f"{i}. **{title}** [{confidence}]")

        # Build metadata line with session ID and optional lineage info
        meta_parts = [f"ID: {session_id}"]
        if chain_position:
            meta_parts.append(f"chain: {chain_position}")
        if parent_session_id:
            meta_parts.append(f"parent: {parent_session_id[:12]}")
        if created_at_epoch:
            from datetime import UTC, datetime

            created_dt = datetime.fromtimestamp(created_at_epoch, tz=UTC)
            meta_parts.append(f"created: {created_dt.strftime(CI_FORMAT_DATE_DISPLAY)}")
        lines.append(f"   {' | '.join(meta_parts)}")

        if preview:
            # Truncate and indent preview
            preview_text = (
                preview[:CI_FORMAT_PREVIEW_LENGTH] + "..."
                if len(preview) > CI_FORMAT_PREVIEW_LENGTH
                else preview
            )
            lines.append(f"   {preview_text}")
        lines.append("")

    return "\n".join(lines)


def format_search_results(result: SearchResult, query: str | None = None) -> str:
    """Format combined search results from RetrievalEngine.

    Args:
        result: SearchResult from RetrievalEngine.search().
        query: Optional query string for header.

    Returns:
        Formatted markdown string with all result types.
    """
    output_parts = []

    if query:
        output_parts.append(f"Search results for: {query}\n")

    if result.code:
        output_parts.append("## Code Results\n")
        output_parts.append(format_code_results(result.code))

    if result.memory:
        output_parts.append("\n## Memory Results\n")
        output_parts.append(format_memory_results(result.memory))

    if hasattr(result, "plans") and result.plans:
        output_parts.append("\n## Plan Results\n")
        output_parts.append(format_plan_results(result.plans))

    if hasattr(result, "sessions") and result.sessions:
        output_parts.append("\n## Session Results\n")
        output_parts.append(format_session_search_results(result.sessions))

    if not output_parts or (query and len(output_parts) == 1):
        output_parts.append("No results found for your query.")

    return "\n".join(output_parts)


def format_context_results(
    code: list[dict[str, Any]] | None = None,
    memories: list[dict[str, Any]] | None = None,
) -> str:
    """Format task context results.

    Args:
        code: Relevant code chunks.
        memories: Related memories.

    Returns:
        Formatted markdown string with context.
    """
    parts = []

    if code:
        parts.append("## Relevant Code\n")
        for r in code:
            file_path = r.get("file_path", r.get("filepath", "unknown"))
            chunk_type = r.get("chunk_type", "code")
            name = r.get("name", "")
            start_line = r.get("start_line", 0)
            relevance = r.get("relevance", 0)

            parts.append(
                f"### {file_path} ({chunk_type}: {name})\n"
                f"Line {start_line} (relevance: {round(relevance, 2)})\n"
            )

    if memories:
        parts.append("## Related Memories\n")
        parts.append(format_memory_results(memories))

    if not parts:
        parts.append(
            "No specific context found for this task. This may be a new area of the codebase."
        )

    return "\n".join(parts)


def format_activity_results(activities: list[dict[str, Any]]) -> str:
    """Format activity list for agent consumption.

    Args:
        activities: Activity records from ActivityStore.

    Returns:
        Formatted markdown string with activity details.
    """
    if not activities:
        return "No activities found."

    lines = [f"Found {len(activities)} activities:\n"]
    for i, a in enumerate(activities, 1):
        tool_name = a.get("tool_name", "unknown")
        success = a.get("success", True)
        status_icon = "+" if success else "x"
        file_path = a.get("file_path", "")
        timestamp = a.get("timestamp", "")
        error_message = a.get("error_message", "")
        output_summary = a.get("tool_output_summary", "")

        lines.append(f"{i}. [{status_icon}] {tool_name}")
        meta_parts = []
        if file_path:
            meta_parts.append(f"file: {file_path}")
        if timestamp:
            meta_parts.append(f"at: {timestamp}")
        if meta_parts:
            lines.append(f"   {' | '.join(meta_parts)}")
        if not success and error_message:
            preview = (
                error_message[:CI_FORMAT_PREVIEW_LENGTH] + "..."
                if len(error_message) > CI_FORMAT_PREVIEW_LENGTH
                else error_message
            )
            lines.append(f"   Error: {preview}")
        elif output_summary:
            preview = (
                output_summary[:CI_FORMAT_PREVIEW_LENGTH] + "..."
                if len(output_summary) > CI_FORMAT_PREVIEW_LENGTH
                else output_summary
            )
            lines.append(f"   Output: {preview}")
        lines.append("")

    return "\n".join(lines)


def format_network_search_results(results: list[dict[str, Any]]) -> str:
    """Format federated network search results for agent consumption.

    Each result includes a source machine_id badge to distinguish
    which peer node contributed the result.

    Args:
        results: Network search results from relay, each containing
            machine_id, observation/summary, relevance, etc.

    Returns:
        Formatted markdown string with network results.
    """
    if not results:
        return "No network results found."

    lines = [f"Found {len(results)} network results:\n"]
    for i, r in enumerate(results, 1):
        machine_id = r.get("machine_id", "unknown")
        result_type = r.get("_result_type", "")
        memory_type = r.get("memory_type", "")
        relevance = r.get("relevance")
        confidence = r.get("confidence", "medium")

        # Extract display text — field name depends on result type.
        # memory: summary/observation, plan/session: title + preview
        text = r.get("observation", r.get("summary", ""))
        if not text:
            title = r.get("title", "")
            preview = r.get("preview", "")
            if title and preview:
                text = f"{title}: {preview}"
            else:
                text = title or preview

        # Build header with type badge
        header = f"{i}. [{machine_id}]"
        type_label = memory_type or result_type
        if type_label:
            header += f" [{type_label}]"
        if relevance is not None:
            header += f" (relevance: {round(relevance, 2)})"
        elif confidence:
            header += f" [{confidence}]"

        lines.append(header)
        if text:
            lines.append(f"   {text}")
        lines.append("")

    return "\n".join(lines)


def format_node_results(nodes: list[dict[str, Any]]) -> str:
    """Format node list for agent consumption.

    Args:
        nodes: Node info dicts from relay_client.online_nodes.

    Returns:
        Formatted markdown string with node details.
    """
    if not nodes:
        return "No nodes currently online."

    lines = [f"Found {len(nodes)} node(s):\n"]
    for i, n in enumerate(nodes, 1):
        machine_id = n.get("machine_id", "unknown")
        online = n.get("online", False)
        oak_version = n.get("oak_version", "")
        capabilities = n.get("capabilities", [])
        status_icon = "+" if online else "-"

        header = f"{i}. [{status_icon}] {machine_id}"
        if oak_version:
            header += f" (v{oak_version})"
        lines.append(header)

        if capabilities:
            lines.append(f"   Capabilities: {', '.join(capabilities)}")
        lines.append("")

    return "\n".join(lines)


def format_federated_tool_results(results: list[dict[str, Any]]) -> str:
    """Format federated tool call results for agent consumption.

    Each result includes a source machine_id badge to distinguish
    which peer node contributed the result.

    Args:
        results: Federated results from relay, each containing
            from_machine_id, result, and optional error.

    Returns:
        Formatted markdown string with federated results.
    """
    if not results:
        return "No results from peer nodes."

    lines = []
    for r in results:
        machine_id = r.get("from_machine_id", "unknown")
        error = r.get("error")
        result = r.get("result")

        if error:
            lines.append(f"### [{machine_id}] Error\n{error}\n")
        elif result:
            # Result may be a dict with content, or a plain string
            if isinstance(result, dict):
                content = result.get("content", [])
                if isinstance(content, list):
                    for item in content:
                        if isinstance(item, dict) and item.get("type") == "text":
                            lines.append(f"### [{machine_id}]\n{item['text']}\n")
                else:
                    lines.append(f"### [{machine_id}]\n{result}\n")
            else:
                lines.append(f"### [{machine_id}]\n{result}\n")

    return "\n".join(lines) if lines else "No results from peer nodes."


def extract_text_from_mcp_result(tool_result: Any) -> str:
    """Extract text content from an MCP-style tool result dict.

    MCP tool results follow the shape ``{content: [{type: "text", text: "..."}]}``.
    This helper normalises that into a plain string.

    Args:
        tool_result: Raw result from a remote tool call.

    Returns:
        Extracted text, or ``str(tool_result)`` as fallback.
    """
    if isinstance(tool_result, dict):
        content = tool_result.get("content", [])
        if isinstance(content, list):
            texts = [
                item.get("text", "")
                for item in content
                if isinstance(item, dict) and item.get("type") == "text"
            ]
            if texts:
                return "\n".join(texts)
        return str(tool_result)
    return str(tool_result) if tool_result is not None else ""


def format_swarm_search_results(results: dict[str, Any]) -> str:
    """Format swarm search results for LLM consumption.

    Each result includes a source project badge to distinguish
    which swarm node contributed the result.

    Args:
        results: Swarm search response dict containing ``results`` list,
            each with project, type, and content fields.

    Returns:
        Formatted markdown string with swarm search results.
    """
    items = results.get("results", [])
    if not items:
        return "No swarm search results found."

    lines = [f"Found {len(items)} swarm result(s):\n"]
    for i, r in enumerate(items, 1):
        project = r.get("project", r.get("machine_id", "unknown"))
        result_type = r.get("type", r.get("_result_type", ""))
        memory_type = r.get("memory_type", "")
        relevance = r.get("relevance")

        # Extract display text — field depends on result type
        text = r.get("observation", r.get("summary", ""))
        if not text:
            title = r.get("title", "")
            preview = r.get("preview", "")
            if title and preview:
                text = f"{title}: {preview}"
            else:
                text = title or preview
        if not text:
            text = r.get("content", "")

        # Build header with project badge
        header = f"{i}. [{project}]"
        type_label = memory_type or result_type
        if type_label:
            header += f" [{type_label}]"
        if relevance is not None:
            header += f" (relevance: {round(relevance, 2)})"

        lines.append(header)
        if text:
            preview_text = (
                text[:CI_FORMAT_PREVIEW_LENGTH] + "..."
                if len(text) > CI_FORMAT_PREVIEW_LENGTH
                else text
            )
            lines.append(f"   {preview_text}")
        lines.append("")

    return "\n".join(lines)


def format_swarm_nodes(data: dict[str, Any]) -> str:
    """Format swarm node list for LLM consumption.

    Args:
        data: Swarm nodes response dict containing ``nodes`` list,
            each with project, status, and capability fields.

    Returns:
        Formatted markdown string with swarm node details.
    """
    nodes = data.get("nodes", [])
    if not nodes:
        return "No swarm nodes found."

    lines = [f"Found {len(nodes)} swarm node(s):\n"]
    for i, n in enumerate(nodes, 1):
        project = n.get("project_slug", n.get("project", n.get("machine_id", "unknown")))
        # Swarm returns "stale" (True = offline), team returns "online"/"connected"
        if "stale" in n:
            online = not n["stale"]
        else:
            online = n.get("online", n.get("connected", False))
        oak_version = n.get("oak_version", "")
        capabilities = n.get("capabilities", [])
        status_icon = "+" if online else "-"

        header = f"{i}. [{status_icon}] {project}"
        if oak_version:
            header += f" (v{oak_version})"
        lines.append(header)

        if capabilities:
            lines.append(f"   Capabilities: {', '.join(capabilities)}")
        lines.append("")

    return "\n".join(lines)


def format_stats_results(
    code_chunks: int = 0,
    unique_files: int = 0,
    memory_count: int = 0,
    observation_count: int = 0,
    status_breakdown: dict[str, int] | None = None,
) -> str:
    """Format project statistics.

    Args:
        code_chunks: Number of indexed code chunks.
        unique_files: Number of unique files indexed.
        memory_count: Number of memories stored.
        observation_count: Number of observations in activity store.
        status_breakdown: Observation counts by status (e.g. {"active": 42, "resolved": 10}).

    Returns:
        Formatted markdown string with stats.
    """
    lines = ["## Project Statistics\n"]

    lines.append("### Code Index")
    lines.append(f"- Indexed chunks: {code_chunks}")
    lines.append(f"- Unique files: {unique_files}")
    lines.append(f"- Total memories: {memory_count}")
    lines.append("")

    if observation_count > 0:
        lines.append("### Activity History")
        lines.append(f"- Total observations: {observation_count}")
        if status_breakdown:
            lines.append("- Status breakdown:")
            for status, count in sorted(status_breakdown.items()):
                lines.append(f"  - {status}: {count}")

    return "\n".join(lines)
