#!/usr/bin/env bash
#
# capture-evidence.sh  -  produce the visual artefacts the PR and the Jira comment
# cite: the "after" screenshot, and the size-normalised form of anything that has
# to survive an attachment ceiling.
#
# Contract: multi-agent-refs/features/visual-evidence.md
#
# Two jobs, deliberately in one script because they share the naming convention
# that the Jira comment references by filename:
#
#   capture-evidence.sh after  --task <id> --platform <ios|android> --label <slug>
#       Capture the current screen of the running app into the evidence dir.
#
#   capture-evidence.sh fit    --file <path> [--max-mb <n>]
#   capture-evidence.sh limits
#       Print the resolved visualEvidence settings as KEY=VALUE lines.
#       Bring one artefact under the attachment ceiling, degrading quality but
#       never dropping the artefact. Prints the path to use (may be a new file).
#
# Env:
#   EVIDENCE_DIR   default "$PWD/.pipeline/evidence"
#   MAX_ATTACH_MB  default from prefs visualEvidence.maxAttachmentMb, else 10
#
# Exit: 0 produced, 2 usage/environment, 4 capture unavailable (no device / no
#       build) - a caller records this as a gap rather than failing the phase.
set -uo pipefail

MODE="${1:-}"; shift 2>/dev/null || true

EVIDENCE_DIR="${EVIDENCE_DIR:-$PWD/.pipeline/evidence}"
PREFS="$HOME/.claude/multi-agent-preferences.json"

# Reads visualEvidence.enabled, visualEvidence.maxAttachmentMb and
# visualEvidence.maxVideoSeconds. One node call: three separate ones cost three
# process starts on every phase boundary.
prefs_visual() {
  node -e '
    const fs=require("fs");
    let v={};
    try{v=((JSON.parse(fs.readFileSync(process.argv[1],"utf8")).global||{}).visualEvidence)||{}}catch{}
    const num=(x,d)=>Number.isFinite(x)&&x>0?x:d;
    process.stdout.write(
      "VISUAL_EVIDENCE_ENABLED="+(v.enabled===false?"false":"true")+"\n"+
      "MAX_ATTACHMENT_MB="+num(v.maxAttachmentMb,10)+"\n"+
      "MAX_VIDEO_SECONDS="+num(v.maxVideoSeconds,60)+"\n");
  ' "$PREFS" 2>/dev/null || printf 'VISUAL_EVIDENCE_ENABLED=true\nMAX_ATTACHMENT_MB=10\nMAX_VIDEO_SECONDS=60\n'
}
eval "$(prefs_visual)"
MAX_ATTACH_MB="${MAX_ATTACH_MB:-$MAX_ATTACHMENT_MB}"

# visualEvidence.enabled off means the whole feature is off: no capture, no
# re-encode, no render. Exit 0 so a phase that calls it unconditionally is not
# failed by a setting the user chose.
if [ "$VISUAL_EVIDENCE_ENABLED" = "false" ] && [ "$MODE" != "limits" ]; then
  echo "capture-evidence: visualEvidence.enabled is false  -  nothing to do" >&2
  exit 0
fi

file_bytes() {
  # Branch on the OS rather than trying one form and falling through: on GNU
  # coreutils `stat -f` is --file-system, so it SUCCEEDS and returns a mount
  # point. The fallback would never run and every size check would compare
  # against a filesystem name.
  case "$(uname -s)" in
    Darwin | *BSD*) stat -f %z "$1" 2>/dev/null || echo 0 ;;
    *) stat -c %s "$1" 2>/dev/null || echo 0 ;;
  esac
}

