#!/usr/bin/env bash
# scripts/install/server.sh - install and setup all server related apps and environments
set -euo pipefail
ORIG_USER=${SUDO_USER:-$(id -un)}
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
SCRIPT_REPO_ROOT=$(cd -- "$SCRIPT_DIR/../.." && pwd)
DEFAULT_REPO_URL=ssh://git@scm.bstein.dev:2242/bstein/lesavka.git
export TMPDIR=${TMPDIR:-/var/tmp}

REF=${LESAVKA_REF:-master}        # fallback
REPO_URL=${LESAVKA_REPO_URL:-}
USER_HOME=$(getent passwd "$ORIG_USER" | cut -d: -f6)

udc_state() {
  local udc=""
  udc=$(ls /sys/class/udc 2>/dev/null | head -n1 || true)
  if [[ -z $udc ]]; then
    echo "unknown"
    return 0
  fi
  cat "/sys/class/udc/$udc/state" 2>/dev/null || echo "unknown"
}

is_attached_state() {
  case "$1" in
    configured|addressed|default|suspended|unknown)
      return 0
      ;;
  esac
  return 1
}

normalize_hdmi_connector() {
  local name="$1"
  if [[ $name =~ (HDMI-A-[0-9]+)$ ]]; then
    printf '%s\n' "${BASH_REMATCH[1]}"
  else
    printf '%s\n' "$name"
  fi
}

