#!/bin/sh
set -eu

BASE_URL="${LEARNME_BASE_URL:-https://learnme.info/remarkable}"
MANIFEST_URL="${LEARNME_MANIFEST_URL:-$BASE_URL/manifest.json}"
ROOT_DIR="${LEARNME_ROOT_DIR:-/home/root/learnme}"
AGENT_DIR="$ROOT_DIR/agent"
RUNTIME_DIR="$ROOT_DIR/xochitl-runtime"
DATA_DIR="$ROOT_DIR/data"
SYSTEM_UNIT_DIR="${LEARNME_SYSTEM_UNIT_DIR:-/usr/lib/systemd/system}"
AGENT_SERVICE_FILE="$AGENT_DIR/learnme-agent.service"
XOCHITL_DROPIN_FILE="$AGENT_DIR/xochitl-runtime.conf"
AGENT_HOST="${LEARNME_AGENT_HOST:-127.0.0.1}"
AGENT_PORT="${LEARNME_AGENT_PORT:-8765}"
INSTALL_RESOURCES=1
TESTED_MODEL="reMarkable Ferrari"
TESTED_PRODUCT="reMarkable Paper Pro"
TESTED_OS_VERSION="3.27.3.0"

for arg in "$@"; do
  case "$arg" in
    --no-resources) INSTALL_RESOURCES=0 ;;
    --with-resources) INSTALL_RESOURCES=1 ;;
    --help|-h)
      echo "Usage: sh install.sh [--no-resources]"
      exit 0
      ;;
    *)
      echo "Unknown option: $arg" >&2
      exit 64
      ;;
  esac
done

log() {
  echo "[LearnMe] $*"
}

need_root() {
  if [ "$(id -u)" != "0" ]; then
    echo "Run this installer as root on the reMarkable tablet." >&2
    exit 1
  fi
}

read_device_model() {
  for path in /sys/firmware/devicetree/base/model /proc/device-tree/model /sys/devices/soc0/machine; do
    if [ -r "$path" ]; then
      tr -d '\000' < "$path"
      return
    fi
  done
  printf '%s' unknown
}

read_os_version() {
  if [ -r /etc/os-release ]; then
    value="$(sed -n 's/^IMG_VERSION=["'\'']\{0,1\}\([^"'\'']*\)["'\'']\{0,1\}$/\1/p' /etc/os-release | sed -n '1p')"
    if [ -z "$value" ]; then
      value="$(sed -n 's/^VERSION_ID=["'\'']\{0,1\}\([^"'\'']*\)["'\'']\{0,1\}$/\1/p' /etc/os-release | sed -n '1p')"
    fi
    if [ -n "$value" ]; then
      printf '%s' "$value"
      return
    fi
  fi
  printf '%s' unknown
}

check_compatibility() {
  architecture="$(uname -m)"
  case "$architecture" in
    aarch64|arm64) ;;
    *)
      echo "LearnMe requires a remarkable-aarch64 device; detected $architecture." >&2
      exit 1
      ;;
  esac
  if ! command -v systemctl >/dev/null 2>&1 ||
     ! systemctl cat xochitl.service >/dev/null 2>&1; then
    echo "LearnMe requires a reMarkable system with the Xochitl systemd service." >&2
    exit 1
  fi

  device_model="$(read_device_model)"
  os_version="$(read_os_version)"
  if [ "$device_model" != "$TESTED_MODEL" ] || [ "$os_version" != "$TESTED_OS_VERSION" ]; then
    log "Compatibility note: tested on $TESTED_PRODUCT ($TESTED_MODEL) with OS $TESTED_OS_VERSION."
    log "Continuing on detected model '$device_model', OS '$os_version'; automatic rollback is enabled."
  fi
}

fetch() {
  url="$1"
  output="$2"
  if command -v wget >/dev/null 2>&1; then
    wget -qO "$output" "$url"
  elif command -v curl >/dev/null 2>&1; then
    curl -fsSL "$url" -o "$output"
  else
    echo "Need wget or curl." >&2
    exit 1
  fi
}

post_json() {
  url="$1"
  body="$2"
  if command -v wget >/dev/null 2>&1; then
    wget -qO- --header "Content-Type: application/json" --post-data "$body" "$url"
  elif command -v curl >/dev/null 2>&1; then
    curl -fsSL -H "Content-Type: application/json" -d "$body" "$url"
  else
    echo "Need wget or curl." >&2
    exit 1
  fi
}

sha256_of() {
  path="$1"
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$path" | awk '{print $1}'
  elif command -v openssl >/dev/null 2>&1; then
    openssl dgst -sha256 "$path" | awk '{print $NF}'
  else
    echo "Need sha256sum or openssl." >&2
    exit 1
  fi
}

