#!/usr/bin/env bash
set -euo pipefail

BIN_NAME="kraken-player"
DATA_DIR="/var/lib/kraken-player"
# Binary lives UNDER the data dir (owned by the service user) so the player
# can self-update by renaming it in place - a root-owned /usr/local/bin
# would deny the rename to User=pi. ExecStart in the unit matches this path.
INSTALL_DIR="$DATA_DIR/bin"
# Default the service user to whoever invoked sudo (the real box/desktop user),
# falling back to `pi` for classic Pi images. The unit ships with User=pi
# hardcoded, so on a box with no `pi` account the service would never start; the
# unit's User= is rewritten to this value below.
SERVICE_USER="${SERVICE_USER:-${SUDO_USER:-pi}}"
# raspberrypi vs generic linux, for a truthful platform/health tag on an x86 box.
case "$(uname -m 2>/dev/null)" in
  aarch64 | armv7l | armv6l) PLATFORM_TAG="raspberrypi" ;;
  *) PLATFORM_TAG="linux" ;;
esac
SERVICE_FILE="/etc/systemd/system/kraken-player.service"
CONFIG_PATH="$DATA_DIR/config.json"
PACKAGE_CONFIG="./config.json"
DEFAULT_API_BASE_URL="https://api.dev.whitecore.ai"

escape_json() {
  printf '%s' "$1" | sed 's/\\/\\\\/g; s/\"/\\\"/g'
}

write_config() {
  local api_base_url
  local device_id
  local device_name
  local allow_system

  api_base_url="$(escape_json "${1:-$DEFAULT_API_BASE_URL}")"
  device_id="$(escape_json "${2:-}")"
  device_name="$(escape_json "${3:-}")"
  allow_system="${4:-false}"

  sudo tee "$CONFIG_PATH" >/dev/null <<EOF
{
  "apiBaseUrl": "$api_base_url",
  "deviceId": "$device_id",
  "deviceName": "$device_name",
  "platform": "$PLATFORM_TAG",
  "cacheDir": "/var/lib/kraken-player/cache",
  "renderer": "chromium",
  "chromiumPath": "",
  "heartbeatIntervalSeconds": 30,
  "playlistIntervalSeconds": 20,
  "commandIntervalSeconds": 10,
  "healthIntervalSeconds": 60,
  "allowSystemCommands": $allow_system
}
EOF

  sudo chmod 600 "$CONFIG_PATH"
}

# Keep a customer's device name, cache and local settings on upgrade, but never
# preserve an API environment from a different package. A DEV-configured player
# installed over with an APP1 package used to keep sending its six-digit code to
# DEV, so APP1 correctly answered "Pairing code not found". When the packaged
# endpoint changes, migrate only that endpoint and discard environment-scoped
# credentials; the player immediately requests a fresh code from the new API.
migrate_packaged_api() {
  [[ -f "$CONFIG_PATH" && -f "$PACKAGE_CONFIG" ]] || return 0
  if ! command -v python3 >/dev/null 2>&1; then
    echo "WARNING: python3 is required to migrate an existing player environment." >&2
    echo "Re-run with FORCE_CONFIG=1 to replace the config from this package." >&2
    return 0
  fi

  local current_api package_api
  current_api="$(sudo python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("apiBaseUrl", ""))' "$CONFIG_PATH" 2>/dev/null || true)"
  package_api="$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("apiBaseUrl", ""))' "$PACKAGE_CONFIG" 2>/dev/null || true)"
  [[ -n "$package_api" && "$package_api" != "$current_api" ]] || return 0

  sudo python3 - "$CONFIG_PATH" "$package_api" <<'PY'
import json
import os
import sys

path, api_base_url = sys.argv[1], sys.argv[2]
with open(path, "r", encoding="utf-8") as handle:
    config = json.load(handle)

config["apiBaseUrl"] = api_base_url
for key in ("token", "tokenExpiresAt", "pairingCode", "pairingExpiresAt", "pairingSecret"):
    config[key] = ""

temporary = path + ".environment-migrate"
with open(temporary, "w", encoding="utf-8") as handle:
    json.dump(config, handle, indent=2)
    handle.write("\n")
os.chmod(temporary, 0o600)
os.replace(temporary, path)
PY
  echo "Player environment migrated: ${current_api:-unset} -> $package_api"
  echo "Old pairing credentials cleared; a fresh code will be generated."
}

