#!/usr/bin/env bash
# Marker-based log slicing for the dev session under temp/dev-logs/latest.
#
# Replaces the two-step bookkeeping ritual from V2 plan §2:
#   PRE=$(wc -l < temp/dev-logs/latest/net.log | tr -d ' ')
#   # … do thing …
#   awk -v n=$PRE 'NR > n' temp/dev-logs/latest/net.log | grep -c 'tapestry/page'
#
# A single `since.sh mark` snapshots the line counts of every active stream
# under `temp/dev-logs/latest/.markers/<stream>`. Subsequent `count` /
# `show` reads slice from the saved offset.
#
# Usage:
#   since.sh mark                        snapshot line counts for every stream
#   since.sh mark net                    snapshot just net.log (others untouched)
#   since.sh markers                     list saved markers
#   since.sh count <stream> <pattern>    grep count since mark
#                                          --unique-by=<regex>   count -oE | sort -u
#                                          --raw                 emit just the number
#   since.sh show <stream> <pattern>     pipe matched lines to stdout
#   since.sh slice <stream>              all lines since mark (no pattern)
#   since.sh clear                       wipe all markers in the latest session
#
# Streams are the bare log names: net, metro, wc, auth, push, watch-app,
# watch-img, ios-app, ios-app-system, js-errors, watch-app-system. Any
# `.log` file under temp/dev-logs/latest/ is valid.

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
cd "$REPO_ROOT"

SESSION="temp/dev-logs/latest"
if [[ ! -e "$SESSION" ]]; then
  echo "since: no session at $SESSION — run scripts/dev/tail-logs.sh" >&2
  exit 1
fi
SESSION_REAL="$(cd "$SESSION" && pwd -P)"
MARKERS_DIR="$SESSION_REAL/.markers"
mkdir -p "$MARKERS_DIR"

