#!/usr/bin/env bash
#
# Fixture listen-only — Pi on POG422 bus (no Ethernet bridge / WireGuard).
# Default: pog422_uartd owns UART (same C capture / HW_DEBUG path as bridge).
# Inject is always off (witness only).
# Options from ~/EtherGold/fixture-bridge.env when FIXTURE_POS is unset
# (run_listen_jun11_good.sh / hat_listen.sh). Capture default: ./data ./logs
# under this tree (~/EtherGold/dev) so rclone SYNC_SOURCE=dev lands in logs/N.
#
# Env:
#   FIXTURE_POS=1..99   (required; 51 = pos5 POG422 witness)
#   PI_LABEL=topbox1    (optional, default topbox${FIXTURE_POS})
#   FIXTURE_DIR=        optional override for data/logs base
#   CHANNEL=machine     default; ois → raw HS window log (no SOH/EOP parse)
#   FIXTURE_PROD=1      errors.jsonl only; no packets.raw (field collector)
#   VERBOSE=1           mirror events to stderr + fixture_listen.log
#   IGNORE_HS=1         continuous UART capture (forces pyserial; no UARTD)
#   NO_PROGRESSIVE_CSV=1  disable Master 0x0A → progressives.csv
#   ETHERGOLD_PROGRESSIVES_CSV=  override CSV path (default ~/EtherGold/progressives.csv)
#   CLEAR_CAPTURE=1     default: wipe data/ and logs/ contents on start (prod uses 0)
#   LISTEN_AUTO_RESTART=1  default: on non-zero exit, restart Python without
#                          wiping capture (CLEAR_CAPTURE only applies to first start)
#   ETHERGOLD_UARTD_PERSIST=1  default: leave pog422_uartd running on listen exit
#   POG422_UARTD=1      default: start pog422_uartd (same as bridge)
#   POG422_UARTD=0      legacy pyserial register-poll
#   POG422_HS_PATH=pi   J3/J4 PI: GPIO HS (BCM8/9) + XR UART RX (forces UARTD=0)
#   POG422_UARTD_SOCK=  override /tmp/pog422_uart_<pos>.sock
#   HW_DEBUG=1          pass through to inject-shim make (bench dense timeline)
#   LOOP_SLEEP_US=10    main Python loop sleep (µs)
#   FIXTURE_EARLY_WIN_PHASE1=1     listen default ON — progwin → fireworks JSON
#   FIXTURE_EARLY_WIN_PHASE2_KENO=1  listen default ON — SuperBall/TouchEasy cash
#   FIXTURE_EARLY_WIN_PHASE2_SLOTS=1 listen default ON — 970C cash
#   FIXTURE_EARLY_WIN_DRY_RUN=0    1 = log hits, do not write fireworks-request.json
#   ETHERGOLD_LOCAL_WIRE_TID=      Phase 2 local TID (else POG_TID / CURRENT_GAME_TID)

set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${ROOT}"

# Preserve explicit CLI; otherwise load ~/EtherGold/fixture-bridge.env
# (listen options live there — not on the command line).
_CLI_POS="${FIXTURE_POS-}"
_CLI_CHANNEL="${CHANNEL-}"
_CLI_VERBOSE="${VERBOSE-}"
_CLI_CLEAR="${CLEAR_CAPTURE-}"
_CLI_UARTD="${POG422_UARTD-}"
_CLI_HS="${POG422_HS_PATH-}"
for _sync in \
  "${ETHERGOLD_SYNC:-}" \
  "${ROOT}/../sync" \
  "/home/topbox/EtherGold/sync"; do
  if [[ -f "${_sync}/source_ethergold_env.sh" ]]; then
    # shellcheck source=/dev/null
    source "${_sync}/source_ethergold_env.sh"
    break
  fi
