"""Human-readable terminal report and JSON export."""

from __future__ import annotations

import json
from typing import TextIO
import sys

from dataset_inspector.analyzer import AnalysisResult

# ---------------------------------------------------------------------------
# Terminal colours (disabled when stdout is not a TTY)
# ---------------------------------------------------------------------------
_USE_COLOR = hasattr(sys.stdout, "isatty") and sys.stdout.isatty()

BOLD = "\033[1m" if _USE_COLOR else ""
GREEN = "\033[92m" if _USE_COLOR else ""
YELLOW = "\033[93m" if _USE_COLOR else ""
RED = "\033[91m" if _USE_COLOR else ""
RESET = "\033[0m" if _USE_COLOR else ""

SEPARATOR = "─" * 60


def _header(title: str) -> str:
    return f"\n{BOLD}{title}{RESET}\n{SEPARATOR}"


# ---------------------------------------------------------------------------
# Terminal report
# ---------------------------------------------------------------------------

def print_report(result: AnalysisResult, file: TextIO | None = None) -> None:
    """Print a formatted analysis report to *file* (default stdout)."""
    out = file or sys.stdout

    def p(*args: object, **kw: object) -> None:  # noqa: ANN401
        print(*args, file=out, **kw)  # type: ignore[arg-type]

    p(f"\n{BOLD}{'═' * 60}{RESET}")
    p(f"{BOLD}  Dataset Inspector – Analysis Report{RESET}")
    p(f"{BOLD}{'═' * 60}{RESET}")

    # --- Shape & dtypes ---
    p(_header("Dataset Overview"))
    p(f"  Rows   : {result.shape[0]}")
    p(f"  Columns: {result.shape[1]}")
    p(f"  Column types:")
    for col, dtype in result.dtypes.items():
        p(f"    • {col}: {dtype}")

    # --- Basic stats ---
    if result.basic_stats:
        p(_header("Basic Statistics (numeric columns)"))
        for s in result.basic_stats:
            p(f"  {BOLD}{s.name}{RESET}")
            p(f"    mean={s.mean:.4f}  std={s.std:.4f}  "
              f"min={s.min:.4f}  max={s.max:.4f}  median={s.median:.4f}")

    # --- Missing values ---
    p(_header("Missing Values"))
    if result.missing:
        for m in result.missing:
            colour = RED if m.percentage > 20 else YELLOW
            p(f"  {colour}{m.column}: {m.count} ({m.percentage}%){RESET}")
    else:
        p(f"  {GREEN}No missing values detected.{RESET}")

    # --- Duplicates ---
    p(_header("Duplicate Rows"))
    if result.duplicate_row_count:
        p(f"  {YELLOW}{result.duplicate_row_count} duplicate row(s) found:{RESET}")
        for orig, dup in result.duplicate_pairs:
            p(f"    row {dup} is a duplicate of row {orig}")
    else:
        p(f"  {GREEN}No duplicate rows.{RESET}")

    # --- Class imbalance ---
    p(_header("Class Imbalance"))
    if result.class_imbalance:
        for col, info in result.class_imbalance.items():
            p(f"  {YELLOW}Column '{col}' is imbalanced "
              f"(minority/majority ratio = {info['minority_majority_ratio']}):{RESET}")
            for val, cnt in info["value_counts"].items():
                p(f"    {val}: {cnt}")
    else:
        p(f"  {GREEN}No class imbalance detected.{RESET}")

    # --- Anomalies ---
    p(_header("Anomalies"))
    if result.anomalies:
        for a in result.anomalies:
            n = len(a.row_indices)
            preview = a.row_indices[:10]
            p(f"  {YELLOW}{a.column}: {n} outlier(s) [{a.method}] "
              f"– rows {preview}{'…' if n > 10 else ''}{RESET}")
    else:
        p(f"  {GREEN}No anomalies detected.{RESET}")

    p(f"\n{BOLD}{'═' * 60}{RESET}\n")


# ---------------------------------------------------------------------------
# JSON export
# ---------------------------------------------------------------------------

def export_json(result: AnalysisResult, path: str) -> None:
    """Write the analysis result as JSON to *path*."""
    with open(path, "w", encoding="utf-8") as f:
        json.dump(result.to_dict(), f, indent=2, default=str)
