"""Content list command implementation."""

from typing import Annotated, Optional
from uuid import UUID

import typer

from alloy_runtime_types.dtos.structured_filter import ConditionSpec, LogicalSpec
from alloy_runtime_types.enums.content_enums import (
    ContentSortField,
    ContentStorageType,
    SortOrder,
)
from alloy_runtime_types.parsing.filter_parser import (
    FilterParseError,
    parse_filter_spec,
)
from cli.commands.content.list.export_formatters import get_available_formats
from cli.commands.content.list.export_handler import export_content
from cli.commands.content.list.presenter import present_content_list
from cli.commands.flag_utils import validate_output_mode, validate_uuid
from cli.infrastructure.command import async_command, authenticated_client
from cli.infrastructure.field_extractor import (
    FieldExtractor,
    OutputFormat,
    parse_fields_option,
    parse_jq_keys_option,
)
from cli.infrastructure.filter_parser import merge_filter_specs, parse_where_filters


def content_list_command(
    search: Optional[str] = typer.Option(
        None,
        "-s",
        "--search",
        help="Fuzzy search terms",
    ),
    agent: Optional[str] = typer.Option(
        None,
        "-a",
        "--agent",
        help="Filter by agent UUID (assistant-only)",
    ),
    session: Optional[str] = typer.Option(
        None,
        "--session",
        help="Filter by session UUID",
    ),
    tag_path: Optional[str] = typer.Option(
        None,
        "-t",
        "--tag",
        help="Tag pattern (e.g., '*.email.*')",
    ),
    exclude_tags: Annotated[
        Optional[list[str]],
        typer.Option(
            "-x",
            "--exclude",
            help="Exclude tag pattern (repeatable)",
        ),
    ] = None,
    content_type: Optional[str] = typer.Option(
        None,
        "--content-type",
        help="Filter by content type",
    ),
    storage_type: Optional[str] = typer.Option(
        None,
        "--storage-type",
        help="Storage type: text or structured",
    ),
    start_date: Optional[str] = typer.Option(
        None,
        "--start-date",
        help="After date (ISO 8601)",
    ),
    end_date: Optional[str] = typer.Option(
        None,
        "--end-date",
        help="Before date (ISO 8601)",
    ),
    where: Annotated[
        Optional[list[str]],
        typer.Option(
            "-w",
            "--where",
            help="Filter: field=value. Repeatable.",
        ),
    ] = None,
    structured_filter: Optional[str] = typer.Option(
        None,
        "--structured-filter",
        help="Advanced JSONB filter (JSON)",
    ),
    metadata_filter: Optional[str] = typer.Option(
        None,
        "--metadata-filter",
        help="JSONB filter on part_metadata (JSON)",
    ),
    sort_by: Optional[str] = typer.Option(
        None,
        "--sort-by",
        help="Sort field: created_at, updated_at",
    ),
    sort_order: Optional[str] = typer.Option(
        None,
        "--sort-order",
        help="Sort: asc or desc",
    ),
    limit: int = typer.Option(
        50,
        "-l",
        "--limit",
        min=1,
        max=100,
        help="Max results",
    ),
    cursor: Optional[str] = typer.Option(
        None,
        "--cursor",
        help="Pagination cursor",
    ),
    export_format: Optional[str] = typer.Option(
        None,
        "-e",
        "--export",
        help="Export: markdown, xml, json, plain, numbered",
    ),
    output_mode: str = typer.Option(
        "console",
        "-o",
        "--output",
        help="Output: console, clipboard, file",
    ),
    output_file: Optional[str] = typer.Option(
        None,
        "-O",
        "--output-file",
        help="Output file path",
    ),
    group_by: Optional[str] = typer.Option(
        None,
        "-g",
        "--group-by",
        help="Group by tag prefix",
    ),
    fields: Optional[str] = typer.Option(
        None,
        "-F",
        "--fields",
        help="Extract fields (comma-separated)",
    ),
    jq_keys: Optional[str] = typer.Option(
        None,
        "--jq",
        help="Sub-select JSON keys (jq-style)",
    ),
    fields_format: Optional[str] = typer.Option(
        None,
        "--fields-format",
        help="Fields output: tsv, csv, json, jsonl, values",
    ),
) -> None:
    """List content parts from user and assistant messages.

    Examples:
        alloy content list
        alloy content list -s "python code"
        alloy content list -a <agent-id>
        alloy content list -t "*.email.*" -x rejected
        alloy content list -w status=complete
        alloy content list -e markdown -o clipboard
        alloy content list -F id,content_text --jq profession
    """
    # Parse UUIDs
    agent_uuid: Optional[UUID] = None
    if agent:
        agent_uuid = validate_uuid(agent, "agent")

    session_uuid: Optional[UUID] = None
    if session:
        session_uuid = validate_uuid(session, "session")

    # Parse enums
    storage_type_enum: Optional[ContentStorageType] = None
    if storage_type:
        try:
            storage_type_enum = ContentStorageType(storage_type)
        except ValueError:
            valid = ", ".join(st.value for st in ContentStorageType)
            raise typer.BadParameter(
                f"Invalid storage type '{storage_type}'. Valid: {valid}"
            )

    sort_by_enum: Optional[ContentSortField] = None
    if sort_by:
        try:
            sort_by_enum = ContentSortField(sort_by)
        except ValueError:
            valid = ", ".join(sf.value for sf in ContentSortField)
            raise typer.BadParameter(f"Invalid sort field '{sort_by}'. Valid: {valid}")

    sort_order_enum: Optional[SortOrder] = None
    if sort_order:
        try:
            sort_order_enum = SortOrder(sort_order)
        except ValueError:
            valid = ", ".join(so.value for so in SortOrder)
            raise typer.BadParameter(
                f"Invalid sort order '{sort_order}'. Valid: {valid}"
            )

    # Validate export options
    if export_format:
        available_formats = get_available_formats()
        if export_format not in available_formats:
            raise typer.BadParameter(
                f"Invalid export '{export_format}'. Valid: {', '.join(available_formats)}"
            )
        validate_output_mode(output_mode, output_file)

    # Validate field selection
    fields_output_format: Optional[OutputFormat] = None
    jq_keys_list: list[str] | None = None
    if fields:
        if export_format:
            raise typer.BadParameter("--fields and --export are mutually exclusive")
        if fields_format:
            try:
                fields_output_format = OutputFormat(fields_format)
            except ValueError:
                valid = ", ".join(f.value for f in OutputFormat)
                raise typer.BadParameter(
                    f"Invalid fields format '{fields_format}'. Valid: {valid}"
                )
        else:
            fields_output_format = OutputFormat.TSV
        if jq_keys:
            jq_keys_list = parse_jq_keys_option(jq_keys)
    elif jq_keys:
        raise typer.BadParameter("--jq requires --fields/-F")

    # Parse filters
    where_spec = parse_where_filters(where)

    try:
        json_filter_spec = parse_filter_spec(structured_filter)
    except FilterParseError as e:
        raise typer.BadParameter(f"Invalid structured-filter: {e.message}")

    filter_spec = merge_filter_specs(where_spec, json_filter_spec)

    # Parse metadata filter
    try:
        metadata_filter_spec = parse_filter_spec(metadata_filter)
    except FilterParseError as e:
        raise typer.BadParameter(f"Invalid metadata-filter: {e.message}")

    _execute_list(
        search=search,
        agent_id=agent_uuid,
        session_id=session_uuid,
        tag_path=tag_path,
        exclude_tags=exclude_tags,
        content_type=content_type,
        storage_type=storage_type_enum,
        start_date=start_date,
        end_date=end_date,
        structured_filter=filter_spec,
        metadata_filter=metadata_filter_spec,
        sort_by=sort_by_enum,
        sort_order=sort_order_enum,
        limit=limit,
        cursor=cursor,
        export_format=export_format,
        output_mode=output_mode,
        output_file=output_file,
        group_by=group_by,
        fields=fields,
        jq_keys=jq_keys_list,
        fields_format=fields_output_format,
    )


