"""
display.py — pretty-print a CziMetadata object to the terminal.
"""

from __future__ import annotations

import statistics
from typing import List, Optional

from .reader import CziMetadata

BOLD  = "\033[1m"
CYAN  = "\033[96m"
GREEN = "\033[92m"
YELL  = "\033[93m"
RESET = "\033[0m"
WIDTH = 60


def _header(text: str) -> None:
    print(f"\n{CYAN}{'─'*WIDTH}{RESET}")
    print(f"{BOLD}{CYAN}  {text}{RESET}")
    print(f"{CYAN}{'─'*WIDTH}{RESET}")


def _ok(label: str, value) -> None:
    print(f"  {GREEN}{label:<28}{RESET} {value}")


def _warn(msg: str) -> None:
    print(f"  {YELL}⚠  {msg}{RESET}")


def _fmt_time(seconds: float) -> str:
    if seconds < 1:
        return f"{seconds*1e3:.3f} ms"
    if seconds < 60:
        return f"{seconds:.3f} s"
    m, s = divmod(seconds, 60)
    if m < 60:
        return f"{int(m)} min {s:.2f} s  ({seconds:.3f} s)"
    h, m2 = divmod(m, 60)
    return f"{int(h)} h {int(m2)} min {s:.2f} s  ({seconds:.3f} s)"


def print_metadata(meta: CziMetadata,
                   verbose: bool = False,
                   timestamps: Optional[List[float]] = None) -> None:
    """Pretty-print a CziMetadata object to stdout."""

    # ── File ──────────────────────────────────────────────────────────────────
    _header("czipeak  —  CZI Metadata Inspector")
    _ok("File",         meta.path.split("/")[-1])
    _ok("Size on disk", f"{meta.size_mb:.2f} MB")
    if meta.axes:
        _ok("Axes", f"{meta.axes}  shape={meta.shape}")

    # ── Acquisition ───────────────────────────────────────────────────────────
    _header("Acquisition Info")
    if meta.acq_date_raw:
        _ok("Acquisition start", meta.acq_date_raw)
    else:
        _warn("Acquisition date/time not found in metadata")
    if meta.objective:
        _ok("Objective", meta.objective)
    if meta.magnification:
        _ok("Magnification", f"{meta.magnification}×")
    if meta.dx_um is not None and meta.dy_um is not None:
        _ok("Pixel size (XY)", f"{meta.dx_um:.4f} × {meta.dy_um:.4f} µm")

    # ── Z-stack ───────────────────────────────────────────────────────────────
    if meta.z_count is not None:
        _header("Z-Stack Info")
        _ok("Z slices",        meta.z_count)
        if meta.dz_um is not None:
            _ok("dz  (step size)", f"{meta.dz_um:.4f} µm")
        if meta.z_start_um is not None:
            _ok("Z start",         f"{meta.z_start_um:.4f} µm")
        if meta.z_end_um is not None:
            _ok("Z end",           f"{meta.z_end_um:.4f} µm")
        if meta.z_range_um is not None:
            _ok("Z range (total)", f"{meta.z_range_um:.4f} µm")

    # ── Temporal ──────────────────────────────────────────────────────────────
    _header("Temporal Analysis")

    if timestamps:
        # Per-frame timestamp list (legacy CZI schema)
        n = len(timestamps)
        _ok("Time points (T)", n)
        _ok("t₀  (= 0 s)",     f"{timestamps[0]:.4f} s")
        _ok("t_end",           f"{timestamps[-1]:.4f} s")
        _ok("Total acquisition time", _fmt_time(timestamps[-1] - timestamps[0]))

        if n > 1:
            dts     = [timestamps[i+1] - timestamps[i] for i in range(n - 1)]
            dt_mean = statistics.mean(dts)
            dt_std  = statistics.stdev(dts) if n > 2 else 0.0
            _ok("Δt  (mean)", f"{dt_mean:.4f} s  ({dt_mean/60:.4f} min)")
            _ok("Δt  (min)",  f"{min(dts):.4f} s")
            _ok("Δt  (max)",  f"{max(dts):.4f} s")
            _ok("Δt  (std)",  f"{dt_std:.4f} s")
            _ok("Frame rate", f"{1/dt_mean:.5f} fps")
            if dt_std / dt_mean > 0.05:
                _warn("Δt is NOT constant (std/mean > 5 %) — check for dropped frames")
            else:
                _ok("Regularity", "✓ Regular (std/mean ≤ 5 %)")

        if verbose:
            _header("All Timestamps")
            print(f"  {'Frame':>6}   {'t (s)':>14}   {'Δt (s)':>12}")
            print(f"  {'─'*6}   {'─'*14}   {'─'*12}")
            for i, t in enumerate(timestamps):
                dt_str = f"{timestamps[i]-timestamps[i-1]:12.4f}" if i > 0 else "           —"
                print(f"  {i:>6}   {t:>14.6f}   {dt_str}")

    else:
        # Metadata-derived timing (modern ZEN schema)
        if meta.t_count is not None:
            _ok("Time points (T)", meta.t_count)
        else:
            _warn("Frame count not found — ensure czifile is installed")

        if meta.s_count and meta.s_count > 1:
            _ok("Tiles (scenes)", meta.s_count)
        for sc in meta.scenes:
            _ok(f"  Scene {sc['index']} ({sc['name']})",
                f"center {sc['center']} µm" if sc["center"] else "")

        if meta.t_start:
            _ok("Acquisition start", meta.t_start.strftime("%Y-%m-%d %H:%M:%S UTC"))
        if meta.t_stop:
            _ok("Last frame",        meta.t_stop.strftime("%Y-%m-%d %H:%M:%S UTC"))
        if meta.total_duration_s is not None:
            _ok("Total acquisition time", _fmt_time(meta.total_duration_s))
        if meta.dt_actual_s is not None:
            _ok("Δt  (actual mean)",
                f"{meta.dt_actual_s:.2f} s  =  {meta.dt_actual_s/60:.3f} min")
            _ok("Frame rate", f"{1/meta.dt_actual_s:.5f} fps")
        if meta.interval_configured_s is not None:
            _ok("Session interval (ZEN)",
                f"{meta.interval_configured_raw}  =  "
                f"{_fmt_time(meta.interval_configured_s)}")

        if meta.t_count is None and meta.t_stop is None:
            _warn("No temporal information found in metadata.")

    # ── Channels ──────────────────────────────────────────────────────────────
    if meta.channels:
        _header("Channel Info")
        for ch in meta.channels:
            parts = [ch["name"]]
            if ch["fluor"]: parts.append(f"fluor={ch['fluor']}")
            if ch["ex_nm"]: parts.append(f"ex={ch['ex_nm']} nm")
            if ch["em_nm"]: parts.append(f"em={ch['em_nm']} nm")
            _ok(f"  Channel {ch['index']}", " | ".join(parts))

    print(f"\n{CYAN}{'─'*WIDTH}{RESET}\n")
