"""Terminal formatting helpers for Vibelexity output."""

from __future__ import annotations

import os
import re
import sys
from typing import Callable

# ── Color support ─────────────────────────────────────────────────────
# Auto-detect: color only when stdout is an interactive terminal.
# Respect the NO_COLOR convention (https://no-color.org/).

_USE_COLOR: bool = sys.stdout.isatty() and not bool(os.environ.get("NO_COLOR"))

_RESET = "\033[0m"
_BOLD = "\033[1m"
_DIM = "\033[2m"

_CYAN = "\033[36m"
_GREEN = "\033[32m"
_YELLOW = "\033[33m"
_RED = "\033[31m"
_MAGENTA = "\033[35m"


# ── Color wrappers ────────────────────────────────────────────────────


def _colorize(text: str, *codes: str) -> str:
    """Wrap *text* in ANSI escape codes (no-op when color is disabled)."""
    if not _USE_COLOR:
        return text
    return "".join(codes) + text + _RESET


def header(text: str) -> str:
    return _colorize(text, _BOLD, _CYAN)


def label(text: str) -> str:
    return _colorize(text, _YELLOW)


def num(value: str) -> str:
    return _colorize(value, _BOLD, _GREEN)


def path(text: str) -> str:
    return _colorize(text, _DIM, _MAGENTA)


def func_name(text: str) -> str:
    return _colorize(text, _BOLD)


def warn(text: str) -> str:
    return _colorize(text, _BOLD, _RED)


def dim(text: str) -> str:
    """Format dimmed/muted text."""
    return _colorize(text, _DIM)


# ── Number formatting ─────────────────────────────────────────────────


def fmt_int(value: int | float) -> str:
    """Format an integer with thousand separators.

    Accepts float for convenience (truncates to int).
    """
    return f"{int(value):,}"


def fmt_float(value: float, decimals: int = 2) -> str:
    """Format a float rounded to *decimals* places, with thousand separators."""
    return f"{value:,.{decimals}f}"


def fmt_number(value: int | float) -> str:
    """Auto-detect int vs float and format accordingly."""
    if isinstance(value, int):
        return fmt_int(value)
    if value == int(value):
        return fmt_int(value)
    return fmt_float(value)


# ── Composite helpers ─────────────────────────────────────────────────


def colored_num(value: int | float) -> str:
    """Format and colorize a number."""
    return num(fmt_number(value))


def colored_float(value: float, decimals: int = 2) -> str:
    """Format and colorize a float."""
    return num(fmt_float(value, decimals))


def colored_int(value: int | float) -> str:
    """Format and colorize an integer."""
    return num(fmt_int(value))


def rpad(
    value: int | float,
    width: int,
    num_formatter: Callable[[int | float], str] | None = None,
) -> str:
    """Right-align a formatted number within *width* visible characters, then colorize.

    Args:
        value: Number to format
        width: Minimum column width
        num_formatter: Optional custom formatter (e.g., fmt_compact, colored_compact)
    """
    formatter = num_formatter or fmt_number
    formatted = formatter(value)

    # Check if the formatter returns a string with ANSI escape codes
    has_ansi = bool(re.search(r"\x1b\[", formatted))

    if has_ansi:
        # Formatter already includes color codes - need to handle padding carefully
        if not _USE_COLOR:
            # Color disabled, strip ANSI codes and pad
            text_only = re.sub(r"\x1b\[[0-9;]*m", "", formatted)
            visible_len = len(text_only)
            return text_only.rjust(width)
        else:
            # Color enabled, extract text for padding, preserve codes
            text_only = re.sub(r"\x1b\[[0-9;]*m", "", formatted)
            visible_len = len(text_only)
            needed_spaces = max(0, width - visible_len)
            return " " * needed_spaces + formatted

    # Formatter returns plain text (no color yet)
    if not _USE_COLOR:
        return formatted.rjust(width)

    # Color enabled, pad then add color
    plain = formatted
    visible_len = len(plain)
    needed_spaces = max(0, width - visible_len)
    return " " * needed_spaces + num(plain)


def fmt_compact(value: int | float) -> str:
    """Format large numbers (5+ digits) with superscript notation.

    Returns "∞" for values >= 10^6.
    Uses superscript notation for values >= 100,000 and < 10^6.
    Examples:
        - 5,000,000+ → "∞"
        - 123,456 → "123.5 × 10³"
        - 99,999 → "99,999" (no compaction)
    """
    if value >= 1_000_000:
        return "∞"
    if value < 100_000:
        return fmt_number(value)

    is_int_input = isinstance(value, int)
    scaled = value / 1_000
    if is_int_input:
        rounded = round(scaled, 1)
        if abs(rounded - int(rounded)) < 0.01:
            return f"{int(rounded)} × 10³"
    return f"{scaled:.1f} × 10³"


def colored_compact(value: int | float) -> str:
    """Format a number with compact notation and color."""
    return num(fmt_compact(value))
