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

ASSET_BASE="https://packages.eva.st/endpoints/eva/content/kiosk"
LISTEN_ADDRESS=":80"
SERVICE_USER="eva-kiosk"
INSTALL_DIR="/opt/eva-kiosk"
CONFIG_DIR="/etc/eva-kiosk"
CONFIG_PATH="${CONFIG_DIR}/config.json"
CHROMIUM_ARGS_PATH="${CONFIG_DIR}/chromium-args"
KIOSK_URL="http://localhost/kiosk/view"
INSTALL_PACKAGES="1"
ASSET_DIR="/var/lib/eva-kiosk/install-assets"
AUDIO_DEVICE="${EVA_KIOSK_AUDIO_DEVICE:-plughw:1,0}"

usage() {
  cat <<'USAGE'
Usage: install.sh [options]

Options:
  --asset-base URL       Asset folder URL. Default: https://packages.eva.st/endpoints/eva/content/kiosk
  --listen ADDRESS      eva-kiosk listen address. Default: :80
  --user USER           Linux user used to run Chromium. Default: eva-kiosk
  --audio-device DEV    ALSA output device passed to Chromium. Default: plughw:1,0
  --no-apt              Skip apt package installation.
  -h, --help            Show this help.
USAGE
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --asset-base)
      ASSET_BASE="${2:?missing --asset-base value}"
      shift 2
      ;;
    --listen)
      LISTEN_ADDRESS="${2:?missing --listen value}"
      shift 2
      ;;
    --user)
      SERVICE_USER="${2:?missing --user value}"
      shift 2
      ;;
    --audio-device)
      AUDIO_DEVICE="${2:?missing --audio-device value}"
      shift 2
      ;;
    --no-apt)
      INSTALL_PACKAGES="0"
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      echo "Unknown option: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

if [ "$(id -u)" -ne 0 ]; then
  echo "This installer must run as root. Use sudo." >&2
  exit 1
fi

require_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "Required command not found: $1" >&2
    exit 1
  fi
}

apt_install() {
  if [ "$INSTALL_PACKAGES" != "1" ]; then
    return
  fi
  if ! command -v apt-get >/dev/null 2>&1; then
    echo "apt-get not found; skipping package installation."
    return
  fi

  export DEBIAN_FRONTEND=noninteractive
  apt-get update
  apt-get install -y --no-install-recommends \
    ca-certificates \
    curl \
    iproute2 \
    unzip \
    xserver-xorg \
    xinit \
    openbox \
    plymouth

  if ! command -v feh >/dev/null 2>&1; then
    apt-get install -y --no-install-recommends feh || true
  fi

  if ! command -v unclutter >/dev/null 2>&1 && ! command -v unclutter-xfixes >/dev/null 2>&1; then
    apt-get install -y --no-install-recommends unclutter-xfixes || \
      apt-get install -y --no-install-recommends unclutter || true
  fi

  if ! command -v chromium-browser >/dev/null 2>&1 && ! command -v chromium >/dev/null 2>&1; then
    apt-get install -y --no-install-recommends chromium-browser || \
      apt-get install -y --no-install-recommends chromium
  fi
}

detect_chromium() {
  if command -v chromium-browser >/dev/null 2>&1; then
    command -v chromium-browser
    return
  fi
  if command -v chromium >/dev/null 2>&1; then
    command -v chromium
    return
  fi
  echo "chromium"
}

current_ip() {
  ip -4 route get 1.1.1.1 2>/dev/null | awk '{for (i=1; i<=NF; i++) if ($i == "src") {print $(i+1); exit}}'
}

disable_previous_display_services() {
  local units
  units="$(
    {
      systemctl list-unit-files --type=service --no-legend 2>/dev/null | awk '{print $1}'
      systemctl list-units --type=service --all --no-legend 2>/dev/null | awk '{print $1}'
    } | grep -Ei 'anthias.*viewer|viewer.*anthias|screenly.*viewer|screenly-viewer' | sort -u || true
  )"
  if [ -z "$units" ]; then
    return
  fi

  echo "Disabling previous display executor services:"
  echo "$units" | sed 's/^/  - /'
  while IFS= read -r unit; do
    [ -n "$unit" ] || continue
    systemctl disable --now "$unit" || true
  done <<EOF
$units
EOF
}

remove_experimental_plymouth_hold() {
  rm -f /etc/systemd/system/plymouth-quit.service.d/eva-kiosk.conf
  rm -f /etc/systemd/system/plymouth-quit-wait.service.d/eva-kiosk.conf
  rmdir /etc/systemd/system/plymouth-quit.service.d 2>/dev/null || true
  rmdir /etc/systemd/system/plymouth-quit-wait.service.d 2>/dev/null || true
  rm -f "${CONFIG_DIR}/keep-plymouth-until-browser"
}

