#!/bin/bash
# winkod_verify.sh — E2E smoke for Винкод (Winkod).
# Verifies: (1) every bottle launches & stays alive with no fatal errors,
# (2) virtual serial (COM) pass-through works end-to-end through wine.
#
# Usage:
#   winkod_verify.sh            # run all checks
#   winkod_verify.sh launch     # only bottle launch-smoke
#   winkod_verify.sh serial     # only virtual-serial pass-through
#
# Exit 0 = all green. Non-zero = at least one check failed.

set -uo pipefail
MODE="${1:-all}"
FAILS=0

# --- locate bundled wine -----------------------------------------------------
WINE=""
for c in "/Applications/Винкод.app/Contents/Resources/wine" \
         "$HOME/Library/Application Support/Winkod-build/wine"; do
  [ -x "$c/bin/wineloader" ] && { WINE="$c"; break; }
done
[ -n "$WINE" ] || { echo "FATAL: bundled wineloader not found"; exit 2; }
SUP="$HOME/Library/Application Support/Winkod"
MANIFEST="$SUP/bottles/manifest.json"

wine_env() { # $1=prefix  → prints env-prefixed wineloader invocation base
  echo "env -i HOME=$HOME PATH=$WINE/bin:/usr/bin:/bin WINEPREFIX=$1 WINEDEBUG=warn \
WINEDLLPATH=$WINE/lib/wine DYLD_FALLBACK_LIBRARY_PATH=$WINE/lib64:$WINE/lib:/usr/lib"
}

# --- 1. launch smoke ---------------------------------------------------------
launch_smoke() {
  echo "═══ [1] launch-smoke: каждая бутыль стартует и держит окно ═══"
  [ -f "$MANIFEST" ] || { echo "  no manifest — нет бутылей"; return; }
  # rows: uuid|name|unix-exe-path
  while IFS='|' read -r uuid name exe; do
    [ -z "$uuid" ] && continue
    local prefix="$SUP/bottles/$uuid"
    local log="/tmp/winkod_verify_$uuid.log"
    if [ ! -f "$exe" ]; then echo "  ✗ $name: exe не найден ($exe)"; FAILS=$((FAILS+1)); continue; fi
    env -i HOME="$HOME" PATH="$WINE/bin:/usr/bin:/bin" WINEPREFIX="$prefix" WINEDEBUG=warn \
      WINEDLLPATH="$WINE/lib/wine" DYLD_FALLBACK_LIBRARY_PATH="$WINE/lib64:$WINE/lib:/usr/lib" \
      "$WINE/bin/wineloader" "$exe" >"$log" 2>&1 &
    local base; base=$(basename "$exe")
    sleep 14
    local alive="no"; pgrep -fl "$base" >/dev/null 2>&1 && alive="yes"
    local fatal; fatal=$(grep -icE 'unhandled exception|0xc0000135|0xc0000005|import_dll.*not found' "$log" 2>/dev/null)
    pkill -f "$base" >/dev/null 2>&1
    if [ "$alive" = "yes" ] && [ "$fatal" -eq 0 ]; then
      echo "  ✓ $name: окно открылось, фатальных ошибок нет"
    else
      echo "  ✗ $name: alive=$alive fatal-lines=$fatal (лог: $log)"
      FAILS=$((FAILS+1))
    fi
  done < <(python3 - "$MANIFEST" <<'PY'
import json,sys,urllib.parse,os
for b in json.load(open(sys.argv[1])):
    p=urllib.parse.unquote(b.get("primaryExecutable","").replace("file://",""))
    print(f'{b.get("id","")}|{b.get("name","")}|{p}')
PY
)
}

# --- 2. virtual serial pass-through -----------------------------------------
serial_passthrough() {
  echo "═══ [2] virtual serial (COM) pass-through через wine ═══"
  command -v socat >/dev/null || { echo "  ✗ socat не установлен (brew install socat)"; FAILS=$((FAILS+1)); return; }
  # throwaway prefix so we never touch real bottles
  local prefix="/tmp/winkod_verify_serial_prefix"
  rm -rf "$prefix"; mkdir -p "$prefix"
  env -i HOME="$HOME" PATH="$WINE/bin:/usr/bin:/bin" WINEPREFIX="$prefix" WINEDEBUG=-all \
    WINEDLLPATH="$WINE/lib/wine" DYLD_FALLBACK_LIBRARY_PATH="$WINE/lib64:$WINE/lib:/usr/lib" \
    "$WINE/bin/wineloader" wineboot -i >/dev/null 2>&1
  # socat PTY pair
  socat -d -d pty,raw,echo=0 pty,raw,echo=0 >/tmp/winkod_verify_socat.log 2>&1 &
  local sp=$!; sleep 2
  local A B
  A=$(grep -oE '/dev/ttys[0-9]+' /tmp/winkod_verify_socat.log | sed -n 1p)
  B=$(grep -oE '/dev/ttys[0-9]+' /tmp/winkod_verify_socat.log | sed -n 2p)
  if [ -z "$A" ] || [ -z "$B" ]; then echo "  ✗ socat не создал PTY-пару"; kill $sp 2>/dev/null; FAILS=$((FAILS+1)); return; fi
  ln -sf "$A" "$prefix/dosdevices/com3"
  # reader on far end
  python3 - "$B" <<'PY' &
import os,sys,time,fcntl
fb=os.open(sys.argv[1],os.O_RDWR|os.O_NOCTTY); fcntl.fcntl(fb,fcntl.F_SETFL,os.O_NONBLOCK)
got=[]; t=time.time()
while time.time()-t<8:
    try:
        d=os.read(fb,128)
        if d: got.append(d)
    except BlockingIOError: pass
    time.sleep(0.05)
open('/tmp/winkod_verify_serial_rx.txt','wb').write(b''.join(got))
PY
  sleep 1
  env -i HOME="$HOME" PATH="$WINE/bin:/usr/bin:/bin" WINEPREFIX="$prefix" WINEDEBUG=-all \
    WINEDLLPATH="$WINE/lib/wine" DYLD_FALLBACK_LIBRARY_PATH="$WINE/lib64:$WINE/lib:/usr/lib" \
    "$WINE/bin/wineloader" cmd /c "echo WINKOD-SERIAL-OK> COM3" >/dev/null 2>&1
  sleep 7
  if grep -q 'WINKOD-SERIAL-OK' /tmp/winkod_verify_serial_rx.txt 2>/dev/null; then
    echo "  ✓ wine записал в COM3 → байты дошли на внешний конец PTY (проброс работает)"
  else
    echo "  ✗ байты от wine не дошли через мост"
    FAILS=$((FAILS+1))
  fi
  kill $sp 2>/dev/null; pkill -f 'pty,raw,echo=0' 2>/dev/null
  rm -rf "$prefix"
}

case "$MODE" in
  launch) launch_smoke ;;
  serial) serial_passthrough ;;
  all)    launch_smoke; serial_passthrough ;;
  *) echo "usage: winkod_verify.sh [launch|serial|all]"; exit 2 ;;
esac

echo "═══════════════════════════════════════════"
if [ "$FAILS" -eq 0 ]; then echo "ИТОГ: всё зелёное ✓"; exit 0
else echo "ИТОГ: провалов — $FAILS ✗"; exit 1; fi
