"""Pipeline execution update command implementation."""

import json
from typing import Any, Optional, cast
from uuid import UUID

import typer

from alloy_runtime_types.dtos.pipeline import UpdatePipelineExecutionRequest

from cli.commands.flag_utils import (
    parse_tags,
    require_exclusive,
    require_update_flags,
    validate_uuid,
)
from cli.commands.pipelines.executions.update.presenter import present_execution_update
from cli.infrastructure.command import async_command, authenticated_client


def pipeline_execution_update_command(
    pipeline_id: str = typer.Argument(
        ...,
        help="Pipeline UUID",
    ),
    execution_id: str = typer.Argument(
        ...,
        help="Execution UUID",
    ),
    metadata: Optional[str] = typer.Option(
        None,
        "--metadata",
        help='Replace metadata with JSON (e.g. \'{"key": "value"}\')',
    ),
    metadata_merge: Optional[str] = typer.Option(
        None,
        "--metadata-merge",
        help="Merge these JSON keys into existing metadata",
    ),
    tags: Optional[str] = typer.Option(
        None,
        "--tags",
        help="Replace tags (comma-separated, e.g. 'reviewed,approved')",
    ),
) -> None:
    """Update a pipeline execution's metadata or tags.

    Examples:
        alloy pipelines executions update 123e... 456e... --metadata '{"status": "reviewed"}'
        alloy pipelines executions update 123e... 456e... --metadata-merge '{"step": 2}'
        alloy pipelines executions update 123e... 456e... --tags reviewed,approved
    """
    pipeline_uuid = validate_uuid(pipeline_id, "pipeline")
    execution_uuid = validate_uuid(execution_id, "execution")

    require_exclusive(
        ("--metadata", metadata),
        ("--metadata-merge", metadata_merge),
    )

    require_update_flags(
        ("--metadata", metadata),
        ("--metadata-merge", metadata_merge),
        ("--tags", tags),
    )

    parsed_metadata = _parse_json(metadata) if metadata else None
    parsed_metadata_merge = _parse_json(metadata_merge) if metadata_merge else None
    parsed_tags = parse_tags(tags) if tags else None

    _execute_update(
        pipeline_id=pipeline_uuid,
        execution_id=execution_uuid,
        metadata=parsed_metadata,
        metadata_merge=parsed_metadata_merge,
        tags=parsed_tags,
    )


def _parse_json(value: str) -> dict[str, Any]:
    try:
        result = json.loads(value)
        if not isinstance(result, dict):
            raise typer.BadParameter("JSON must be an object")
        return cast(dict[str, Any], result)
    except json.JSONDecodeError as e:
        raise typer.BadParameter(f"Invalid JSON: {e}")


@async_command
async def _execute_update(
    pipeline_id: UUID,
    execution_id: UUID,
    metadata: dict[str, Any] | None,
    metadata_merge: dict[str, Any] | None,
    tags: list[str] | None,
) -> None:
    async with authenticated_client() as (_config, client):
        request = UpdatePipelineExecutionRequest(
            metadata=metadata,
            metadata_merge=metadata_merge,
            tags=tags,
        )
        response = await client.update_pipeline_execution(
            pipeline_id,
            execution_id,
            request,
        )

    present_execution_update(response)
