"""Presenter for knowledge search output."""

import json
from typing import Any, cast

from pydantic import BaseModel
from alloy_runtime_types.dtos.knowledge import SearchResponse
from cli.infrastructure.output import OutputService
from cli.infrastructure.renderers.entity_renderer import FieldConfig
from cli.infrastructure.renderers.list_renderer import ColumnConfig


def _format_optional_text(value: str | None) -> Any:
    if value is None:
        return cast(str, None)
    return value


def _format_timing(value: dict[str, int]) -> str:
    if not value:
        return "{}"
    return ", ".join(f"{key}={timing_ms}ms" for key, timing_ms in value.items())


def _format_optional_list(value: list[str] | None) -> Any:
    if not value:
        return cast(str, None)
    return "\n".join(value)


def _format_tags(value: list[Any]) -> str:
    if not value:
        return "[]"
    return ", ".join(tag.tag_path for tag in value)


def _format_metadata(value: dict[str, Any] | None) -> Any:
    if not value:
        return cast(str, None)
    return json.dumps(value, sort_keys=True, default=str)


def _format_score(value: float) -> str:
    return f"{value:.3f}"


def present_search_results(response: SearchResponse) -> None:
    output = OutputService.get()

    output.entity(
        response,
        "Search Summary",
        [
            FieldConfig("query", "Query"),
            FieldConfig("results", "Results", lambda value: str(len(value))),
            FieldConfig("total_candidates", "Candidates"),
            FieldConfig("avg_relevance_score", "Avg Relevance", _format_score),
            FieldConfig("timing_ms", "Timing", _format_timing),
            FieldConfig(
                "transformations_applied",
                "Transformations",
                lambda value: ", ".join(value) if value else cast(str, None),
            ),
            FieldConfig("sub_queries_used", "Sub Queries", _format_optional_list),
            FieldConfig("original_query", "Original Query", _format_optional_text),
            FieldConfig("search_mode", "Search Mode", _format_optional_text),
        ],
        raw_payload=response,
    )

    output.table(
        cast(list[BaseModel], response.results),
        "Search Results",
        [
            ColumnConfig("document_title", "Document"),
            ColumnConfig("relevance_score", "Score", _format_score),
            ColumnConfig("low_confidence", "Low Confidence"),
            ColumnConfig("document_tags", "Tags", _format_tags),
            ColumnConfig("document_metadata", "Metadata", _format_metadata),
            ColumnConfig("content", "Content"),
        ],
        raw_payload=response.results,
    )