# Marker invalidation: each session dir gets a stable serial that follows
# the dir name (set by tail-logs.sh from the boot timestamp). When the
# 'latest' symlink rolls forward to a new session, the old markers are
# left behind in the old dir — but if we're called against latest and find
# a stale `.session` sentinel, wipe and start fresh so the operator isn't
# slicing yesterday's offsets out of today's logs.
SESSION_SENTINEL_FILE="$MARKERS_DIR/.session"
CUR_SESSION_NAME="$(basename "$SESSION_REAL")"
if [[ -f "$SESSION_SENTINEL_FILE" ]]; then
  SAVED=$(cat "$SESSION_SENTINEL_FILE")
  if [[ "$SAVED" != "$CUR_SESSION_NAME" ]]; then
    rm -f "$MARKERS_DIR"/*
    echo "$CUR_SESSION_NAME" > "$SESSION_SENTINEL_FILE"
  fi
else
  echo "$CUR_SESSION_NAME" > "$SESSION_SENTINEL_FILE"
fi

usage() {
  sed -n '2,29p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
}

# Map a short stream name → full log file path. Accept either the short
# name (`net`) or the full `net.log` form for convenience.
stream_path() {
  local name="$1"
  case "$name" in
    *.log) echo "$SESSION_REAL/$name" ;;
    *)     echo "$SESSION_REAL/$name.log" ;;
  esac
}

# Line count, tolerant of missing files and `wc`'s leading whitespace.
lc() {
  local f="$1"
  if [[ ! -f "$f" ]]; then
    echo 0
  else
    wc -l < "$f" | tr -d ' '
  fi
}

cmd_mark() {
  # `mark` with no args: write a marker for every existing .log under the
  # session. `mark <stream>`: only that one (lets you reset a single
  # offset without disturbing the others).
  if [[ $# -eq 0 ]]; then
    local saved=()
    for f in "$SESSION_REAL"/*.log; do
      [[ -f "$f" ]] || continue
      local stream
      stream="$(basename "$f" .log)"
      local count
      count=$(lc "$f")
      echo "$count" > "$MARKERS_DIR/$stream"
      saved+=("$stream=$count")
    done
    echo "saved markers: ${saved[*]}"
  else
    for name in "$@"; do
      local f
      f="$(stream_path "$name")"
      if [[ ! -f "$f" ]]; then
        echo "since: stream not found: $f" >&2
        return 1
      fi
      local stream
      stream="$(basename "$f" .log)"
      local count
      count=$(lc "$f")
      echo "$count" > "$MARKERS_DIR/$stream"
      echo "saved marker: $stream=$count"
    done
  fi
}

cmd_markers() {
  shopt -s nullglob
  local found=0
  for m in "$MARKERS_DIR"/*; do
    [[ "$(basename "$m")" == .session ]] && continue
    [[ -f "$m" ]] || continue
    local stream value cur
    stream="$(basename "$m")"
    value="$(cat "$m")"
    cur=$(lc "$SESSION_REAL/$stream.log")
    printf "  %-22s mark=%s cur=%s (Δ=%s)\n" "$stream" "$value" "$cur" "$((cur - value))"
    found=1
  done
  if [[ "$found" -eq 0 ]]; then
    echo "  (no markers yet — run: since.sh mark)"
  fi
}

# Slice helper: stream all lines after the saved marker. If no marker
# exists, slice from the start (i.e. behave like cat).
slice_from_marker() {
  local stream="$1"
  local f
  f="$(stream_path "$stream")"
  if [[ ! -f "$f" ]]; then
    echo "since: stream not found: $f" >&2
    return 1
  fi
  local marker_file="$MARKERS_DIR/$(basename "$f" .log)"
  local n=0
  if [[ -f "$marker_file" ]]; then
    n=$(cat "$marker_file")
  fi
  awk -v n="$n" 'NR > n' "$f"
}

cmd_count() {
  local raw=0 unique_by=""
  local stream="" pattern=""
  for arg in "$@"; do
    case "$arg" in
      --raw)         raw=1 ;;
      --unique-by=*) unique_by="${arg#--unique-by=}" ;;
      *)
        if [[ -z "$stream" ]]; then
          stream="$arg"
        elif [[ -z "$pattern" ]]; then
          pattern="$arg"
        fi
        ;;
    esac
  done
  if [[ -z "$stream" || -z "$pattern" ]]; then
    echo "since: count needs <stream> <pattern>" >&2
    return 2
  fi
  local f
  f="$(stream_path "$stream")"
  local marker_file="$MARKERS_DIR/$(basename "$f" .log)"
  local mark=0
  [[ -f "$marker_file" ]] && mark=$(cat "$marker_file")
  local cur
  cur=$(lc "$f")

  # Total: count of pattern-matching lines in the slice.
  local total
  total=$(slice_from_marker "$stream" | grep -c "$pattern" || true)

  if [[ -n "$unique_by" ]]; then
    local uniq
    uniq=$(slice_from_marker "$stream" | grep "$pattern" | grep -oE "$unique_by" | sort -u | wc -l | tr -d ' ')
    if [[ "$raw" -eq 1 ]]; then
      echo "$uniq $total"
    else
      echo "$uniq unique / $total total (from line $((mark + 1)); cur $cur)"
    fi
  else
    if [[ "$raw" -eq 1 ]]; then
      echo "$total"
    else
      echo "$total (from line $((mark + 1)); cur $cur)"
    fi
  fi
}

cmd_show() {
  local stream="$1"
  local pattern="${2:-}"
  if [[ -z "$pattern" ]]; then
    slice_from_marker "$stream"
  else
    slice_from_marker "$stream" | grep "$pattern" || true
  fi
}

cmd_slice() {
  slice_from_marker "$1"
}

cmd_clear() {
  rm -f "$MARKERS_DIR"/*
  echo "$CUR_SESSION_NAME" > "$SESSION_SENTINEL_FILE"
  echo "cleared markers."
}

if [[ $# -eq 0 ]]; then
  usage
  exit 0
fi

sub="$1"; shift
case "$sub" in
  mark)     cmd_mark "$@" ;;
  markers)  cmd_markers ;;
  count)    cmd_count "$@" ;;
  show)     cmd_show "$@" ;;
  slice)    cmd_slice "$@" ;;
  clear)    cmd_clear ;;
  -h|--help) usage ;;
  *)
    echo "since: unknown subcommand: $sub" >&2
    usage
    exit 2
    ;;
esac