read_existing_hdmi_connector() {
  local line value
  line=$(grep -E '^LESAVKA_HDMI_CONNECTOR=' /etc/lesavka/server.env 2>/dev/null | tail -n1 || true)
  [[ -n $line ]] || return 0
  value=${line#*=}
  value=${value%\"}
  value=${value#\"}
  value=${value%\'}
  value=${value#\'}
  normalize_hdmi_connector "$value"
}

boot_config_path() {
  for path in /boot/config.txt /boot/firmware/config.txt; do
    if [[ -e $path ]]; then
      printf '%s\n' "$path"
      return 0
    fi
  done
  return 0
}

boot_cmdline_path() {
  for path in /boot/cmdline.txt /boot/firmware/cmdline.txt; do
    if [[ -e $path ]]; then
      printf '%s\n' "$path"
      return 0
    fi
  done
  return 0
}

runtime_forced_hdmi_connectors() {
  [[ -r /proc/cmdline ]] || return 0
  tr ' ' '\n' </proc/cmdline |
    sed -n 's/^video=\(HDMI-A-[0-9]\+\):.*e$/\1/p' |
    sort -u
}

remove_lesavka_hdmi_config_force() {
  local cfg tmp
  cfg=$(boot_config_path)
  [[ -n $cfg ]] || return 0
  tmp=$(mktemp)
  awk '
    /^# lesavka: force HDMI/ {next}
    /^hdmi_force_hotplug:[0-9]+=1$/ {next}
    /^hdmi_group:[0-9]+=2$/ {next}
    /^hdmi_mode:[0-9]+=82$/ {next}
    {print}
  ' "$cfg" >"$tmp"
  sudo install -m 0644 "$tmp" "$cfg"
  rm -f "$tmp"
}

write_hdmi_cmdline_force() {
  local connector="${1:-}" cmd token tmp line
  cmd=$(boot_cmdline_path)
  [[ -n $cmd ]] || return 0
  tmp=$(mktemp)
  line=$(cat "$cmd")
  for token in $line; do
    case "$token" in
      video=HDMI-A-*:1920x1080@60e)
        continue
        ;;
    esac
    printf '%s ' "$token" >>"$tmp"
  done
  if [[ -n $connector ]]; then
    printf 'video=%s:1920x1080@60e ' "$connector" >>"$tmp"
  fi
  sed -i 's/[[:space:]]*$//' "$tmp"
  printf '\n' >>"$tmp"
  sudo install -m 0644 "$tmp" "$cmd"
  rm -f "$tmp"
}

clear_lesavka_hdmi_boot_force() {
  remove_lesavka_hdmi_config_force
  write_hdmi_cmdline_force ""
}

write_hdmi_boot_force() {
  local connector="$1" idx cfg
  connector=$(normalize_hdmi_connector "$connector")
  if [[ ! $connector =~ ^HDMI-A-([0-9]+)$ ]]; then
    echo "⚠️  cannot write HDMI boot force for unexpected connector '$connector'." >&2
    return 0
  fi

  idx=$((BASH_REMATCH[1] - 1))
  cfg=$(boot_config_path)
  remove_lesavka_hdmi_config_force
  if [[ -n $cfg ]]; then
    {
      echo
      echo "# lesavka: force HDMI mode for capture dongle ($connector)"
      printf 'hdmi_force_hotplug:%s=1\n' "$idx"
      printf 'hdmi_group:%s=2\n' "$idx"
      printf 'hdmi_mode:%s=82\n' "$idx"
    } | sudo tee -a "$cfg" >/dev/null
  fi
  write_hdmi_cmdline_force "$connector"
}

prepare_hdmi_detection_boot_state() {
  local -a forced=()

  if [[ -n ${LESAVKA_HDMI_CONNECTOR:-} ]]; then
    return 0
  fi

  mapfile -t forced < <(runtime_forced_hdmi_connectors)
  if (( ${#forced[@]} <= 1 )); then
    return 0
  fi

  echo "⚠️  current boot forces multiple HDMI connectors: ${forced[*]}" >&2
  echo "    Linux will report every forced connector as connected, so auto-detection cannot be trusted yet." >&2
  clear_lesavka_hdmi_boot_force
  echo "✅  removed Lesavka-managed dual-HDMI force settings from boot config." >&2
  echo "    Reboot Theia, then rerun this installer so it can see the real connected HDMI port." >&2
  echo "    If both ports are physically connected after reboot, rerun once with LESAVKA_HDMI_CONNECTOR=HDMI-A-N." >&2
  exit 1
}

describe_hdmi_connectors() {
  local dev name status id
  for dev in /sys/class/drm/card*-HDMI-A-*; do
    [[ -e $dev/status ]] || continue
    name=$(basename "$dev")
    status=$(cat "$dev/status" 2>/dev/null || true)
    id=$(cat "$dev/connector_id" 2>/dev/null || true)
    printf '    %s (%s): %s\n' "$(normalize_hdmi_connector "$name")" "${id:-no-id}" "${status:-unknown}" >&2
  done
}

detect_connected_hdmi_connector() {
  local dev name stable_name status score
  local best="" best_score=0 best_count=0
  declare -A scores=()

  # HDMI status can briefly flap during gadget/display bring-up, so sample a
  # few times. Store the logical connector suffix, not the DRM card prefix, so
  # this stays valid if Linux renumbers cardN across boots.
  for _ in 1 2 3 4 5; do
    for dev in /sys/class/drm/card*-HDMI-A-*; do
      [[ -e $dev/status ]] || continue
      name=$(basename "$dev")
      stable_name=$(normalize_hdmi_connector "$name")
      status=$(cat "$dev/status" 2>/dev/null || true)
      if [[ $status == connected ]]; then
        scores[$stable_name]=$(( ${scores[$stable_name]:-0} + 1 ))
      fi
    done
    sleep 0.2
  done

  for name in "${!scores[@]}"; do
    score=${scores[$name]}
    if (( score > best_score )); then
      best=$name
      best_score=$score
    fi
  done

  (( best_score > 0 )) || return 0

  for name in "${!scores[@]}"; do
    if (( scores[$name] == best_score )); then
      best_count=$((best_count + 1))
    fi
  done

  if (( best_count == 1 )); then
    printf '%s\n' "$best"
    return 0
  fi

  echo "❌ multiple HDMI connectors are equally connected; refusing to guess." >&2
  describe_hdmi_connectors
  echo "   Disconnect the non-target HDMI output or run once with LESAVKA_HDMI_CONNECTOR=HDMI-A-N." >&2
  return 1
}

resolve_hdmi_connector() {
  local detected existing
  if [[ -n ${LESAVKA_HDMI_CONNECTOR:-} ]]; then
    normalize_hdmi_connector "$LESAVKA_HDMI_CONNECTOR"
    return 0
  fi

  if ! detected=$(detect_connected_hdmi_connector); then
    return 1
  fi
  if [[ -n $detected ]]; then
    printf '%s\n' "$detected"
    return 0
  fi

  existing=$(read_existing_hdmi_connector)
  if [[ -n $existing ]]; then
    echo "⚠️  no connected HDMI connector detected; preserving existing LESAVKA_HDMI_CONNECTOR=$existing." >&2
    printf '%s\n' "$existing"
  fi
}

run_as_user() {
  sudo -u "$ORIG_USER" env HOME="$USER_HOME" SSH_AUTH_SOCK="${SSH_AUTH_SOCK:-}" "$@"
}

while [[ $# -gt 0 ]]; do
  case $1 in
    -r|--ref) REF="$2"; shift 2 ;;
    -h|--help)
      echo "Usage: $0 [--ref <branch|commit>]"; exit 0 ;;
    *) echo "Unknown option: $1"; exit 1 ;;
  esac
done
echo "==> Using git ref: $REF"
mkdir -p "$TMPDIR"

if [[ -z $REPO_URL ]] && [[ -d $SCRIPT_REPO_ROOT/.git ]]; then
  REPO_URL=$(git -C "$SCRIPT_REPO_ROOT" config --get remote.origin.url || true)
fi
REPO_URL=${REPO_URL:-$DEFAULT_REPO_URL}

echo "==> 1a. Base packages"
sudo pacman -Sq --needed --noconfirm git \
                                      rustup \
                                      protobuf \
                                      abseil-cpp \
                                      gcc \
                                      alsa-utils \
                                      pipewire \
                                      pipewire-pulse \
                                      tailscale \
                                      base-devel \
                                      v4l-utils \
                                      gstreamer \
                                      gst-plugins-base \
                                      gst-plugins-base-libs \
                                      gst-plugins-good \
                                      gst-plugins-bad \
                                      gst-plugins-bad-libs \
                                      gst-plugins-ugly \
                                      gst-libav \
                                      tcpdump \
                                      lsof
if ! command -v yay >/dev/null 2>&1; then
  echo "==> 1b. installing yay from AUR ..."
  run_as_user env TMPDIR="$TMPDIR" bash -c '
    rm -rf "$TMPDIR/yay" &&
    cd "$TMPDIR" && git clone --depth 1 https://aur.archlinux.org/yay.git &&
    cd yay && makepkg -si --noconfirm'
fi
# yay -S --noconfirm grpcurl-bin

echo "==> 1c. GPIO permissions for relay"
echo 'z /dev/gpiochip* 0660 root gpio -' | sudo tee /etc/tmpfiles.d/gpiochip.conf >/dev/null
sudo systemd-tmpfiles --create /etc/tmpfiles.d/gpiochip.conf || true

echo "==> 2a. Kernel-driver tweaks"
cat <<'EOF' | sudo tee /etc/modprobe.d/gc311-stream.conf >/dev/null
options uvcvideo quirks=0x200 timeout=10000
EOF
prepare_hdmi_detection_boot_state

echo "==> 2b. Predictable /dev names for each capture card"
# ensure relay (GPIO power) is on if present
if systemctl list-unit-files | grep -q '^relay.service'; then
  sudo systemctl enable --now relay.service
  sleep 2
fi

# probe v4l2 devices for GC311s (07ca:3311)
mapfile -t GC_VIDEOS < <(
    sudo v4l2-ctl --list-devices 2>/dev/null |
        awk '/Live Gamer MINI/{getline; print $1}'
)

# fallback via udev if v4l2-ctl output is empty/partial
if [ "${#GC_VIDEOS[@]}" -ne 2 ]; then
    mapfile -t GC_VIDEOS < <(
        for dev in /dev/video*; do
            props=$(sudo udevadm info -q property -n "$dev" 2>/dev/null || true)
            if echo "$props" | grep -q 'ID_VENDOR_ID=07ca' && echo "$props" | grep -q 'ID_MODEL_ID=3311'; then
                echo "$dev"
            fi
        done | sort -u
    )
fi

if [ "${#GC_VIDEOS[@]}" -ne 2 ]; then
    echo "⚠️  GC311 capture cards not fully present; skipping udev eye-link refresh." >&2
    if [ "${#GC_VIDEOS[@]}" -eq 0 ]; then
        echo "    Detected: none" >&2
    else
        printf '    Detected: %s\n' "${GC_VIDEOS[@]}" >&2
    fi
    echo "    The server install will continue, and existing /dev/lesavka_* links stay untouched." >&2
else
    mapfile -t TAGS < <(
        for v in "${GC_VIDEOS[@]}"; do
            sudo udevadm info -q property -n "$v" |
              awk -F= '/^ID_PATH_TAG=/{print $2}'
        done
    )

    if [ -z "${TAGS[0]:-}" ] || [ -z "${TAGS[1]:-}" ]; then
        echo "⚠️  GC311 cards were detected, but ID_PATH_TAG lookup was incomplete." >&2
        echo "    Skipping udev eye-link refresh and preserving any existing /dev/lesavka_* links." >&2
    else
        printf '   ↪ Left  card: %s  (%s)\n' "${GC_VIDEOS[0]}" "${TAGS[0]}"
        printf '   ↪ Right card: %s  (%s)\n' "${GC_VIDEOS[1]}" "${TAGS[1]}"

        LEFT_TAG=${TAGS[0]}
        RIGHT_TAG=${TAGS[1]}

        sudo tee /etc/udev/rules.d/85-gc311.rules >/dev/null <<EOF
# auto-generated by lesavka/scripts/daemon/install-server.sh - DO NOT EDIT
SUBSYSTEM=="video4linux", ATTR{index}=="0", ENV{ID_PATH_TAG}=="$LEFT_TAG",  SYMLINK+="lesavka_l_eye"
SUBSYSTEM=="video4linux", ATTR{index}=="0", ENV{ID_PATH_TAG}=="$RIGHT_TAG", SYMLINK+="lesavka_r_eye"
EOF

        sudo udevadm control --reload
        sudo udevadm trigger --subsystem-match=video4linux
        sudo udevadm settle
    fi
fi

echo "==> 3. Rust toolchain"
sudo rustup default stable
run_as_user rustup default stable

echo "==> 4a. Source checkout"
SRC_DIR=/var/src/lesavka
if [[ ! -d $SRC_DIR ]]; then
  sudo mkdir -p /var/src
  sudo chown "$ORIG_USER":"$ORIG_USER" /var/src
fi
if [[ -d $SRC_DIR/.git ]]; then
  run_as_user git -C "$SRC_DIR" fetch --all --tags --prune
else
  run_as_user git clone "$REPO_URL" "$SRC_DIR"
fi

if run_as_user git -C "$SRC_DIR" rev-parse --verify --quiet "origin/$REF" >/dev/null; then
  run_as_user git -C "$SRC_DIR" checkout -B "$REF" "origin/$REF"
else
  run_as_user git -C "$SRC_DIR" checkout --force "$REF"
fi

echo "==> 4b. Kernel upgrade (optional)"
if [[ "${LESAVKA_KERNEL_UPDATE:-0}" != "0" ]]; then
  sudo LESAVKA_KERNEL_BUILD_USER="$ORIG_USER" bash "$SRC_DIR/scripts/kernel/build-linux-rpi.sh"
else
  echo "⚠️  skipping kernel upgrade (LESAVKA_KERNEL_UPDATE=0)"
fi

echo "==> 4c. Source build"
run_as_user env TMPDIR="$TMPDIR" bash -c "cd '$SRC_DIR/server' && cargo clean && cargo build --release --bins"

echo "==> 5. Install binaries"
sudo install -Dm755 "$SRC_DIR/target/release/lesavka-server" /usr/local/bin/lesavka-server
sudo install -Dm755 "$SRC_DIR/target/release/lesavka-uvc"    /usr/local/bin/lesavka-uvc
sudo install -Dm755 "$SRC_DIR/scripts/daemon/lesavka-core.sh"       /usr/local/bin/lesavka-core.sh
sudo install -Dm755 "$SRC_DIR/scripts/daemon/lesavka-uvc.sh"        /usr/local/bin/lesavka-uvc.sh
sudo install -Dm755 "$SRC_DIR/scripts/manual/run_uac_output_sanity.sh" /usr/local/bin/lesavka-uac-sanity

echo "==> 5b. Runtime environment defaults"
sudo install -d -m 0755 /etc/lesavka
HDMI_CONNECTOR=$(resolve_hdmi_connector)
if [[ -n $HDMI_CONNECTOR ]]; then
  echo "   ↪ HDMI connector: $HDMI_CONNECTOR"
  write_hdmi_boot_force "$HDMI_CONNECTOR"
else
  echo "⚠️  no connected HDMI connector detected; leaving LESAVKA_HDMI_CONNECTOR unset." >&2
fi
{
  echo "# generated by lesavka/scripts/install/server.sh"
  echo "# Edit only for local hardware overrides; rerunning the installer refreshes defaults."
  if [[ -n $HDMI_CONNECTOR ]]; then
    printf 'LESAVKA_HDMI_CONNECTOR=%s\n' "$HDMI_CONNECTOR"
    printf 'LESAVKA_CAM_OUTPUT=%s\n' "${LESAVKA_CAM_OUTPUT:-hdmi}"
  fi
  printf 'LESAVKA_CAM_WIDTH=%s\n' "${LESAVKA_CAM_WIDTH:-1920}"
  printf 'LESAVKA_CAM_HEIGHT=%s\n' "${LESAVKA_CAM_HEIGHT:-1080}"
  printf 'LESAVKA_CAM_FPS=%s\n' "${LESAVKA_CAM_FPS:-30}"
  printf 'LESAVKA_HDMI_WIDTH=%s\n' "${LESAVKA_HDMI_WIDTH:-1920}"
  printf 'LESAVKA_HDMI_HEIGHT=%s\n' "${LESAVKA_HDMI_HEIGHT:-1080}"
  printf 'LESAVKA_HDMI_SINK=%s\n' "${LESAVKA_HDMI_SINK:-fbdevsink}"
  printf 'LESAVKA_HDMI_FBDEV=%s\n' "${LESAVKA_HDMI_FBDEV:-/dev/fb0}"
  printf 'LESAVKA_HDMI_DRIVER=%s\n' "${LESAVKA_HDMI_DRIVER:-vc4}"
  printf 'LESAVKA_HDMI_PRESENTATION_DELAY_US=%s\n' "${LESAVKA_HDMI_PRESENTATION_DELAY_US:-180000}"
  printf 'LESAVKA_UAC_DEV=%s\n' "${LESAVKA_UAC_DEV:-hw:UAC2Gadget,0}"
  printf 'LESAVKA_ALSA_DEV=%s\n' "${LESAVKA_ALSA_DEV:-hw:UAC2Gadget,0}"
  printf 'LESAVKA_UAC_HDMI_COMPENSATION_US=%s\n' "${LESAVKA_UAC_HDMI_COMPENSATION_US:-0}"
  printf 'LESAVKA_UAC_SESSION_CLOCK_ALIGN=%s\n' "${LESAVKA_UAC_SESSION_CLOCK_ALIGN:-1}"
} | sudo tee /etc/lesavka/server.env >/dev/null

echo "==> 6a. Systemd units - lesavka-core"
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-core.service >/dev/null
[Unit]
Description=lesavka USB gadget bring-up
After=sys-kernel-config.mount
Requires=sys-kernel-config.mount

[Service]
Type=oneshot
ExecStart=/usr/local/bin/lesavka-core.sh
RemainAfterExit=yes
Environment=LESAVKA_UVC_FALLBACK=0
Environment=LESAVKA_UVC_CODEC=mjpeg
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SYS_MODULE
AmbientCapabilities=CAP_SYS_MODULE
MountFlags=slave

[Install]
WantedBy=multi-user.target
UNIT

echo "==> 6b. Systemd units - lesavka-server"
cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-server.service >/dev/null
[Unit]
Description=lesavka gRPC relay
After=network.target lesavka-core.service
StartLimitIntervalSec=30
StartLimitBurst=10

[Service]
ExecStartPre=/usr/local/bin/lesavka-core.sh --attach
ExecStart=/usr/local/bin/lesavka-server
TimeoutStopSec=10
KillSignal=SIGTERM
KillMode=process
Restart=always
Environment=RUST_LOG=lesavka_server=info,lesavka_server::audio=info,lesavka_server::video=debug,lesavka_server::gadget=info
Environment=RUST_BACKTRACE=1
Environment=GST_DEBUG="*:2,alsasink:6,alsasrc:6"
Environment=LESAVKA_UVC_CODEC=mjpeg
Environment=LESAVKA_UVC_EXTERNAL=1
Environment=LESAVKA_EYE_ADAPTIVE=1
Environment=LESAVKA_EYE_MIN_FPS=12
Environment=LESAVKA_EYE_FPS=20
Environment=LESAVKA_MIC_INIT_ATTEMPTS=5
Environment=LESAVKA_MIC_INIT_DELAY_MS=250
Environment=LESAVKA_ALLOW_GADGET_CYCLE=1
EnvironmentFile=-/etc/lesavka/server.env
Restart=always
RestartSec=5
StandardError=append:/tmp/lesavka-server.stderr
User=root

[Install]
WantedBy=multi-user.target
UNIT

echo "==> 6c. Systemd units - initialization"
sudo truncate -s 0 /tmp/lesavka-server.log
sudo systemctl daemon-reload
sudo systemctl enable lesavka-core lesavka-server

UDC_STATE=$(udc_state)
if [[ -n ${LESAVKA_ALLOW_GADGET_RESET:-} ]] || ! is_attached_state "$UDC_STATE"; then
  echo "⚠️  UDC state is '$UDC_STATE' - forcing a Lesavka gadget rebuild before server start."
  sudo env \
    LESAVKA_ALLOW_GADGET_RESET=1 \
    LESAVKA_ATTACH_WRITE_UDC=1 \
    LESAVKA_DETACH_CLEAR_UDC=1 \
    LESAVKA_RELOAD_UVCVIDEO=1 \
    LESAVKA_UVC_FALLBACK=1 \
    LESAVKA_UVC_CODEC=mjpeg \
    /usr/local/bin/lesavka-core.sh
  sudo systemctl restart lesavka-core
  echo "✅  lesavka-core installed and restarted..."
else
  echo "⚠️  UDC state is '$UDC_STATE' - skipping lesavka-core restart."
  echo "    Set LESAVKA_ALLOW_GADGET_RESET=1 to force."
fi

cat <<'UNIT' | sudo tee /etc/systemd/system/lesavka-uvc.service >/dev/null
[Unit]
Description=lesavka UVC control helper
After=lesavka-core.service
Requires=lesavka-core.service
RefuseManualStop=yes

[Service]
ExecStart=/usr/local/bin/lesavka-uvc.sh
Restart=always
RestartSec=2
KillSignal=SIGTERM
KillMode=process
TimeoutStopSec=10
StandardError=append:/tmp/lesavka-uvc.stderr
User=root
EnvironmentFile=-/etc/lesavka/uvc.env

[Install]
WantedBy=multi-user.target
UNIT

sudo systemctl daemon-reload
sudo systemctl enable lesavka-uvc

echo "==> 6d. Systemd units - remove legacy reboot watchdog"
sudo systemctl stop lesavka-watchdog.timer lesavka-watchdog.service >/dev/null 2>&1 || true
sudo systemctl disable lesavka-watchdog.timer lesavka-watchdog.service >/dev/null 2>&1 || true
sudo systemctl unmask lesavka-watchdog.timer lesavka-watchdog.service >/dev/null 2>&1 || true
sudo rm -f /etc/systemd/system/lesavka-watchdog.timer \
          /etc/systemd/system/lesavka-watchdog.service \
          /usr/local/bin/lesavka-watchdog.sh \
          /etc/lesavka/watchdog.touch

sudo systemctl daemon-reload

if systemctl is-active --quiet lesavka-uvc; then
  echo "✅  lesavka-uvc is active (dependency-managed; manual restart disabled)."
else
  echo "⚠️  lesavka-uvc is not active; start via lesavka-core dependency path."
fi

sudo systemctl restart lesavka-server
echo "✅  lesavka-server installed and restarted..."
echo "➡️  Status: sudo systemctl status lesavka-server --no-pager"
echo "➡️  Logs:   sudo journalctl -u lesavka-server -f --no-pager"
