"""Knowledge collection create command implementation."""

import json
from typing import Any, Optional, cast

import typer

from cli.commands.flag_utils import validate_section_strategy
from cli.commands.knowledge.collections.create.presenter import (
    present_collection_create_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,
    CreateCollectionRequest,
)


SEMANTIC_ROUTING_PROFILE_OPTION = "--semantic-routing-profile"


class _CollectionCreateRequestProxy:
    def __init__(
        self,
        base_request: CreateCollectionRequest,
        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_create_collection_request(
    *,
    name: str,
    description: str | None,
    chunk_size: int,
    chunk_overlap: int,
    section_strategy: str,
    dedup_threshold: float | None,
    disable_dedup: bool,
    rerank_instruction: str | None,
    min_length: int,
    semantic_routing_profile: str | None,
) -> CreateCollectionRequest | _CollectionCreateRequestProxy:
    strategy = validate_section_strategy(section_strategy)

    if chunk_overlap >= chunk_size:
        raise typer.BadParameter(
            f"Chunk overlap ({chunk_overlap}) must be less than chunk size ({chunk_size})"
        )

    if disable_dedup and dedup_threshold is not None:
        raise typer.BadParameter("Cannot use --disable-dedup with --dedup-threshold")

    final_dedup_threshold: float | None
    if disable_dedup:
        final_dedup_threshold = None
    elif dedup_threshold is not None:
        final_dedup_threshold = dedup_threshold
    else:
        final_dedup_threshold = 0.95

    base_request = CreateCollectionRequest(
        name=name,
        description=description,
        chunk_size_tokens=chunk_size,
        chunk_overlap_tokens=chunk_overlap,
        section_strategy=strategy,
        dedup_similarity_threshold=final_dedup_threshold,
        rerank_instruction=rerank_instruction,
        preprocessing_config=CollectionPreprocessingConfig(min_length=min_length),
    )

    parsed_semantic_routing_profile = _parse_semantic_routing_profile_option(
        semantic_routing_profile
    )
    if parsed_semantic_routing_profile is None:
        return base_request

    if "semantic_routing_profile" in CreateCollectionRequest.model_fields:
        return CreateCollectionRequest.model_validate(
            {
                **base_request.model_dump(),
                "semantic_routing_profile": parsed_semantic_routing_profile,
            }
        )

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


def knowledge_collections_create_command(
    name: str = typer.Option(
        ...,
        "-n",
        "--name",
        help="Collection name",
    ),
    description: Optional[str] = typer.Option(
        None,
        "-d",
        "--description",
        help="Collection description",
    ),
    chunk_size: int = typer.Option(
        512,
        "-c",
        "--chunk-size",
        min=64,
        max=8192,
        help="Target tokens per chunk (64-8192)",
    ),
    chunk_overlap: int = typer.Option(
        50,
        "-o",
        "--chunk-overlap",
        min=0,
        help="Token overlap between chunks",
    ),
    section_strategy: str = typer.Option(
        "paragraph_based",
        "-s",
        "--section-strategy",
        help="Section extraction strategy: heading_based, paragraph_based, fixed_size, none",
    ),
    dedup_threshold: Optional[float] = typer.Option(
        None,
        "--dedup-threshold",
        min=0.50,
        max=1.00,
        help="Dedup similarity threshold (0.50-1.00). Default: 0.95",
    ),
    disable_dedup: bool = typer.Option(
        False,
        "--disable-dedup",
        help="Disable chunk deduplication entirely",
    ),
    rerank_instruction: Optional[str] = typer.Option(
        None,
        "-r",
        "--rerank-instruction",
        help="Default instruction for Voyage AI reranking (max 500 chars)",
    ),
    min_length: int = typer.Option(
        50,
        "--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 as a JSON object or @file.json.",
    ),
) -> None:
    """Create a new knowledge collection.

    Examples:
        alloy knowledge collections create -n "Product Docs"
        alloy knowledge collections create -n "Support" -d "Support articles" -c 1024
        alloy knowledge collections create -n "Wiki" -s heading_based
        alloy knowledge collections create -n "NoDedup" --disable-dedup
        alloy knowledge collections create -n "StrictDedup" --dedup-threshold 0.99
    """
    _execute_create_from_flags(
        name=name,
        description=description,
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        section_strategy=section_strategy,
        dedup_threshold=dedup_threshold,
        disable_dedup=disable_dedup,
        rerank_instruction=rerank_instruction,
        min_length=min_length,
        semantic_routing_profile=semantic_routing_profile,
    )


@async_command
async def _execute_create(
    request: CreateCollectionRequest | _CollectionCreateRequestProxy,
) -> None:
    """Execute the collection creation with the given request."""
    async with authenticated_client() as (_config, client):
        response = await client.create_collection(
            cast(CreateCollectionRequest, request)
        )

    present_collection_create_success(response)


def _execute_create_from_flags(
    name: str,
    description: Optional[str],
    chunk_size: int,
    chunk_overlap: int,
    section_strategy: str,
    dedup_threshold: Optional[float],
    disable_dedup: bool,
    rerank_instruction: Optional[str],
    min_length: int,
    semantic_routing_profile: Optional[str],
) -> None:
    """Build and execute collection creation from command flags."""
    request = _build_create_collection_request(
        name=name,
        description=description,
        chunk_size=chunk_size,
        chunk_overlap=chunk_overlap,
        section_strategy=section_strategy,
        dedup_threshold=dedup_threshold,
        disable_dedup=disable_dedup,
        rerank_instruction=rerank_instruction,
        min_length=min_length,
        semantic_routing_profile=semantic_routing_profile,
    )

    _execute_create(request)
