#!/usr/bin/env python3
"""
Lab 1 - thermal · SETTLING CURVE PLOTTER  (runs on jedas)
school.worhl.net

Turns a gauge_logger.py CSV into the settling curve: what the shared bath
did, with every event you marked drawn where it happened.

    The regulating scalar never becomes a packet.

Safe to run against a log that is still being written - the CSV is
append-only, so plotting at minute 5 to check the shape costs nothing and
tells you early whether the run is worth staying for.

Usage:
    python3 plot_settling.py gauge_20260719_141500.csv
    python3 plot_settling.py run.csv --out settling_curve.png
    python3 plot_settling.py run.csv --setpoint 30.0 --title "run 2, lid closed"

Requires: matplotlib   (pip install --user matplotlib)
"""

import argparse
import csv
import os
import sys

try:
    import matplotlib
    matplotlib.use("Agg")
    import matplotlib.pyplot as plt
except ImportError:
    sys.exit("matplotlib is not installed.  pip install --user matplotlib")


# Site palette, so the bench artefacts match the simulation figures.
COL_TRACE = "#1D9E75"
COL_SETPOINT = "#BA7517"
COL_EVENT = "#185FA5"
COL_TEXT = "#0F6E56"


def load(path):
    """Return (minutes, celsius, [(minute, label), ...])."""
    minutes, celsius, events = [], [], []
    with open(path, newline="") as fh:
        for row in csv.DictReader(fh):
            try:
                elapsed = float(row["elapsed_s"]) / 60.0
            except (TypeError, ValueError):
                continue
            note = (row.get("event") or "").strip()
            temp = (row.get("celsius") or "").strip()
            if temp:
                try:
                    celsius.append(float(temp))
                    minutes.append(elapsed)
                except ValueError:
                    pass
            elif note:
                events.append((elapsed, note))
    return minutes, celsius, events


def plot(minutes, celsius, events, setpoint, title, out_path):
    fig, ax = plt.subplots(figsize=(9, 4.8))

    ax.plot(minutes, celsius, color=COL_TRACE, lw=1.8, zorder=3)

    if setpoint is not None:
        ax.axhline(setpoint, color=COL_SETPOINT, ls="--", lw=1.3, zorder=2)
        ax.text(minutes[0] if minutes else 0, setpoint, " setpoint %.1f C" % setpoint,
                va="bottom", ha="left", fontsize=9, color=COL_SETPOINT)

    span = (max(celsius) - min(celsius)) if celsius else 1.0
    label_y = (max(celsius) + span * 0.04) if celsius else 1.0

    skip = ("log started", "log stopped")
    for minute, note in events:
        if note.lower() in skip:
            continue
        ax.axvline(minute, color=COL_EVENT, ls=":", lw=1.2, alpha=0.8, zorder=1)
        ax.text(minute, label_y, " " + note, rotation=90, fontsize=8,
                va="bottom", ha="center", color=COL_EVENT)

    ax.set_xlabel("minutes since logging started")
    ax.set_ylabel("shared bath temperature (C)")
    ax.set_title(title)
    ax.grid(alpha=0.3)

    if celsius:
        ax.text(0.99, 0.02,
                "settled near %.2f C  ·  %d samples" % (celsius[-1], len(celsius)),
                transform=ax.transAxes, ha="right", va="bottom",
                fontsize=9, color=COL_TEXT)

    fig.tight_layout()
    fig.savefig(out_path, dpi=150)
    print("wrote %s  (%d samples, %d marked events)"
          % (out_path, len(celsius), len(events)))


def main():
    ap = argparse.ArgumentParser(description="Plot a lab 1 gauge log.")
    ap.add_argument("csv", help="CSV written by gauge_logger.py")
    ap.add_argument("--out", default=None, help="output PNG path")
    ap.add_argument("--setpoint", type=float, default=30.0,
                    help="draw the setpoint line here (use --setpoint nan to omit)")
    ap.add_argument("--title", default=None, help="plot title")
    args = ap.parse_args()

    if not os.path.exists(args.csv):
        sys.exit("no such file: %s" % args.csv)

    minutes, celsius, events = load(args.csv)

    if not celsius:
        sys.exit("no readings in %s yet - is the gauge running?" % args.csv)

    setpoint = args.setpoint
    if setpoint is not None and setpoint != setpoint:   # nan
        setpoint = None

    out_path = args.out or (os.path.splitext(args.csv)[0] + ".png")
    title = args.title or "lab 1 - the shared bath settling"

    plot(minutes, celsius, events, setpoint, title, out_path)


if __name__ == "__main__":
    main()