verify_sha256() {
  path="$1"
  expected="$2"
  actual="$(sha256_of "$path")"
  if [ "$actual" != "$expected" ]; then
    echo "SHA256 mismatch for $path" >&2
    echo "expected: $expected" >&2
    echo "actual:   $actual" >&2
    exit 1
  fi
}

json_value_from_block() {
  block="$1"
  key="$2"
  printf '%s\n' "$block" |
    sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" |
    sed -n '1p'
}

component_block() {
  name="$1"
  sed -n "/\"$name\"[[:space:]]*:/,/^[[:space:]]*}/p" "$MANIFEST_PATH"
}

install_file() {
  src="$1"
  dst="$2"
  mode="$3"
  mkdir -p "$(dirname "$dst")"
  if [ -f "$dst" ]; then
    cp "$dst" "$dst.bak"
  fi
  mv "$src" "$dst"
  chmod "$mode" "$dst"
}

write_agent_service() {
  cat > "$AGENT_SERVICE_FILE" <<EOF
[Unit]
Description=LearnMe local agent
After=network-online.target
RequiresMountsFor=$ROOT_DIR

[Service]
Type=simple
Environment="LEARNME_AGENT_HOST=$AGENT_HOST"
Environment="LEARNME_AGENT_PORT=$AGENT_PORT"
Environment="LEARNME_UPDATE_MANIFEST_URL=$MANIFEST_URL"
ExecStart=$AGENT_DIR/learnme-agent
Restart=on-failure
RestartSec=2
TimeoutStopSec=5

[Install]
WantedBy=multi-user.target
EOF
}

write_xochitl_dropin() {
  cat > "$XOCHITL_DROPIN_FILE" <<EOF
[Unit]
Wants=learnme-xochitl-guard.service
After=learnme-xochitl-guard.service

[Service]
Environment="LD_PRELOAD=$RUNTIME_DIR/learnme-xochitl-runtime.so"
Environment="QML_DISABLE_DISK_CACHE=1"
Environment="MALLOC_ARENA_MAX=8"
RestartSec=3
EOF
}

install_systemd_links() {
  log "Installing persistent systemd units"
  mount -o remount,rw /
  ok=1
  mkdir -p "$SYSTEM_UNIT_DIR/xochitl.service.d" "$SYSTEM_UNIT_DIR/multi-user.target.wants" || ok=0
  rm -f "$SYSTEM_UNIT_DIR/learnme-agent.service" \
    "$SYSTEM_UNIT_DIR/learnme-xochitl-guard.service" \
    "$SYSTEM_UNIT_DIR/xochitl.service.d/90-learnme-xochitl-runtime.conf"
  cp "$AGENT_SERVICE_FILE" "$SYSTEM_UNIT_DIR/learnme-agent.service" || ok=0
  cat > "$SYSTEM_UNIT_DIR/learnme-xochitl-guard.service" <<EOF || ok=0
[Unit]
Description=LearnMe Xochitl runtime health guard
Before=xochitl.service
RequiresMountsFor=$AGENT_DIR

[Service]
Type=simple
ExecStart=$AGENT_DIR/learnme-xochitl-guard.sh
EOF
  cp "$XOCHITL_DROPIN_FILE" \
    "$SYSTEM_UNIT_DIR/xochitl.service.d/90-learnme-xochitl-runtime.conf" || ok=0
  ln -sfn "$SYSTEM_UNIT_DIR/learnme-agent.service" \
    "$SYSTEM_UNIT_DIR/multi-user.target.wants/learnme-agent.service" || ok=0
  sync
  mount -o remount,ro / || true
  if [ "$ok" != "1" ]; then
    echo "Could not install persistent systemd units." >&2
    exit 1
  fi
}

remove_persistent_xochitl_dropin() {
  mount -o remount,rw /
  rm -f "$SYSTEM_UNIT_DIR/xochitl.service.d/90-learnme-xochitl-runtime.conf"
  sync
  mount -o remount,ro / || true
}

enable_glyph_selection() {
  conf="/home/root/.config/remarkable/xochitl.conf"
  tmp="$conf.learnme.tmp"
  mkdir -p "$(dirname "$conf")"
  if [ ! -f "$conf" ]; then
    printf '[Experimental]\nGlyphSelection=true\n' > "$conf"
    return
  fi
  awk '
    BEGIN { in_exp=0; seen_exp=0; seen_glyph=0 }
    /^\[.*\]$/ {
      if (in_exp && !seen_glyph) {
        print "GlyphSelection=true"
      }
      in_exp=0
      seen_glyph=0
    }
    /^\[Experimental\]$/ {
      seen_exp=1
      in_exp=1
      print
      next
    }
    in_exp && /^GlyphSelection=/ {
      print "GlyphSelection=true"
      seen_glyph=1
      next
    }
    { print }
    END {
      if (in_exp && !seen_glyph) {
        print "GlyphSelection=true"
      }
      if (!seen_exp) {
        print ""
        print "[Experimental]"
        print "GlyphSelection=true"
      }
    }
  ' "$conf" > "$tmp"
  mv "$tmp" "$conf"
}

