#!/usr/bin/env bash
# check-boot.sh — typecheck BootTidal.hs WITHOUT booting anything.
#
# Why this exists (2026-07-27, mid-rehearsal):
# a DJF "fix" shipped to BootTidal.hs as  (# lpf .. # hpf ..)  — a right
# section containing another operator of the same fixity, which is a hard
# Haskell PARSE ERROR (`#` is infixl 9). One bad line took the whole `let`
# block down, i.e. EVERY g* helper. And nothing surfaced it: the running
# ghci still held the old definitions, so the rig kept making sound while
# the file on disk was unloadable. The bug would only have detonated at the
# next boot — on stage.
#
# The lesson is the rig's recurring one: a definition that lives only in a
# running process is not verified. Prove it structurally, cheaply, cold.
#
# Method: extract the big helper `let` block, dedent it into a real module,
# stub the few stream-bound names (p, setI) it closes over, and run
# `ghc -fno-code`. Types only — no code generated, no stream started, no
# port 6010 grabbed. Safe to run mid-set.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BOOT="${1:-$ROOT/BootTidal.hs}"
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT

# Locate the helper block: the `:{ let ...` that defines the g* helpers,
# found by content rather than hardcoded line numbers so edits don't rot it.
START="$(grep -n '^let -- DPV specific parameters' "$BOOT" | cut -d: -f1)"
if [ -z "$START" ]; then
  echo "check-boot: FAIL — could not locate the helper let-block in $BOOT" >&2
  exit 2
fi
END="$(awk -v s="$START" 'NR>s && /^:\}/ {print NR-1; exit}' "$BOOT")"
if [ -z "$END" ]; then
  echo "check-boot: FAIL — no closing :} after line $START" >&2
  exit 2
fi

{
  echo '{-# LANGUAGE OverloadedStrings #-}'
  echo '{-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}'
  echo 'module BootCheck where'
  echo 'import Sound.Tidal.Context'
  echo 'import Data.Char (toLower)'
  # Names the block closes over from earlier :{ :} blocks (stream-bound).
  echo 'p :: Int -> ControlPattern -> IO ()'
  echo 'p = undefined'
  echo 'setI :: String -> Pattern Int -> IO ()'
  echo 'setI = undefined'
  sed -n "${START},${END}p" "$BOOT" | sed -e '1s/^let //' -e 's/^    //'
} > "$WORK/BootCheck.hs"

echo "check-boot: typechecking $BOOT lines $START-$END ..."
# NB: run ghc ONCE and capture, rather than piping into `grep -q`. Under
# `set -o pipefail` a failing ghc makes the whole pipeline non-zero no matter
# what grep decides, which silently inverts the test — this guard shipped
# broken exactly that way and only its own negative test caught it.
set +e
OUT="$(ghc -fno-code -package tidal "$WORK/BootCheck.hs" 2>&1)"
RC=$?
set -e
if [ "$RC" -ne 0 ]; then
  echo "" >&2
  printf '%s\n' "$OUT" | sed "s#$WORK/BootCheck.hs#<let-block>#" >&2
  echo "" >&2
  echo "check-boot: FAIL — BootTidal.hs would NOT load. Do not boot." >&2
  exit 1
fi
echo "check-boot: OK — helper block typechecks against tidal-1.9.5."

# --- Pass 2: the #55 control-seed block ---------------------------------
# Seeding runs at boot and silences nothing if it fails, but a type error in
# it aborts the rest of the boot file. Check the seed list's shape and the
# streamSetF signature it depends on.
SEED_START="$(grep -n '^let _seed = concat' "$BOOT" | cut -d: -f1)"
if [ -n "$SEED_START" ]; then
  SEED_END="$(awk -v s="$SEED_START" 'NR>=s && /\] :: \[\(String, Double\)\]/ {print NR; exit}' "$BOOT")"
  {
    echo 'module SeedCheck where'
    echo 'import Sound.Tidal.Context'
    # Pin the exact signature the seed block calls through `setF`.
    echo 'sigF :: Stream -> String -> Pattern Double -> IO ()'
    echo 'sigF = streamSetF'
    sed -n "${SEED_START},${SEED_END}p" "$BOOT" | sed '1s/^let //'
    echo 'useSeed :: Stream -> IO ()'
    echo 'useSeed t = mapM_ (\(k, v) -> streamSetF t k (pure v)) _seed'
  } > "$WORK/SeedCheck.hs"
  set +e
  SOUT="$(ghc -fno-code -package tidal "$WORK/SeedCheck.hs" 2>&1)"
  SRC=$?
  set -e
  if [ "$SRC" -ne 0 ]; then
    echo "" >&2
    printf '%s\n' "$SOUT" | sed "s#$WORK/SeedCheck.hs#<seed-block>#" >&2
    echo "check-boot: FAIL — the #55 control-seed block does not typecheck." >&2
    exit 1
  fi
  echo "check-boot: OK — #55 control-seed block typechecks."
else
  echo "check-boot: WARN — no #55 control-seed block found (untouched knobs" >&2
  echo "            will silence their streams; see BootTidal.hs history)." >&2
fi