done
unset _sync
if [[ -n "${_CLI_POS}" ]]; then export FIXTURE_POS="${_CLI_POS}"; fi
if [[ -n "${_CLI_CHANNEL}" ]]; then export CHANNEL="${_CLI_CHANNEL}"; fi
if [[ -n "${_CLI_VERBOSE}" ]]; then export VERBOSE="${_CLI_VERBOSE}"; fi
if [[ -n "${_CLI_CLEAR}" ]]; then export CLEAR_CAPTURE="${_CLI_CLEAR}"; fi
if [[ -n "${_CLI_UARTD}" ]]; then export POG422_UARTD="${_CLI_UARTD}"; fi
if [[ -n "${_CLI_HS}" ]]; then export POG422_HS_PATH="${_CLI_HS}"; fi
unset _CLI_POS _CLI_CHANNEL _CLI_VERBOSE _CLI_CLEAR _CLI_UARTD _CLI_HS

# shellcheck source=uartd_common.sh
source "${ROOT}/uartd_common.sh"

FIXTURE_POS="${FIXTURE_POS:-}"
if [[ -z "${FIXTURE_POS}" ]] || ! [[ "${FIXTURE_POS}" =~ ^[1-9][0-9]?$ ]]; then
  echo "FATAL: set FIXTURE_POS=1..99 (51 = pos5 POG422 witness)" >&2
  exit 1
fi

PI_LABEL="${PI_LABEL:-topbox${FIXTURE_POS}}"
CHANNEL="${CHANNEL:-machine}"
# Listen is single-UART witness — bridge's CHANNEL=both is not valid here.
if [[ "${CHANNEL}" == "both" ]]; then
  echo "NOTE: CHANNEL=both (from fixture-bridge.env) → machine for listen" >&2
  CHANNEL=machine
fi
if [[ "${CHANNEL}" != "machine" && "${CHANNEL}" != "ois" ]]; then
  echo "FATAL: listen CHANNEL must be machine or ois (got ${CHANNEL})" >&2
  exit 1
fi
OUTPUT_DIR="${OUTPUT_DIR:-./data}"
LOG_DIR="${LOG_DIR:-./logs}"
VERBOSE="${VERBOSE:-0}"
IGNORE_HS="${IGNORE_HS:-0}"
FIXTURE_DIR="${FIXTURE_DIR:-}"
CLEAR_CAPTURE="${CLEAR_CAPTURE:-1}"
LISTEN_AUTO_RESTART="${LISTEN_AUTO_RESTART:-1}"
FIXTURE_PROD="${FIXTURE_PROD:-0}"
NO_PROGRESSIVE_CSV="${NO_PROGRESSIVE_CSV:-0}"
ETHERGOLD_ROOT="${ETHERGOLD_ROOT:-${HOME}/EtherGold}"
ETHERGOLD_PROGRESSIVES_CSV="${ETHERGOLD_PROGRESSIVES_CSV:-${ETHERGOLD_ROOT}/progressives.csv}"
STATS_INTERVAL="${STATS_INTERVAL:-30}"
POG422_UARTD="${POG422_UARTD:-1}"
LOOP_SLEEP_US="${LOOP_SLEEP_US:-10}"
# Witness never injects — even if shell inherited bridge env.
export POG422_UARTD_INJECT=0

if [[ -n "${FIXTURE_DIR}" ]]; then
  OUTPUT_DIR="${FIXTURE_DIR}/data"
  LOG_DIR="${FIXTURE_DIR}/logs"
fi

mkdir -p "${LOG_DIR}" "${OUTPUT_DIR}"

remove_capture_file() {
  local f="$1"
  [[ -e "${f}" ]] || return 0
  if rm -f "${f}" 2>/dev/null; then
    return 0
  fi
  local owner
  owner="$(stat -c '%U:%G' "${f}" 2>/dev/null || echo unknown)"
  echo "FATAL: cannot remove ${f} (owner ${owner})" >&2
  echo "  A prior sudo run left root-owned capture files. Fix once:" >&2
  echo "    sudo chown -R $(id -un):$(id -gn) ${OUTPUT_DIR} ${LOG_DIR}" >&2
  echo "  Or append without delete: CLEAR_CAPTURE=0" >&2
  return 1
}