ensure_user() {
  if id "$SERVICE_USER" >/dev/null 2>&1; then
    return
  fi
  local groups=""
  for group in video audio input render; do
    if getent group "$group" >/dev/null 2>&1; then
      if [ -z "$groups" ]; then
        groups="$group"
      else
        groups="${groups},${group}"
      fi
    fi
  done
  if [ -n "$groups" ]; then
    useradd --system --create-home --shell /bin/bash --groups "$groups" "$SERVICE_USER"
  else
    useradd --system --create-home --shell /bin/bash "$SERVICE_USER"
  fi
}

install_binary() {
  mkdir -p "$INSTALL_DIR"
  local tmp
  local version
  local binary_url
  tmp="$(mktemp)"
  version="$(curl -fsSL -H 'Cache-Control: no-cache' "${ASSET_BASE}/eva-kiosk-linux-arm.txt" 2>/dev/null | tr -d '[:space:]' || true)"
  if [ -n "$version" ]; then
    binary_url="${ASSET_BASE}/eva-kiosk-linux-arm?v=${version}"
  else
    binary_url="${ASSET_BASE}/eva-kiosk-linux-arm?t=$(date +%s)"
  fi
  curl -fsSL -H 'Cache-Control: no-cache' "$binary_url" -o "$tmp"
  install -m 0755 "$tmp" "${INSTALL_DIR}/eva-kiosk"
  rm -f "$tmp"
}

install_asset_bundle() {
  if ! command -v unzip >/dev/null 2>&1; then
    echo "unzip not found; skipping bundled assets."
    return
  fi
  mkdir -p "$ASSET_DIR"
  local tmp
  tmp="$(mktemp)"
  if ! curl -fsSL "${ASSET_BASE}/eva-kiosk-raspi-assets.zip" -o "$tmp"; then
    echo "Asset bundle not available at ${ASSET_BASE}/eva-kiosk-raspi-assets.zip; continuing without bundled assets."
    rm -f "$tmp"
    return
  fi
  set +e
  unzip -o -q "$tmp" -d "$ASSET_DIR"
  local unzip_status=$?
  set -e
  rm -f "$tmp"
  if [ "$unzip_status" -eq 1 ]; then
    echo "Warning: asset bundle extracted with warnings; attempting to normalize bundled files."
  elif [ "$unzip_status" -gt 1 ]; then
    echo "Error: could not extract asset bundle; continuing without bundled assets."
    return
  fi

  # Some ZIPs produced on Windows store backslashes as literal separators.
  # Normalize the files we consume so old bundles remain installable.
  if [ -f "${ASSET_DIR}/assets\\boot.png" ] && [ ! -f "${ASSET_DIR}/assets/boot.png" ]; then
    mkdir -p "${ASSET_DIR}/assets"
    mv "${ASSET_DIR}/assets\\boot.png" "${ASSET_DIR}/assets/boot.png"
  fi
}

install_audio_config() {
  case "$AUDIO_DEVICE" in
    plughw:[0-9]*,[0-9]*|hw:[0-9]*,[0-9]*)
      local card_device="${AUDIO_DEVICE#*:}"
      local card="${card_device%%,*}"
      cat > /etc/asound.conf <<EOF
pcm.!default {
    type plug
    slave.pcm "${AUDIO_DEVICE}"
}

ctl.!default {
    type hw
    card ${card}
}
EOF
      ;;
    *)
      echo "Skipping /etc/asound.conf setup for unsupported audio device format: ${AUDIO_DEVICE}"
      ;;
  esac
}

install_config() {
  mkdir -p "$CONFIG_DIR"
  if [ ! -f "$CONFIG_PATH" ]; then
    cat > "$CONFIG_PATH" <<'JSON'
{
  "kioskId": "raspi-kiosk",
  "name": "Raspberry kiosk",
  "frameUrl": "about:blank",
  "contents": [],
  "sounds": {
    "victory": "/kiosk/assets/sounds/victory.wav",
    "alarm": "/kiosk/assets/sounds/alarm.wav",
    "mock": "/kiosk/assets/sounds/mock.wav"
  },
  "managementUrl": "",
  "chromiumArgs": [
    "--kiosk",
    "--no-first-run",
    "--disable-infobars",
    "--autoplay-policy=no-user-gesture-required",
    "--alsa-output-device=plughw:1,0",
    "--disable-session-crashed-bubble",
    "--noerrdialogs",
    "--check-for-update-interval=31536000"
  ],
  "lastModifiedAt": "1970-01-01T00:00:00Z"
}
JSON
    chmod 0600 "$CONFIG_PATH"
  fi

  cat > "$CHROMIUM_ARGS_PATH" <<EOF
--kiosk
--start-fullscreen
--window-position=0,0
--window-size=\${EVA_KIOSK_WINDOW_SIZE:-1920,1080}
--no-first-run
--disable-infobars
--autoplay-policy=no-user-gesture-required
--alsa-output-device=${AUDIO_DEVICE}
--disable-session-crashed-bubble
--noerrdialogs
--disable-gpu
--disable-dev-shm-usage
--check-for-update-interval=31536000
EOF
  chmod 0644 "$CHROMIUM_ARGS_PATH"
}

