"""
CLI output formatting helpers.

This module is hand-written (not generated) and provides rich table
formatting for CLI commands.  It can be freely edited without worrying
about regeneration.
"""

from __future__ import annotations

import json
from typing import Any, Callable

from rich import print_json, box

from rich.console import Console
from rich.table import Table

console = Console()

# ---------------------------------------------------------------------------
# Column registry
# ---------------------------------------------------------------------------
# Maps "<resource_path>.<method>" to an ordered list of column names.
# Commands not listed here auto-detect columns from the data.

COLUMN_REGISTRY: dict[str, list[str]] = {
    "containers.list": ["id", "label", "created_at"],
    "models.list": ["slug", "provider", "type"],
    "evaluators.list": ["id", "name", "container_id", "container", "created_at"],
    "evaluation.runs.list": ["id", "status", "container", "created_at"],
    "chat.completions.list": ["id", "model", "container", "created_at"],
    "datasets.list": ["id", "name", "created_at"],
}

# ---------------------------------------------------------------------------
# Single-object registry
# ---------------------------------------------------------------------------
# Maps "<resource_path>.<method>" to a list of (label, accessor) tuples for
# single-object views (e.g. create, retrieve).
#
# accessor is either:
#   - a dot-separated path string like "choices.0.message.content"
#   - a callable (dict) -> Any for custom extraction
#
# Commands not listed here fall back to displaying all keys.

ColumnAccessor = str | Callable[[dict[str, Any]], Any]

SINGLE_REGISTRY: dict[str, list[tuple[str, ColumnAccessor]]] = {
    "chat.completions.create": [
        ("id", "id"),
        ("model", "model"),
        ("prompt_tokens", "usage.prompt_tokens"),
        ("completion_tokens", "usage.completion_tokens"),
        ("total_tokens", "usage.total_tokens"),
        ("content", "choices.0.message.content"),
    ],
    "chat.completions.retrieve": [
        ("id", "id"),
        ("model", "model"),
        ("prompt_tokens", "usage.prompt_tokens"),
        ("completion_tokens", "usage.completion_tokens"),
        ("total_tokens", "usage.total_tokens"),
        ("content", "choices.0.message.content"),
    ],
    "evaluators.create": [
        ("id", "id"),
        ("name", "name"),
        ("description", "description"),
        ("container_id", "container_id"),
        ("container", "container"),
        ("model", "model"),
        ("prompt", "prompt"),
        ("source", "source"),
        ("requirements", "requirements"),
    ],
    "evaluators.retrieve": [
        ("id", "id"),
        ("name", "name"),
        ("description", "description"),
        ("container_id", "container_id"),
        ("container", "container"),
        ("model", "model"),
        ("prompt", "prompt"),
        ("source", "source"),
        ("requirements", "requirements"),
    ],
}


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def format_output(ctx: Any, result: Any) -> None:
    """Dispatch output to JSON or rich table based on ``--json`` flag."""
    if ctx.obj.get("json"):
        print_json(data=result)
        return

    if isinstance(result, dict):
        data = result.get("data")
        if isinstance(data, list):
            _print_list(data, ctx=ctx)
            total = result.get("total")
            if total is not None:
                console.print(f"Showing {len(data)} of {total} results")
            return
        # Single object
        _print_single(result, ctx=ctx)
        return

    if isinstance(result, list):
        _print_list(result, ctx=ctx)
        return

    # Scalar / unknown – fall back to JSON
    print_json(data=result)


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------


def _command_key(ctx: Any) -> str | None:
    """Derive a registry key like ``containers.list`` from the click context."""
    parts: list[str] = []
    c = ctx
    while c and c.info_name:
        parts.append(c.info_name)
        c = c.parent
    # parts is e.g. ["list", "containers", "maniac"] – reverse & drop root
    parts.reverse()
    if parts:
        parts = parts[1:]  # drop "maniac" (the root group name)
    return ".".join(parts) if parts else None


def _columns_for(ctx: Any, rows: list[dict[str, Any]]) -> list[str]:
    """Return the column list for the current command.

    Base columns come from the registry (or are auto-detected from the data).
    If the user passed ``--columns col1,col2,...``, those are appended to the
    base list (duplicates are skipped).
    """
    key = _command_key(ctx)
    if key and key in COLUMN_REGISTRY:
        columns = list(COLUMN_REGISTRY[key])
    elif not rows:
        columns = []
    else:
        columns = list(rows[0].keys())

    extra = ctx.obj.get("columns")
    if extra:
        for col in extra:
            if col not in columns:
                columns.append(col)

    return columns


def _resolve(obj: Any, path: str) -> Any:
    """Resolve a dot-separated path like ``choices.0.message.content``."""
    for part in path.split("."):
        if obj is None:
            return None
        if isinstance(obj, list):
            try:
                obj = obj[int(part)]
            except (ValueError, IndexError):
                return None
        elif isinstance(obj, dict):
            obj = obj.get(part)
        else:
            return None
    return obj


def _format_cell(value: Any, max_width: int = 60) -> str:
    """Format a single cell value for display."""
    if value is None:
        return ""
    if isinstance(value, (dict, list)):
        text = json.dumps(value, default=str)
        if len(text) > max_width:
            return text[: max_width - 3] + "..."
        return text
    return str(value)


def _print_list(rows: list[Any], *, ctx: Any) -> None:
    """Render a list of dicts as a rich table."""
    if not rows:
        console.print("[dim]No results.[/dim]")
        return

    if not isinstance(rows[0], dict):
        # List of scalars – just print each one
        for item in rows:
            console.print(item)
        return

    columns = _columns_for(ctx, rows)
    if not columns:
        print_json(data=rows)
        return

    table = Table(box=box.SIMPLE, show_header=True, show_lines=False)
    for col in columns:
        table.add_column(col)

    for row in rows:
        table.add_row(*[_format_cell(row.get(col)) for col in columns])

    console.print(table)


def _print_single(obj: dict[str, Any], *, ctx: Any) -> None:
    """Render a single dict as a vertical key/value table."""
    key = _command_key(ctx)
    fields = SINGLE_REGISTRY.get(key) if key else None

    if fields:
        table = Table(box=box.SIMPLE, show_header=True, show_lines=False)
        table.add_column("Field", style="bold", no_wrap=True, max_width=20)
        table.add_column("Value", overflow="fold")
        for label, accessor in fields:
            if callable(accessor):
                value = accessor(obj)
            else:
                value = _resolve(obj, accessor)
            table.add_row(label, _format_cell(value, max_width=0))
    else:
        table = Table(box=box.SIMPLE, show_header=True, show_lines=False)
        table.add_column("Field", style="bold", no_wrap=True, max_width=20)
        table.add_column("Value", overflow="fold")
        for k, v in obj.items():
            table.add_row(k, _format_cell(v, max_width=0))

    console.print(table)