case "$MODE" in
  after)
    TASK=""; PLATFORM=""; LABEL="screen"
    while [ "$#" -gt 0 ]; do
      case "$1" in
        --task) TASK="${2:-}"; shift 2 ;;
        --platform) PLATFORM="${2:-}"; shift 2 ;;
        --label) LABEL="${2:-screen}"; shift 2 ;;
        *) echo "capture-evidence: unknown option $1" >&2; exit 2 ;;
      esac
    done
    [ -n "$TASK" ] && [ -n "$PLATFORM" ] || {
      echo "usage: capture-evidence.sh after --task <id> --platform <ios|android> [--label <slug>]" >&2
      exit 2
    }
    mkdir -p "$EVIDENCE_DIR"
    OUT="$EVIDENCE_DIR/${TASK}-${LABEL}-after.png"

    # The capture itself goes through the toolkit MCP tools, which the phase doc
    # calls - this script owns the naming, the status-bar hygiene and the
    # downscale, so every caller produces frames that can be compared. When the
    # CLI helpers are absent the caller records a gap; a missing device is not a
    # phase failure.
    case "$PLATFORM" in
      ios)
        command -v xcrun >/dev/null 2>&1 || { echo "capture-evidence: xcrun unavailable" >&2; exit 4; }
        xcrun simctl status_bar booted override \
          --time "9:41" --batteryState charged --batteryLevel 100 \
          --cellularMode active --cellularBars 4 --wifiMode active --wifiBars 3 >/dev/null 2>&1 || true
        xcrun simctl io booted screenshot "$OUT" >/dev/null 2>&1 || { echo "capture-evidence: no booted simulator" >&2; exit 4; }
        ;;
      android)
        command -v adb >/dev/null 2>&1 || { echo "capture-evidence: adb unavailable" >&2; exit 4; }
        adb shell cmd statusbar overlay-icon-visibility hide >/dev/null 2>&1 || true
        adb exec-out screencap -p > "$OUT" 2>/dev/null || { echo "capture-evidence: no attached device" >&2; exit 4; }
        [ -s "$OUT" ] || { echo "capture-evidence: empty capture" >&2; exit 4; }
        ;;
      *) echo "capture-evidence: unsupported platform '$PLATFORM'" >&2; exit 2 ;;
    esac

    # Downscale to 1242px wide when wider. sips ships with macOS; ImageMagick is
    # the fallback. Neither present -> keep the native frame, which is correct,
    # only larger.
    if command -v sips >/dev/null 2>&1; then
      W=$(sips -g pixelWidth "$OUT" 2>/dev/null | awk '/pixelWidth/{print $2}')
      [ -n "$W" ] && [ "$W" -gt 1242 ] 2>/dev/null && sips -Z 1242 "$OUT" >/dev/null 2>&1
    elif command -v magick >/dev/null 2>&1; then
      magick "$OUT" -resize '1242x>' "$OUT" >/dev/null 2>&1
    fi
    printf '%s\n' "$OUT"
    ;;

  fit)
    FILE=""; MAX_MB="$MAX_ATTACH_MB"
    while [ "$#" -gt 0 ]; do
      case "$1" in
        --file) FILE="${2:-}"; shift 2 ;;
        --max-mb) MAX_MB="${2:-$MAX_ATTACH_MB}"; shift 2 ;;
        *) echo "capture-evidence: unknown option $1" >&2; exit 2 ;;
      esac
    done
    [ -n "$FILE" ] && [ -f "$FILE" ] || { echo "usage: capture-evidence.sh fit --file <path> [--max-mb <n>]" >&2; exit 2; }
    LIMIT=$((MAX_MB * 1024 * 1024))
    SIZE=$(file_bytes "$FILE")
    if [ "$SIZE" -le "$LIMIT" ]; then printf '%s\n' "$FILE"; exit 0; fi

    BASE="${FILE%.*}"; EXT="${FILE##*.}"
    case "$EXT" in
      mp4|mov|m4v)
        # Quality down, artefact kept. 720p + a lower bitrate clears an order of
        # magnitude; a flow video is watched for what moves, not for its pixels.
        if command -v ffmpeg >/dev/null 2>&1; then
          SMALL="${BASE}-720p.mp4"
          ffmpeg -y -i "$FILE" -vf "scale='min(1280,iw)':-2" -c:v libx264 -crf 30 \
                 -preset veryfast -an "$SMALL" >/dev/null 2>&1
          if [ -f "$SMALL" ] && [ "$(file_bytes "$SMALL")" -le "$LIMIT" ]; then
            printf '%s\n' "$SMALL"; exit 0
          fi
          # Last resort: four frames as one sheet, so the reviewer still sees the
          # flow even though the recording could not travel.
          SHEET="${BASE}-contact.png"
          DUR=$(ffprobe -v error -show_entries format=duration -of csv=p=0 "$FILE" 2>/dev/null | cut -d. -f1)
          [ -n "$DUR" ] && [ "$DUR" -gt 0 ] 2>/dev/null || DUR=4
          ffmpeg -y -i "$FILE" -vf "fps=4/$DUR,scale=480:-2,tile=2x2" -frames:v 1 "$SHEET" >/dev/null 2>&1
          if [ -f "$SHEET" ]; then printf '%s\n' "$SHEET"; exit 0; fi
        fi
        echo "capture-evidence: $FILE exceeds ${MAX_MB}MB and ffmpeg is unavailable" >&2
        printf '%s\n' "$FILE"; exit 0
        ;;
      png|jpg|jpeg)
        SMALL="${BASE}.jpg"
        if command -v sips >/dev/null 2>&1; then
          sips -s format jpeg -s formatOptions 80 "$FILE" --out "$SMALL" >/dev/null 2>&1
        elif command -v magick >/dev/null 2>&1; then
          magick "$FILE" -quality 80 "$SMALL" >/dev/null 2>&1
        fi
        if [ -f "$SMALL" ] && [ "$(file_bytes "$SMALL")" -le "$LIMIT" ]; then
          printf '%s\n' "$SMALL"; exit 0
        fi
        printf '%s\n' "$FILE"; exit 0
        ;;
      *) printf '%s\n' "$FILE"; exit 0 ;;
    esac
    ;;

  limits)
    # The flow recorder lives in the phase doc (it drives MCP tools, not a
    # shell); it reads its duration cap from here so visualEvidence.maxVideoSeconds
    # is one resolved value rather than a number repeated in two documents.
    prefs_visual
    ;;

  *)
    echo "usage: capture-evidence.sh after|fit|limits ..." >&2
    exit 2
    ;;
esac
