"""Knowledge collection update command implementation."""

import json
from typing import Any, Optional, cast

import typer

from cli.commands.flag_utils import require_update_flags, validate_uuid
from cli.commands.knowledge.collections.update.presenter import (
    present_collection_update_success,
)
from cli.infrastructure.command import async_command, authenticated_client
from cli.infrastructure.file_content import resolve_content_or_raise
from alloy_runtime_types.dtos.knowledge import (
    CollectionPreprocessingConfig,
    UpdateCollectionRequest,
)


SEMANTIC_ROUTING_PROFILE_OPTION = "--semantic-routing-profile"


class _CollectionUpdateRequestProxy:
    def __init__(
        self,
        base_request: UpdateCollectionRequest,
        semantic_routing_profile: dict[str, Any],
    ) -> None:
        self._base_request = base_request
        self._semantic_routing_profile = semantic_routing_profile

    def model_dump(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
        payload = self._base_request.model_dump(*args, **kwargs)
        payload["semantic_routing_profile"] = self._semantic_routing_profile
        return payload

    def __getattr__(self, name: str) -> Any:
        return getattr(self._base_request, name)


def _parse_semantic_routing_profile_option(
    value: object,
) -> dict[str, Any] | None:
    if value is None or not isinstance(value, str):
        return None

    resolved = resolve_content_or_raise(value)
    if resolved is None:
        return None

    try:
        result = json.loads(resolved)
    except json.JSONDecodeError as error:
        raise typer.BadParameter(
            f"Invalid JSON for {SEMANTIC_ROUTING_PROFILE_OPTION}: {error}"
        )

    if not isinstance(result, dict):
        raise typer.BadParameter(
            f"{SEMANTIC_ROUTING_PROFILE_OPTION} must be a JSON object"
        )

    return cast(dict[str, Any], result)


def _build_update_collection_request(
    *,
    name: str | None,
    description: str | None,
    rerank_instruction: str | None,
    min_length: int | None,
    semantic_routing_profile: str | None,
) -> UpdateCollectionRequest | _CollectionUpdateRequestProxy:
    parsed_semantic_routing_profile = _parse_semantic_routing_profile_option(
        semantic_routing_profile
    )

    payload: dict[str, Any] = {}
    if name is not None:
        payload["name"] = name
    if description is not None:
        payload["description"] = description
    if rerank_instruction is not None:
        payload["rerank_instruction"] = rerank_instruction
    if min_length is not None:
        payload["preprocessing_config"] = CollectionPreprocessingConfig(
            min_length=min_length
        )

    base_request = UpdateCollectionRequest.model_validate(payload)

    if parsed_semantic_routing_profile is None:
        return base_request

    if "semantic_routing_profile" in UpdateCollectionRequest.model_fields:
        return UpdateCollectionRequest.model_validate(
            {
                **base_request.model_dump(exclude_unset=True),
                "semantic_routing_profile": parsed_semantic_routing_profile,
            }
        )

    return _CollectionUpdateRequestProxy(
        base_request=base_request,
        semantic_routing_profile=parsed_semantic_routing_profile,
    )


def knowledge_collections_update_command(
    collection_id: str = typer.Argument(
        ...,
        help="Collection UUID or name to update",
    ),
    name: Optional[str] = typer.Option(
        None,
        "-n",
        "--name",
        help="New collection name",
    ),
    description: Optional[str] = typer.Option(
        None,
        "-d",
        "--description",
        help="New collection description",
    ),
    rerank_instruction: Optional[str] = typer.Option(
        None,
        "-r",
        "--rerank-instruction",
        help="New default instruction for Voyage AI reranking (max 500 chars)",
    ),
    min_length: Optional[int] = typer.Option(
        None,
        "--min-length",
        min=0,
        help="Minimum characters required after preprocessing. Set to 0 to disable length filtering.",
    ),
    semantic_routing_profile: Optional[str] = typer.Option(
        None,
        SEMANTIC_ROUTING_PROFILE_OPTION,
        help="Semantic retrieval routing profile patch as a JSON object or @file.json.",
    ),
) -> None:
    """Update a knowledge collection.

    At least one update flag is required.

    Examples:
        alloy knowledge collections update <uuid> -n "New Name"
        alloy knowledge collections update <uuid> -d "Updated description"
        alloy knowledge collections update <uuid> -r "Prioritize recent documents"
        alloy knowledge collections update <uuid> -n "Docs" -d "Documentation" -r "Focus on API details"
    """
    require_update_flags(
        ("--name", name),
        ("--description", description),
        ("--rerank-instruction", rerank_instruction),
        ("--min-length", min_length),
        (SEMANTIC_ROUTING_PROFILE_OPTION, semantic_routing_profile),
    )

    _execute_update_from_flags(
        collection_id=collection_id,
        name=name,
        description=description,
        rerank_instruction=rerank_instruction,
        min_length=min_length,
        semantic_routing_profile=semantic_routing_profile,
    )


def _execute_update_from_flags(
    collection_id: str,
    name: Optional[str],
    description: Optional[str],
    rerank_instruction: Optional[str],
    min_length: Optional[int],
    semantic_routing_profile: Optional[str],
) -> None:
    """Build and execute collection update from command flags."""
    # Validate collection ID if it looks like a UUID
    # (Server accepts both UUID and name, so we only validate UUID format)
    try:
        validate_uuid(collection_id, "collection")
    except typer.BadParameter:
        # Not a UUID - treat as name, let server handle validation
        pass

    request = _build_update_collection_request(
        name=name,
        description=description,
        rerank_instruction=rerank_instruction,
        min_length=min_length,
        semantic_routing_profile=semantic_routing_profile,
    )

    _execute_update(collection_id, request)


@async_command
async def _execute_update(
    collection_id: str,
    request: UpdateCollectionRequest | _CollectionUpdateRequestProxy,
) -> None:
    """Execute the collection update with the given request."""
    async with authenticated_client() as (_config, client):
        response = await client.update_collection(
            collection_id, cast(UpdateCollectionRequest, request)
        )

    present_collection_update_success(response)
