#!/usr/bin/env python3
"""clock — track work intervals using $ZDOTDIR/.zsh_history as the source of truth.

Usage:
  clock in     Record a punch-in (the command lands in history automatically).
               Reports the last completed work interval for context.
  clock out    Close the open interval started by the most recent "clock in".
               Reports its length.
  clock        Report all work intervals from the last 35 days, plus totals.
"""

import os
import re
import sys
from datetime import datetime, timedelta

HISTORY_FILE = os.path.expanduser(os.environ.get("HISTFILE", "~/.zsh_history"))
WINDOW_DAYS = 35

# Matches zsh extended-history lines: ": <epoch>:<duration>;command"
LINE_RE = re.compile(r"^: (\d+):\d+;(.*)$")


LOCAL_TZ = datetime.now().astimezone().tzinfo


def dt(ts):
    return datetime.fromtimestamp(ts, tz=LOCAL_TZ)


def parse_history(path):
    """Return [(timestamp, command)] parsed from the history file."""
    entries = []
    try:
        with open(path, encoding="utf-8", errors="replace") as fh:
            for raw in fh:
                m = LINE_RE.match(raw.rstrip("\n"))
                if m:
                    ts = int(m.group(1))
                    cmd = m.group(2).strip()
                    # Skip continuation-line artifacts that carry no timestamp.
                    entries.append((ts, cmd))
    except FileNotFoundError:
        print(f"error: history file not found: {path}", file=sys.stderr)
        sys.exit(1)
    return entries


def is_clock(cmd, verb=None):
    """True if this history entry is a `clock [verb]` invocation."""
    parts = cmd.split(None, 1)
    if not parts or parts[0] != "clock":
        return False
    rest = parts[1].strip() if len(parts) > 1 else ""
    if verb is None:
        return rest == ""
    return rest == verb


def find_punches(entries):
    """Pair clock punches from history into work intervals.

    Returns (intervals, open_start) where intervals is a list of
    (start_dt, end_dt) tuples oldest-first, and open_start is the
    start time of an unclosed interval, or None.
    """
    events = []
    for ts, c in entries:
        verb = None
        if is_clock(c, "in"):
            verb = "in"
        elif is_clock(c, "out"):
            verb = "out"
        if verb is not None:
            events.append((ts, verb))
    events.sort(key=lambda ev: ev[0])

    # Pair punches left-to-right: each "in" opens an interval, closed by the
    # next "out". A stray "out" with nothing open is ignored; a stray "in"
    # leaves the interval open.
    #
    # Note on the current invocation: zsh writes the running command's own
    # history entry at varying times depending on how the shell was started,
    # so we deliberately do NOT special-case it. For `clock in` the stale
    # state is harmless (it just prints "already clocked in" once). For
    # `clock out`, if the matching `clock in` hasn't been flushed yet, the
    # close fails cleanly — run `clock out` again a moment later.
    intervals = []
    open_start = None
    for ts, verb in events:
        if verb == "in":
            open_start = dt(ts)
        else:
            if open_start is not None:
                intervals.append((open_start, dt(ts)))
                open_start = None
    return intervals, open_start


def fmt_duration(seconds):
    seconds = int(round(seconds))
    h, rem = divmod(abs(seconds), 3600)
    m, s = divmod(rem, 60)
    sign = "-" if seconds < 0 else ""
    if h:
        return f"{sign}{h}h {m:02d}m {s:02d}s"
    if m:
        return f"{sign}{m}m {s:02d}s"
    return f"{sign}{s}s"


def fmt_stamp(dt):
    return dt.strftime("%Y-%m-%d %H:%M:%S")


def report_intervals(intervals, title):
    total = sum((e - s).total_seconds() for s, e in intervals)
    print(title)
    print("-" * 74)
    if not intervals:
        print("(no complete work intervals)")
    for idx, (s, e) in enumerate(intervals, 1):
        dur = (e - s).total_seconds()
        print(f"{idx:>3}. {fmt_stamp(s)}  ->  {fmt_stamp(e)}   [{fmt_duration(dur)}]")
    if intervals:
        print("-" * 74)
        print(f"total: {len(intervals)} interval(s), {fmt_duration(total)} worked")


def main():
    args = sys.argv[1:]
    if len(args) > 1 or args and args[0] not in ("in", "out"):
        print(__doc__.strip(), file=sys.stderr)
        sys.exit(2)

    now = datetime.now().astimezone()
    entries = parse_history(HISTORY_FILE)
    intervals, open_start = find_punches(entries)

    if not args:
        since = now - timedelta(days=WINDOW_DAYS)
        recent = [(s, e) for s, e in intervals if s >= since]
        report_intervals(recent, f"work intervals, last {WINDOW_DAYS} days")
        if open_start:
            elapsed = (now - open_start).total_seconds()
            print(f"\ncurrently CLOCKED IN since {fmt_stamp(open_start)} ({fmt_duration(elapsed)} so far)")
        elif recent:
            print("\nstatus: clocked out")
        return

    if args[0] == "in":
        if open_start:
            print(f"already clocked in since {fmt_stamp(open_start)} "
                  f"({fmt_duration((now - open_start).total_seconds())} so far)")
            print("use 'clock out' first if you meant to close that interval.")
            sys.exit(1)
        print(f"clocked in at {fmt_stamp(now)}")
        if intervals:
            ls, le = intervals[-1]
            print(f"last interval: {fmt_stamp(ls)} -> {fmt_stamp(le)} "
                  f"[{fmt_duration((le - ls).total_seconds())}]")
        return

    # clock out
    if not open_start:
        print("not clocked in — nothing to close.", file=sys.stderr)
        sys.exit(1)
    closed = (now - open_start).total_seconds()
    print(f"interval closed: {fmt_stamp(open_start)} -> {fmt_stamp(now)} "
          f"[{fmt_duration(closed)}]")


if __name__ == "__main__":
    main()