if [[ ! -f "./$BIN_NAME" ]]; then
  echo "Binary ./$BIN_NAME not found. Build it first."
  exit 1
fi

sudo mkdir -p "$DATA_DIR"
sudo mkdir -p "$INSTALL_DIR"

# ffmpeg is used to grab camera frames for Vision AI calibration/preview.
# Best-effort on Debian/Pi OS; the player degrades gracefully if absent.
if ! command -v ffmpeg >/dev/null 2>&1; then
  if command -v apt-get >/dev/null 2>&1; then
    echo "Installing ffmpeg (for camera snapshots)..."
    sudo apt-get update -y >/dev/null 2>&1 || true
    sudo apt-get install -y ffmpeg >/dev/null 2>&1 || echo "  (ffmpeg install skipped - install manually for camera frames)"
  fi
fi

sudo cp "./$BIN_NAME" "$INSTALL_DIR/$BIN_NAME"

# Bundle the ONNX Vision AI detector next to the binary (detect_onnx.py + model).
# Replace, don't nest: a plain `cp -r ./vision $INSTALL_DIR/vision` on a re-install
# would copy INTO the existing dir (vision/vision) and stop refreshing the detector.
if [[ -d ./vision ]]; then
  sudo rm -rf "$INSTALL_DIR/vision"
  sudo cp -r ./vision "$INSTALL_DIR/vision"
fi

# v4l-utils gives friendly USB-camera names (v4l2-ctl --list-devices); best-effort.
if command -v apt-get >/dev/null 2>&1 && ! command -v v4l2-ctl >/dev/null 2>&1; then
  sudo apt-get install -y v4l-utils >/dev/null 2>&1 || true
fi

# On-device people counting runs the ONNX detector (onnxruntime, NO torch) from a
# venv. The venv is REQUIRED on modern distros (Debian 12 / Ubuntu 23.04+) where a
# system-wide `pip install` fails with "externally-managed-environment" (PEP 668),
# and keeps deps off the system python everywhere else. The player auto-discovers
# this venv next to the detector (vision/venv). Best-effort: counting just idles if
# the deps can't build.
VISION_DIR="$INSTALL_DIR/vision"
if [[ -f "$VISION_DIR/requirements.txt" ]]; then
  if ! command -v python3 >/dev/null 2>&1 && command -v apt-get >/dev/null 2>&1; then
    sudo apt-get install -y python3 python3-venv python3-pip >/dev/null 2>&1 || true
  fi
  if command -v python3 >/dev/null 2>&1; then
    echo "Setting up the Vision AI detector (onnxruntime venv)..."
    if ! sudo python3 -m venv "$VISION_DIR/venv" >/dev/null 2>&1; then
      # `python3 -m venv` needs the python3-venv package on Debian/Ubuntu.
      command -v apt-get >/dev/null 2>&1 && sudo apt-get install -y python3-venv >/dev/null 2>&1 || true
      sudo python3 -m venv "$VISION_DIR/venv" >/dev/null 2>&1 || true
    fi
    if [[ -x "$VISION_DIR/venv/bin/pip" ]]; then
      sudo "$VISION_DIR/venv/bin/pip" install --quiet --upgrade pip >/dev/null 2>&1 || true
      sudo "$VISION_DIR/venv/bin/pip" install --quiet -r "$VISION_DIR/requirements.txt" >/dev/null 2>&1 \
        || echo "  (detector deps failed - Vision AI counting stays off until they install)"
      # Demographics (WHITECORE_DEMOGRAPHICS=1) also needs insightface; best-effort.
      sudo "$VISION_DIR/venv/bin/pip" install --quiet insightface >/dev/null 2>&1 || true
    else
      echo "  (could not create a venv - install python3-venv, then re-run)"
    fi
  fi
fi

# The service user must own the binary dir so auto-update can rename the
# binary in place (rename needs write+exec on the PARENT directory).
sudo chown -R "$SERVICE_USER":"$SERVICE_USER" "$DATA_DIR" 2>/dev/null || true
sudo chmod 755 "$INSTALL_DIR/$BIN_NAME"

if [[ -f "$CONFIG_PATH" && "${FORCE_CONFIG:-}" != "1" ]]; then
  migrate_packaged_api
  echo "Existing player settings preserved at $CONFIG_PATH."