wait_for_agent() {
  url="http://$AGENT_HOST:$AGENT_PORT/health"
  i=0
  while [ "$i" -lt 20 ]; do
    if command -v wget >/dev/null 2>&1; then
      if wget -qO- "$url" 2>/dev/null | grep -q '"ok":true'; then
        return 0
      fi
    elif command -v curl >/dev/null 2>&1; then
      if curl -fsSL "$url" 2>/dev/null | grep -q '"ok":true'; then
        return 0
      fi
    fi
    i=$((i + 1))
    sleep 1
  done
  echo "LearnMe agent did not become healthy." >&2
  return 1
}

install_resource() {
  id="$1"
  log "Installing resource: $id"
  if ! post_json "http://$AGENT_HOST:$AGENT_PORT/resources/install" "{\"id\":\"$id\"}" | grep -q '"ok":true'; then
    echo "Could not install resource $id. You can retry from LearnMe Settings." >&2
    return 1
  fi
}

need_root
check_compatibility

TMP_DIR="${TMPDIR:-/tmp}/learnme-install.$$"
MANIFEST_PATH="$TMP_DIR/manifest.json"
mkdir -p "$TMP_DIR"
trap 'rm -rf "$TMP_DIR"' EXIT

log "Downloading manifest"
fetch "$MANIFEST_URL" "$MANIFEST_PATH"

agent_block="$(component_block agent)"
runtime_block="$(component_block xochitlRuntime)"
agent_url="$(json_value_from_block "$agent_block" url)"
agent_sha="$(json_value_from_block "$agent_block" sha256)"
runtime_url="$(json_value_from_block "$runtime_block" url)"
runtime_sha="$(json_value_from_block "$runtime_block" sha256)"
runtime_version="$(json_value_from_block "$runtime_block" version)"

if [ -z "$agent_url" ] || [ -z "$agent_sha" ] || [ -z "$runtime_url" ] || [ -z "$runtime_sha" ] || [ -z "$runtime_version" ]; then
  echo "Could not read component metadata from $MANIFEST_URL" >&2
  exit 1
fi

mkdir -p "$AGENT_DIR" "$RUNTIME_DIR" "$DATA_DIR/dictionaries" "$DATA_DIR/morphology" "$DATA_DIR/phrases"

log "Downloading agent"
fetch "$agent_url" "$TMP_DIR/learnme-agent"
verify_sha256 "$TMP_DIR/learnme-agent" "$agent_sha"

log "Downloading Xochitl runtime"
fetch "$runtime_url" "$TMP_DIR/learnme-xochitl-runtime.so"
verify_sha256 "$TMP_DIR/learnme-xochitl-runtime.so" "$runtime_sha"

log "Installing files"
systemctl stop learnme-agent.service >/dev/null 2>&1 || true
install_file "$TMP_DIR/learnme-agent" "$AGENT_DIR/learnme-agent" 0755
install_file "$TMP_DIR/learnme-xochitl-runtime.so" "$RUNTIME_DIR/learnme-xochitl-runtime.so" 0644
printf '%s\n' "$runtime_version" > "$RUNTIME_DIR/version"
rm -f "$RUNTIME_DIR/disabled" "$RUNTIME_DIR/status.json"

log "Writing services"
write_agent_service
write_xochitl_dropin
install_systemd_links
enable_glyph_selection
systemctl daemon-reload
systemctl restart learnme-agent.service
wait_for_agent

log "Restarting Xochitl"
systemctl stop learnme-xochitl-guard.service >/dev/null 2>&1 || true
systemctl reset-failed xochitl.service >/dev/null 2>&1 || true
systemctl restart xochitl.service >/dev/null 2>&1 || true
if ! systemctl start learnme-xochitl-guard.service; then
  log "Runtime guard failed to start; restoring stock Xochitl"
  printf '%s\n' "Runtime disabled because its health guard could not start." > "$RUNTIME_DIR/disabled"
  remove_persistent_xochitl_dropin
  systemctl daemon-reload
  systemctl reset-failed xochitl.service >/dev/null 2>&1 || true
  systemctl restart xochitl.service || true
  exit 1
fi
if [ -f "$RUNTIME_DIR/disabled" ]; then
  echo "LearnMe runtime was not compatible with this Xochitl build; stock Xochitl was restored." >&2
  exit 1
fi

if [ "$INSTALL_RESOURCES" = "1" ]; then
  install_resource en-ru || true
  install_resource en-morphology || true
fi

log "Installed. Select text in a document and use LearnMe from the selection menu."