if [[ "${CLEAR_CAPTURE}" == "1" ]]; then
  # Full wipe (rotated events.jsonl.* included). Must run before uartd starts
  # writing logs/uartd.log. Python then --append-capture so it does not re-wipe.
  # Auto-restart path below never re-wipes — capture continues across recoveries.
  pog422_wipe_capture_dirs "${OUTPUT_DIR}" "${LOG_DIR}"
  echo "Wiped capture dirs ${OUTPUT_DIR} and ${LOG_DIR}" >&2
fi

if [[ -x "${ROOT}/ensure_tty_xrusb.sh" ]]; then
  bash "${ROOT}/ensure_tty_xrusb.sh"
elif ! ls /dev/ttyXRUSB* >/dev/null 2>&1; then
  echo "FATAL: /dev/ttyXRUSB* not found" >&2
  exit 1
fi

export PYTHONUNBUFFERED=1

# ignore_hs is pyserial-only — do not start uartd (it would hold the port).
if [[ "${IGNORE_HS}" == "1" ]]; then
  POG422_UARTD=0
fi

# Pi HS: J3/J4 PI disconnects XR GPIO1. Sense HS on BCM8/9; RX stays ttyXRUSB.
if [[ -z "${POG422_HS_PATH:-}" && -f /etc/ethergold/ethergold.env ]]; then
  POG422_HS_PATH="$(
    awk -F= '/^POG422_HS_PATH=/ {
      v=$2; gsub(/^[ \t"\047]+|[ \t"\047]+$/, "", v); print v
    }' /etc/ethergold/ethergold.env | tail -1
  )"
fi
case "${POG422_HS_PATH:-}" in
  pi|gpio|j19|pi_hs)
    export POG422_HS_PATH=pi
    if [[ "${POG422_UARTD}" == "1" ]]; then
      echo "NOTE: POG422_HS_PATH=pi → UARTD=0 (GPIO HS + XR UART RX)" >&2
      POG422_UARTD=0
    fi
    ;;
esac

# Production collector restart used to SIGKILL uartd via this EXIT trap and
# wedge ttyXRUSB until a hard power cycle. Persist by default; attach on respawn.
# Pi-HS listen is pyserial (UARTD=0) — do not print the persist line.
if [[ "${POG422_UARTD}" == "1" && "${ETHERGOLD_UARTD_PERSIST:-1}" == "1" ]]; then
  trap 'echo "hat_listen: leaving pog422_uartd up (ETHERGOLD_UARTD_PERSIST=1)" >&2' EXIT
elif [[ "${POG422_UARTD}" == "1" ]]; then
  trap 'pog422_stop_uartd' EXIT INT TERM
fi

ARGS=(
  "${ROOT}/pog422_listen.py"
  --fixture-pos "${FIXTURE_POS}"
  --pi-label "${PI_LABEL}"
  --channel "${CHANNEL}"
  --output-dir "${OUTPUT_DIR}"
  --logs-dir "${LOG_DIR}"
  --stats-interval "${STATS_INTERVAL}"
  --progressives-csv "${ETHERGOLD_PROGRESSIVES_CSV}"
  --loop-sleep-us "${LOOP_SLEEP_US}"
)

if [[ "${POG422_UARTD}" == "1" ]]; then
  ARGS+=(--uartd)
else
  ARGS+=(--no-uartd)
fi
if [[ "${VERBOSE}" == "1" ]]; then
  ARGS+=(--echo-events)
fi
# Shell owns wipe when CLEAR_CAPTURE=1 (before uartd). Always append afterward so
# Python does not empty logs/ while uartd is already writing uartd.log.
ARGS+=(--append-capture)
if [[ "${IGNORE_HS}" == "1" ]]; then
  ARGS+=(--ignore-hs)
fi
if [[ "${FIXTURE_PROD}" == "1" ]]; then
  ARGS+=(--prod)
