from __future__ import annotations

import json
from datetime import datetime
from typing import Any

from rich.console import Group
from rich.pretty import Pretty
from rich.table import Table
from rich.text import Text


COMPACT_ROW_PRIORITY = (
    "id",
    "name",
    "symbol",
    "asset",
    "underlying",
    "family",
    "status",
    "latest_quality",
    "quality_flag",
    "latest_updated_at",
    "latest_ts",
    "latest_logical_ts",
    "mid_price",
    "close",
    "rows",
    "source",
    "venue",
    "venue_scope",
)
COMPACT_SKIP_KEYS = {
    "description",
    "latest_values",
    "context",
    "schema_columns",
    "primary_key",
    "partition_columns",
    "sample_partitions",
    "available_sources",
    "supported_underlyings",
    "supported_assets",
    "supported_venue_scopes",
    "supported_order_types",
}


def format_value(value: Any, *, compact: bool = False) -> str:
    if value is None:
        return "—"
    if isinstance(value, bool):
        return "yes" if value else "no"
    if isinstance(value, float):
        if abs(value) >= 1_000:
            return f"{value:,.2f}"
        return f"{value:.4f}".rstrip("0").rstrip(".")
    if isinstance(value, datetime):
        return value.strftime("%Y-%m-%d %H:%MZ") if compact else value.isoformat()
    if isinstance(value, list):
        if compact:
            preview = ", ".join(format_value(item, compact=True) for item in value[:2])
            return f"{preview}, …" if len(value) > 2 else preview
        return ", ".join(format_value(item) for item in value)
    if isinstance(value, dict):
        rendered = json.dumps(value, sort_keys=True, default=str, separators=(",", ":"))
        return rendered[:39] + "..." if compact and len(rendered) > 42 else rendered
    text = str(value)
    if compact and len(text) > 42:
        return text[:39] + "..."
    return text


def make_kv_table(title: str, values: dict[str, Any], *, compact: bool = False) -> Table:
    table = Table(title=title, show_header=False, box=None, pad_edge=False)
    table.add_column("key", style="cyan", no_wrap=True)
    table.add_column("value")
    items = values.items()
    if compact:
        items = compact_dict(values, max_items=6).items()
    for key, value in items:
        table.add_row(key, format_value(value, compact=compact))
    return table


def compact_dict(
    values: dict[str, Any],
    *,
    preferred: list[str] | None = None,
    max_items: int = 6,
) -> dict[str, Any]:
    if not values:
        return {}
    keys: list[str] = []
    if preferred:
        keys.extend(key for key in preferred if key in values)
    for key, value in values.items():
        if key in keys:
            continue
        if key in COMPACT_SKIP_KEYS:
            continue
        if value in (None, "", [], {}, "—"):
            continue
        if isinstance(value, (dict, list)):
            continue
        keys.append(key)
        if len(keys) >= max_items:
            break
    if not keys:
        keys = list(values)[:max_items]
    return {key: values[key] for key in keys}


def compact_columns(
    rows: list[dict[str, Any]],
    *,
    preferred: list[str] | None = None,
    max_columns: int = 6,
) -> list[str]:
    if not rows:
        return preferred or []
    keys: list[str] = []
    first_row = rows[0]
    if preferred:
        keys.extend(key for key in preferred if key in first_row)
    for key, value in first_row.items():
        if key in keys:
            continue
        if key in COMPACT_SKIP_KEYS:
            continue
        if value in (None, "", [], {}, "—"):
            continue
        if isinstance(value, (dict, list)):
            continue
        keys.append(key)
        if len(keys) >= max_columns:
            break
    if not keys:
        keys = list(first_row)[:max_columns]
    ordered = [key for key in COMPACT_ROW_PRIORITY if key in keys]
    ordered.extend(key for key in keys if key not in ordered)
    return ordered[:max_columns]


def make_rows_table(
    title: str,
    rows: list[dict[str, Any]],
    columns: list[str] | None = None,
    limit: int | None = None,
    *,
    compact: bool = False,
) -> Table:
    table = Table(
        title=title,
        expand=not compact,
        box=None,
        pad_edge=not compact,
        show_edge=not compact,
    )
    if not rows:
        table.add_column("status")
        table.add_row("No rows")
        return table
    ordered_columns = columns or (
        compact_columns(rows) if compact else list(rows[0].keys())
    )
    for column in ordered_columns:
        table.add_column(
            column,
            no_wrap=compact,
            max_width=18 if compact else None,
            overflow="ellipsis",
        )
    for row in rows[:limit]:
        table.add_row(*(format_value(row.get(column), compact=compact) for column in ordered_columns))
    return table


def make_warning_text(warnings: list[str]) -> Text | None:
    if not warnings:
        return None
    text = Text()
    for warning in warnings:
        text.append(f"warning: {warning}\n", style="yellow")
    return text


def group_parts(*parts: Any) -> Group:
    filtered = [part for part in parts if part is not None]
    return Group(*filtered)


def pretty_block(title: str, value: Any) -> Table:
    table = Table(title=title, box=None, pad_edge=False)
    table.add_column("data")
    table.add_row(Pretty(value, expand_all=True))
    return table
