"""Plot per-GPU utilization timeline for elastic vs baseline."""
import csv, json, sys, os

def load_gpu(path):
    """Load GPU util CSV, return {gpu_id: [(timestamp, util%)]]}."""
    by_gpu = {}
    with open(path) as f:
        for r in csv.DictReader(f):
            g = int(r["gpu"])
            t = float(r["timestamp"])
            u = float(r["util_pct"])
            by_gpu.setdefault(g, []).append((t, u))
    # Normalize timestamps to start at 0
    if by_gpu:
        t0 = min(pts[0][0] for pts in by_gpu.values())
        for g in by_gpu:
            by_gpu[g] = [(t - t0, u) for t, u in by_gpu[g]]
    return by_gpu

def print_timeline(by_gpu, label, max_time=None):
    """Print ASCII timeline of GPU utilization."""
    print(f"\n{'='*70}")
    print(f"  {label}")
    print(f"{'='*70}")

    if not by_gpu:
        print("  No data")
        return

    # Bucket into 10s windows
    window = 10.0
    if max_time is None:
        max_time = max(t for pts in by_gpu.values() for t, _ in pts)
    n_windows = min(int(max_time / window) + 1, 40)  # cap at 40 columns

    for gpu in sorted(by_gpu.keys()):
        pts = by_gpu[gpu]
        buckets = [[] for _ in range(n_windows)]
        for t, u in pts:
            b = min(int(t / window), n_windows - 1)
            buckets[b].append(u)

        avgs = [sum(b)/len(b) if b else 0 for b in buckets]
        # ASCII bar: . = 0-10%, o = 10-30%, O = 30-60%, # = 60-100%
        bar = ""
        for a in avgs:
            if a < 1: bar += " "
            elif a < 10: bar += "."
            elif a < 30: bar += "o"
            elif a < 60: bar += "O"
            else: bar += "#"

        mean = sum(a for a in avgs) / len(avgs) if avgs else 0
        print(f"  GPU{gpu}: |{bar}| mean={mean:.0f}%")

    print(f"  Time:  {'0':>1}{'':>{n_windows-6}}{int(max_time)}s")
    print(f"  Legend: ' '=0% .=1-10% o=10-30% O=30-60% #=60-100%")

    # Per-GPU stats
    print(f"\n  Per-GPU mean utilization:")
    for gpu in sorted(by_gpu.keys()):
        pts = by_gpu[gpu]
        vals = [u for _, u in pts]
        mean = sum(vals) / len(vals)
        nz = sum(1 for v in vals if v > 0)
        print(f"    GPU{gpu}: mean={mean:.1f}%  active={nz*100//len(vals)}%  samples={len(vals)}")

# Load and compare
configs = [
    ("outputs/baseline_dash1/gpu_util.csv", "Baseline (8 combined, dash1)"),
    ("outputs/elastic_v4/gpu_util.csv", "Elastic P2P v4 (dash0)"),
]

for path, label in configs:
    if os.path.exists(path):
        by_gpu = load_gpu(path)
        print_timeline(by_gpu, label)
    else:
        print(f"\n  {label}: {path} NOT FOUND")

# Imbalance metric
print(f"\n{'='*70}")
print(f"  LOAD IMBALANCE ANALYSIS")
print(f"{'='*70}")

for path, label in configs:
    if not os.path.exists(path):
        continue
    by_gpu = load_gpu(path)
    means = []
    for gpu in sorted(by_gpu.keys()):
        vals = [u for _, u in by_gpu[gpu]]
        means.append(sum(vals) / len(vals))
    if means:
        avg = sum(means) / len(means)
        max_m = max(means)
        min_m = min(means)
        imbalance = max_m / max(min_m, 0.1)
        print(f"  {label}:")
        print(f"    Per-GPU means: {['%.1f' % m for m in means]}")
        print(f"    Avg={avg:.1f}%  Min={min_m:.1f}%  Max={max_m:.1f}%  Imbalance={imbalance:.1f}x")