@async_command
async def _execute_list(
    search: Optional[str],
    agent_id: Optional[UUID],
    session_id: Optional[UUID],
    tag_path: Optional[str],
    exclude_tags: Optional[list[str]],
    content_type: Optional[str],
    storage_type: Optional[ContentStorageType],
    start_date: Optional[str],
    end_date: Optional[str],
    structured_filter: Optional[ConditionSpec | LogicalSpec],
    metadata_filter: Optional[ConditionSpec | LogicalSpec],
    sort_by: Optional[ContentSortField],
    sort_order: Optional[SortOrder],
    limit: int,
    cursor: Optional[str],
    export_format: Optional[str] = None,
    output_mode: str = "console",
    output_file: Optional[str] = None,
    group_by: Optional[str] = None,
    fields: Optional[str] = None,
    jq_keys: list[str] | None = None,
    fields_format: Optional[OutputFormat] = None,
) -> None:
    """Execute list content parts operation."""
    async with authenticated_client() as (_config, client):
        response = await client.list_content_parts(
            search=search,
            agent_id=agent_id,
            session_id=session_id,
            tag_path=tag_path,
            exclude_tags=exclude_tags,
            content_type_id=content_type,
            storage_type=storage_type,
            start_date=start_date,
            end_date=end_date,
            structured_filter=structured_filter,
            metadata_filter=metadata_filter,
            sort_by=sort_by,
            sort_order=sort_order,
            limit=limit,
            cursor=cursor,
        )

    if fields:
        field_list = parse_fields_option(fields)
        extractor = FieldExtractor(field_list, jq_keys=jq_keys)
        results = extractor.extract_many(response.content_parts)
        output = extractor.format(results, fields_format or OutputFormat.TSV)
        print(output)
    elif export_format:
        if not export_content(
            items=response.content_parts,
            export_format=export_format,
            output_mode=output_mode,
            output_file=output_file,
            group_by=group_by,
        ):
            raise typer.Exit(code=1)
    else:
        present_content_list(response)