else
  if [[ -n "${API_BASE_URL:-}" || -n "${DEVICE_ID:-}" || -n "${DEVICE_NAME:-}" || -n "${ALLOW_SYSTEM_COMMANDS:-}" ]]; then
    write_config "${API_BASE_URL:-$DEFAULT_API_BASE_URL}" "${DEVICE_ID:-}" "${DEVICE_NAME:-}" "${ALLOW_SYSTEM_COMMANDS:-false}"
  elif [[ -f "$PACKAGE_CONFIG" ]]; then
    sudo cp "$PACKAGE_CONFIG" "$CONFIG_PATH"
    sudo chmod 600 "$CONFIG_PATH"
  else
    write_config "$DEFAULT_API_BASE_URL" "" "" "false"
  fi
fi

# The config is written root-owned mode 600 here, AFTER the chown -R above, so the
# service user (which runs the player and must READ the config AND save its paired
# identity back to it) was locked out -> "config error: permission denied" -> the
# service crash-looped on every fresh box. Hand the config to the service user.
sudo chown "$SERVICE_USER":"$SERVICE_USER" "$CONFIG_PATH" 2>/dev/null || true

# Install the unit with the REAL service user. The shipped unit hardcodes
# `User=pi`, so on a box with no `pi` account systemd fails to start the service.
# The package ships the unit FLAT as ./kraken-player.service; from a repo checkout
# it lives in deploy/. Resolve whichever exists (running the documented package
# flow from a dir with no deploy/ used to abort here and truncate the unit file).
SVC_SRC="kraken-player.service"
[[ -f "$SVC_SRC" ]] || SVC_SRC="deploy/kraken-player.service"
sudo sed "s/^User=.*/User=$SERVICE_USER/" "$SVC_SRC" | sudo tee "$SERVICE_FILE" >/dev/null

# Passwordless sudo for EXACTLY the commands the player shells out to, so
# SET_TIMEZONE / SYNC_TIME / REBOOT_DEVICE / RESTART_PLAYER work for the non-root
# service user (they call `sudo reboot`, `sudo systemctl restart kraken-player`,
# `sudo timedatectl ...`). Scoped to specific binaries and validated with visudo.
SUDOERS_FILE="/etc/sudoers.d/kraken-player"
sudo tee "$SUDOERS_FILE" >/dev/null <<EOF
$SERVICE_USER ALL=(root) NOPASSWD: /usr/sbin/reboot, /sbin/reboot, /usr/bin/systemctl restart kraken-player, /bin/systemctl restart kraken-player, /usr/bin/timedatectl, /bin/timedatectl
EOF
sudo chmod 440 "$SUDOERS_FILE"
sudo visudo -cf "$SUDOERS_FILE" >/dev/null 2>&1 || { echo "  (sudoers drop-in invalid; removing)"; sudo rm -f "$SUDOERS_FILE"; }

sudo systemctl daemon-reload
# In appliance/kiosk mode the player runs from the X session (appliance-pi.sh
# disables this service). Re-enabling it here on a re-run would start a SECOND,
# DISPLAY-less instance that double-reports plays, so skip when the marker exists.
if [[ "$PLATFORM_TAG" == "raspberrypi" && ! -f /var/lib/kraken-player/appliance.mode ]]; then
  # A Raspberry Pi needs the X kiosk session configured by appliance-pi.sh.
  # Starting this service now gives it no DISPLAY and, on a clean OS, happens
  # before Chromium is installed. That previously persisted a stale
  # `chromium-browser` path and produced a black screen after reboot.
  sudo systemctl disable kraken-player >/dev/null 2>&1 || true
  sudo systemctl stop kraken-player >/dev/null 2>&1 || true
  echo "Raspberry Pi files installed. Now run: sudo bash appliance-pi.sh"
elif [[ -f /var/lib/kraken-player/appliance.mode ]]; then
  echo "Appliance mode detected (/var/lib/kraken-player/appliance.mode) - NOT enabling the systemd service."
  echo "The kiosk runs the player from its X session; re-run scripts/appliance-pi.sh if you changed the setup."
else
  sudo systemctl enable kraken-player
  sudo systemctl restart kraken-player
fi

echo "Kraken Player installed and started."
