"""Knowledge search command implementation."""

from typing import Optional
from uuid import UUID

import typer

from alloy_runtime_types.parsing.filter_parser import (
    FilterParseError,
    parse_filter_spec,
)
from cli.commands.flag_utils import validate_uuid
from cli.commands.knowledge.search.presenter import present_search_results
from cli.infrastructure.command import async_command, authenticated_client
from cli.infrastructure.error_display import display_usage_error
from alloy_runtime_types.dtos.knowledge import SearchRequest


def knowledge_search_command(
    query: str = typer.Argument(..., help="Search query"),
    collections: Optional[str] = typer.Option(
        None,
        "-C",
        "--collections",
        help="Comma-separated collection IDs to search",
    ),
    collection_filter: Optional[str] = typer.Option(
        None,
        "-F",
        "--collection-filter",
        help="Filter by collection name pattern (e.g., 'Product*')",
    ),
    tag_filter: Optional[str] = typer.Option(
        None,
        "-T",
        "--tag-filter",
        help="Filter by document tag pattern (e.g., 'engineering.*', 'docs')",
    ),
    top_k: int = typer.Option(
        5,
        "-k",
        "--top-k",
        min=1,
        max=20,
        help="Number of results to return (1-20)",
    ),
    hyde_agent: Optional[str] = typer.Option(
        None,
        "--hyde-agent",
        help="Agent ID for HyDE hypothetical document generation",
    ),
    hyde_template: Optional[str] = typer.Option(
        None,
        "--hyde-template",
        help="Template ID for HyDE hypothetical document generation",
    ),
    decomposition_agent: Optional[str] = typer.Option(
        None,
        "--decomposition-agent",
        help="Agent ID for query decomposition",
    ),
    decomposition_template: Optional[str] = typer.Option(
        None,
        "--decomposition-template",
        help="Template ID for query decomposition",
    ),
    rerank_instruction: Optional[str] = typer.Option(
        None,
        "-r",
        "--rerank-instruction",
        help="Custom instruction for reranking (e.g., 'Prioritize recent docs')",
    ),
    min_confidence: Optional[float] = typer.Option(
        None,
        "--min-confidence",
        min=0.0,
        max=1.0,
        help="Minimum confidence threshold (0.0-1.0)",
    ),
    retrieval_k: Optional[int] = typer.Option(
        None,
        "--retrieval-k",
        min=1,
        max=1000,
        help="Override initial candidate retrieval count",
    ),
    metadata_filter: Optional[str] = typer.Option(
        None,
        "--metadata-filter",
        help='Filter by chunk metadata as JSON (e.g., \'{"author": "John"}\')',
    ),
) -> None:
    """Search the knowledge base.

    Performs semantic search across document collections using hybrid retrieval
    (vector + full-text search). Opt in to HyDE or query decomposition by
    providing the corresponding agent and template IDs.

    Examples:
        alloy knowledge search "How do I authenticate?"
        alloy knowledge search "API documentation" --collection-filter "Product*"
        alloy knowledge search "complex query" \\
            --hyde-agent <id> --hyde-template <id> \\
            --decomposition-agent <id> --decomposition-template <id>
        alloy knowledge search "query" --retrieval-k 100
        alloy knowledge search "query" --metadata-filter '{"author": "John"}'
    """
    try:
        _execute_search_from_flags(
            query=query,
            collections=collections,
            collection_filter=collection_filter,
            tag_filter=tag_filter,
            top_k=top_k,
            hyde_agent=hyde_agent,
            hyde_template=hyde_template,
            decomposition_agent=decomposition_agent,
            decomposition_template=decomposition_template,
            rerank_instruction=rerank_instruction,
            min_confidence=min_confidence,
            retrieval_k=retrieval_k,
            metadata_filter=metadata_filter,
        )
    except typer.BadParameter as error:
        display_usage_error(str(error), command_path="alloy knowledge search")
        raise typer.Exit(code=2) from error


def _execute_search_from_flags(
    query: str,
    collections: Optional[str],
    collection_filter: Optional[str],
    tag_filter: Optional[str],
    top_k: int,
    hyde_agent: Optional[str],
    hyde_template: Optional[str],
    decomposition_agent: Optional[str],
    decomposition_template: Optional[str],
    rerank_instruction: Optional[str],
    min_confidence: Optional[float],
    retrieval_k: Optional[int],
    metadata_filter: Optional[str],
) -> None:
    """Validate flags and build search request."""
    collection_ids: list[UUID] | None = None
    if collections:
        parsed_collection_ids: list[UUID] = []
        for cid in collections.split(","):
            cid = cid.strip()
            if cid:
                parsed_collection_ids.append(validate_uuid(cid, "collection"))
        collection_ids = parsed_collection_ids

    hyde_agent_id: UUID | None = None
    hyde_template_id: UUID | None = None
    decomposition_agent_id: UUID | None = None
    decomposition_template_id: UUID | None = None

    if hyde_agent:
        hyde_agent_id = validate_uuid(hyde_agent, "hyde-agent")
    if hyde_template:
        hyde_template_id = validate_uuid(hyde_template, "hyde-template")
    if decomposition_agent:
        decomposition_agent_id = validate_uuid(
            decomposition_agent, "decomposition-agent"
        )
    if decomposition_template:
        decomposition_template_id = validate_uuid(
            decomposition_template, "decomposition-template"
        )

    parsed_metadata_filter = None
    if metadata_filter:
        try:
            parsed_metadata_filter = parse_filter_spec(metadata_filter)
        except FilterParseError as error:
            raise typer.BadParameter(
                f"Invalid JSON in --metadata-filter: {error}"
            ) from error

    request = SearchRequest(
        query=query,
        collection_ids=collection_ids,
        collection_filter=collection_filter,
        top_k=top_k,
        metadata_filter=parsed_metadata_filter,
        tag_filter=tag_filter,
        source_date_from=None,
        source_date_to=None,
        rerank_instruction=rerank_instruction,
        retrieval_k=retrieval_k,
        min_retrieval_confidence=min_confidence,
        hyde_agent_id=hyde_agent_id,
        hyde_template_id=hyde_template_id,
        decomposition_agent_id=decomposition_agent_id,
        decomposition_template_id=decomposition_template_id,
    )

    _execute_search(request)


@async_command
async def _execute_search(request: SearchRequest) -> None:
    """Execute knowledge base search."""
    async with authenticated_client() as (_config, client):
        response = await client.search_knowledge(request)

    present_search_results(response)