fi
if [[ "${FIXTURE_HS_WITNESS:-1}" == "0" ]]; then
  ARGS+=(--no-hs-witness)
fi
if [[ "${NO_PROGRESSIVE_CSV}" == "1" ]]; then
  ARGS+=(--no-progressive-csv)
fi

echo "Fixture listen pos=${FIXTURE_POS} label=${PI_LABEL} prod=${FIXTURE_PROD} capture=$([[ "${IGNORE_HS}" == "1" ]] && echo ignore_hs || echo handshake)" >&2
echo "UART mode: uartd=${POG422_UARTD} inject=0 hs_path=${POG422_HS_PATH:-uart} socket=${POG422_UARTD_SOCK:-/tmp/pog422_uart_${FIXTURE_POS}.sock} HW_DEBUG=${HW_DEBUG:-0}" >&2
echo "Auto-restart: ${LISTEN_AUTO_RESTART} (restart keeps capture; wipe only on first CLEAR_CAPTURE=1)" >&2
echo "Progressives: ${ETHERGOLD_PROGRESSIVES_CSV}" >&2
echo "Log dir: ${OUTPUT_DIR}" >&2

LISTEN_RESTARTS=0
while true; do
  if [[ "${POG422_UARTD}" == "1" ]]; then
    if [[ "${EUID}" -eq 0 ]]; then
      echo "WARN: do not run hat_listen.sh with sudo — use topbox" >&2
    fi
    if ! pog422_start_uartd "${ROOT}" "${FIXTURE_POS}" "${CHANNEL}" "${LOG_DIR}"; then
      exit 1
    fi
  fi

  # Always tee Python traceback into fixture_listen.log (append across restarts).
  set +e
  python3 "${ARGS[@]}" 2>> "${LOG_DIR}/fixture_listen.log"
  LISTEN_RC=$?
  set -e

  if [[ "${LISTEN_RC}" -eq 0 ]]; then
    exit 0
  fi

  # 130/143 = Ctrl-C / SIGTERM — do not auto-restart.
  if [[ "${LISTEN_RC}" -eq 130 || "${LISTEN_RC}" -eq 143 ]]; then
    echo "Listen stopped (signal exit ${LISTEN_RC})" >&2
    exit "${LISTEN_RC}"
  fi

  echo "FATAL: listen exited ${LISTEN_RC} — see ${LOG_DIR}/fixture_listen.log" >&2
  if [[ -f "${LOG_DIR}/fixture_listen.log" ]]; then
    tail -n 30 "${LOG_DIR}/fixture_listen.log" >&2
  fi
  if [[ -f "${LOG_DIR}/uartd.log" ]]; then
    echo "--- ${LOG_DIR}/uartd.log (last 20 lines) ---" >&2
    tail -n 20 "${LOG_DIR}/uartd.log" >&2
  fi

  # GPIO EBUSY is another process (often ethergold-collector), not a USB blip.
  if [[ -f "${LOG_DIR}/fixture_listen.log" ]] \
    && grep -q 'already claimed' "${LOG_DIR}/fixture_listen.log"; then
    echo "FATAL: GPIO HS line already claimed — not auto-restarting." >&2
    echo "  If systemd collector owns BCM9: sudo systemctl stop ethergold-collector.service" >&2
    exit "${LISTEN_RC}"
  fi

  if [[ "${LISTEN_AUTO_RESTART}" != "1" ]]; then
    exit "${LISTEN_RC}"
  fi

  LISTEN_RESTARTS=$((LISTEN_RESTARTS + 1))
  {
    echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) listen_auto_restart n=${LISTEN_RESTARTS} exit=${LISTEN_RC} clear_capture=0"
  } >> "${LOG_DIR}/fixture_listen.log"
  echo "WARN: listen auto-restart #${LISTEN_RESTARTS} without clearing capture (exit ${LISTEN_RC})" >&2
  if [[ "${ETHERGOLD_UARTD_PERSIST:-1}" != "1" ]]; then
    pog422_stop_uartd
  fi
  sleep 2
done
