"""Template update command implementation."""

from pathlib import Path
from typing import Optional

import typer

from cli.commands.flag_utils import (
    parse_tags,
    require_exclusive,
    require_update_flags,
    validate_template_content_type,
    validate_visibility,
)
from cli.commands.templates.update.presenter import present_template_update_success
from cli.infrastructure.command import async_command, authenticated_client
from cli.infrastructure.file_content import FileContentError, resolve_content
from alloy_runtime_types.dtos.templates import UpdateTemplateRequest
from alloy_runtime_types.enums.template_enums import (
    TemplateContentType,
    TemplateVisibility,
)
from alloy_runtime_sdk.exceptions.errors import NotFoundError


def templates_update_command(
    template: str = typer.Argument(..., help="Template name or UUID"),
    name: Optional[str] = typer.Option(
        None,
        "-n",
        "--name",
        help="New name",
    ),
    description: Optional[str] = typer.Option(
        None,
        "-d",
        "--description",
        help="New description",
    ),
    content_type: Optional[str] = typer.Option(
        None,
        "--type",
        help="New type: system_instruction, user_message, fragment",
    ),
    visibility: Optional[str] = typer.Option(
        None,
        "-V",
        "--visibility",
        help="New visibility: private, organization, public",
    ),
    content: Optional[str] = typer.Option(
        None,
        "-c",
        "--content",
        help="New content (or @file)",
    ),
    content_file: Optional[Path] = typer.Option(
        None,
        "-f",
        "--file",
        help="Read new content from file",
        exists=True,
        readable=True,
    ),
    tags: Optional[str] = typer.Option(
        None,
        "-t",
        "--tags",
        help="Replace tags (comma-separated)",
    ),
    clear_description: bool = typer.Option(
        False,
        "--clear-description",
        help="Clear description",
    ),
    clear_tags: bool = typer.Option(
        False,
        "--clear-tags",
        help="Remove all tags",
    ),
) -> None:
    """Update an existing template.

    Only specified fields are updated. Content updates only allowed for single-version templates.

    Examples:
        alloy templates update my-template -n new-name
        alloy templates update my-template -d "New description"
        alloy templates update my-template --clear-description
        alloy templates update my-template -f template.jinja2
        alloy templates update my-template -V public -t "new.tag"
    """
    # Validate exclusive flags
    require_exclusive(("--file", content_file), ("--content", content))
    require_exclusive(
        ("--description", description), ("--clear-description", clear_description)
    )
    require_exclusive(("--tags", tags), ("--clear-tags", clear_tags))

    # Check at least one update flag provided
    require_update_flags(
        ("--name", name),
        ("--description", description),
        ("--type", content_type),
        ("--visibility", visibility),
        ("--content", content),
        ("--file", content_file),
        ("--tags", tags),
        ("--clear-description", clear_description),
        ("--clear-tags", clear_tags),
    )

    _execute_update(
        template_identifier=template,
        name=name,
        description=description,
        content_type=content_type,
        visibility=visibility,
        content=content,
        content_file=content_file,
        tags=tags,
        clear_description=clear_description,
        clear_tags=clear_tags,
    )


@async_command
async def _execute_update(
    template_identifier: str,
    name: Optional[str],
    description: Optional[str],
    content_type: Optional[str],
    visibility: Optional[str],
    content: Optional[str],
    content_file: Optional[Path],
    tags: Optional[str],
    clear_description: bool,
    clear_tags: bool,
) -> None:
    """Execute the template update operation."""
    # Resolve @file references
    try:
        content = resolve_content(content)
    except FileContentError as e:
        raise typer.BadParameter(str(e))

    # Get content from file if specified
    template_content: Optional[str] = None
    if content_file is not None:
        template_content = content_file.read_text()
    elif content is not None:
        template_content = content

    # Validate enums
    content_type_enum: Optional[TemplateContentType] = None
    if content_type is not None:
        content_type_enum = validate_template_content_type(content_type)

    visibility_enum: Optional[TemplateVisibility] = None
    if visibility is not None:
        visibility_enum = validate_visibility(visibility)

    # Handle tags
    tag_paths: Optional[list[str]] = None
    if clear_tags:
        tag_paths = []
    elif tags is not None:
        tag_paths = parse_tags(tags)
        if not tag_paths:
            raise typer.BadParameter("--tags must contain at least one tag")

    # Handle description
    final_description: Optional[str] = description
    if clear_description:
        final_description = ""

    # Build update request
    request = UpdateTemplateRequest(
        name=name,
        description=final_description
        if (description is not None or clear_description)
        else None,
        content_type=content_type_enum,
        visibility=visibility_enum,
        content=template_content,
        tag_paths=tag_paths,
    )

    async with authenticated_client() as (_config, client):
        try:
            await client.get_template(template_identifier)
        except NotFoundError:
            raise typer.BadParameter(
                f"Template '{template_identifier}' not found. Use 'alloy templates list' to see available templates."
            )

        response = await client.update_template(template_identifier, request)

    present_template_update_success(response)