install_services() {
  local chromium_path
  chromium_path="$(detect_chromium)"

  cat > "${INSTALL_DIR}/start-browser.sh" <<EOF
#!/usr/bin/env bash
set -e

xset -dpms >/dev/null 2>&1 || true
xset s off >/dev/null 2>&1 || true
xset s noblank >/dev/null 2>&1 || true

if command -v feh >/dev/null 2>&1 && [ -f "${ASSET_DIR}/assets/boot.png" ]; then
  feh --no-fehbg --bg-center "${ASSET_DIR}/assets/boot.png" >/tmp/eva-kiosk-feh.log 2>&1 || true
fi

if command -v unclutter >/dev/null 2>&1; then
  (unclutter --timeout 0 --jitter 0 --root || unclutter -idle 0 -root) >/tmp/eva-kiosk-unclutter.log 2>&1 &
elif command -v unclutter-xfixes >/dev/null 2>&1; then
  unclutter-xfixes --timeout 0 --jitter 0 --root >/tmp/eva-kiosk-unclutter.log 2>&1 &
fi

if command -v openbox >/dev/null 2>&1; then
  openbox >/tmp/eva-kiosk-openbox.log 2>&1 &
  sleep 1
fi

mapfile -t chromium_args < <(grep -v '^[[:space:]]*$' "${CHROMIUM_ARGS_PATH}" 2>/dev/null || true)
exec "${chromium_path}" \\
  --kiosk \\
  --start-fullscreen \\
  --window-position=0,0 \\
  --window-size=\${EVA_KIOSK_WINDOW_SIZE:-1920,1080} \\
  --disable-gpu \\
  --disable-dev-shm-usage \\
  "\${chromium_args[@]}" \\
  ${KIOSK_URL}
EOF
  chmod 0755 "${INSTALL_DIR}/start-browser.sh"

  cat > /etc/systemd/system/eva-kiosk.service <<EOF
[Unit]
Description=EVA Kiosk service
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=${INSTALL_DIR}/eva-kiosk --listen=${LISTEN_ADDRESS} --config=${CONFIG_PATH} --chromium-args-path=${CHROMIUM_ARGS_PATH}
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF

  cat > /etc/systemd/system/eva-kiosk-browser.service <<EOF
[Unit]
Description=EVA Kiosk Chromium
After=eva-kiosk.service systemd-user-sessions.service
Wants=eva-kiosk.service

[Service]
User=${SERVICE_USER}
Environment=DISPLAY=:0
Environment=XAUTHORITY=/home/${SERVICE_USER}/.Xauthority
TTYPath=/dev/tty1
StandardInput=tty
StandardOutput=journal
StandardError=journal
ExecStart=/usr/bin/xinit ${INSTALL_DIR}/start-browser.sh -- :0 vt1 -nolisten tcp -nocursor
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF

  systemctl daemon-reload
  systemctl enable eva-kiosk.service
  systemctl enable eva-kiosk-browser.service
}

install_splash() {
  if ! command -v plymouth-set-default-theme >/dev/null 2>&1; then
    return
  fi

  local theme_dir="/usr/share/plymouth/themes/eva-kiosk"
  mkdir -p "$theme_dir"

  local boot_image="${ASSET_DIR}/assets/boot.png"
  if [ ! -f "$boot_image" ]; then
    echo "Boot image not available in asset bundle; skipping splash setup."
    return
  fi
  install -m 0644 "$boot_image" "${theme_dir}/boot.png"

  cat > "${theme_dir}/eva-kiosk.plymouth" <<'EOF'
[Plymouth Theme]
Name=EVA Kiosk
Description=EVA Kiosk boot screen
ModuleName=script

[script]
ImageDir=/usr/share/plymouth/themes/eva-kiosk
ScriptFile=/usr/share/plymouth/themes/eva-kiosk/eva-kiosk.script
EOF

  cat > "${theme_dir}/eva-kiosk.script" <<'EOF'
screen_width = Window.GetWidth();
screen_height = Window.GetHeight();
image = Image("boot.png");
sprite = Sprite(image);
sprite.SetX((screen_width - image.GetWidth()) / 2);
sprite.SetY((screen_height - image.GetHeight()) / 2);
EOF

  plymouth-set-default-theme -R eva-kiosk || true
}

main() {
  require_cmd curl
  apt_install
  disable_previous_display_services
  remove_experimental_plymouth_hold
  ensure_user
  install_binary
  install_asset_bundle
  install_config
  install_audio_config
  install_services
  install_splash

  systemctl restart eva-kiosk.service
  systemctl restart eva-kiosk-browser.service || true

  local ip_addr
  ip_addr="$(current_ip || true)"
  echo
  echo "EVA Kiosk installed."
  if [ -n "$ip_addr" ]; then
    echo "Admin UI: http://${ip_addr}/kiosk/admin"
    echo "Kiosk view: http://${ip_addr}/kiosk/view"
  else
    echo "Admin UI: http://<raspberry-ip>/kiosk/admin"
    echo "Kiosk view: http://<raspberry-ip>/kiosk/view"
  fi
  echo
  echo "Reboot recommended: sudo reboot"
  echo "This makes sure any previous kiosk/browser session is stopped and Chromium starts cleanly."
}

main "$@"
