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

APP_NAME="pi-car-companion"
SERVICE_USER="pi-companion"
INSTALL_ROOT="/opt/${APP_NAME}"
REPOSITORY="${INSTALL_ROOT}/repository"
RELEASES="${INSTALL_ROOT}/releases"
CURRENT="${INSTALL_ROOT}/current"
CONFIG_DIR="/etc/${APP_NAME}"
DATA_DIR="/var/lib/${APP_NAME}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

if [[ ${EUID} -ne 0 ]]; then
  if ! command -v sudo >/dev/null 2>&1; then
    echo "This installer needs root access and sudo is not installed." >&2
    exit 1
  fi
  exec sudo -- bash "${SCRIPT_DIR}/install.sh" "$@"
fi

if [[ ! -d "${SCRIPT_DIR}/.git" ]]; then
  echo "Run install.sh from a Git clone of Pi Car Companion." >&2
  exit 1
fi

if [[ ! -f /etc/os-release ]]; then
  echo "This installer supports Raspberry Pi OS and Debian." >&2
  exit 1
fi

source /etc/os-release
if [[ "${ID:-}" != "debian" && "${ID:-}" != "raspbian" && "${ID_LIKE:-}" != *debian* ]]; then
  echo "Unsupported operating system: ${PRETTY_NAME:-unknown}." >&2
  exit 1
fi

echo "[1/7] Installing operating-system packages"
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y \
  android-tools-adb \
  avahi-daemon \
  bluez \
  build-essential \
  ca-certificates \
  conntrack \
  curl \
  dnsmasq-base \
  firmware-realtek \
  git \
  gnupg \
  iproute2 \
  network-manager \
  nftables \
  python3 \
  python3-dbus \
  python3-gi \
  rfkill \
  rsync \
  sqlite3 \
  sudo \
  util-linux \
  uuid-runtime \
  wireless-regdb \
  wpasupplicant

for required_command in conntrack dnsmasq ip journalctl nmcli nft rfkill uuidgen wpa_passphrase; do
  if ! command -v "${required_command}" >/dev/null 2>&1; then
    echo "Required network command was not installed: ${required_command}" >&2
    exit 1
  fi
done

node_major=""
if command -v node >/dev/null 2>&1; then
  node_major="$(node --version | sed -E 's/^v([0-9]+).*/\1/')"
fi
if [[ "${node_major}" != "22" ]]; then
  echo "[2/7] Installing Node.js 22"
  install -d -m 0755 /etc/apt/keyrings
  key_tmp="$(mktemp)"
  curl --fail --silent --show-error --location https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key --output "${key_tmp}"
  gpg --dearmor --yes --output /etc/apt/keyrings/nodesource.gpg "${key_tmp}"
  rm -f -- "${key_tmp}"
  chmod 0644 /etc/apt/keyrings/nodesource.gpg
  echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" > /etc/apt/sources.list.d/nodesource.list
  apt-get update
  apt-get install -y nodejs
else
  echo "[2/7] Node.js 22 is already installed"
fi

echo "[3/7] Creating the service account and directories"
if ! id "${SERVICE_USER}" >/dev/null 2>&1; then
  useradd --system --home-dir "${DATA_DIR}" --shell /usr/sbin/nologin --user-group "${SERVICE_USER}"
fi
if getent group plugdev >/dev/null 2>&1; then
  usermod --append --groups plugdev "${SERVICE_USER}"
fi
install -d -m 0755 -o root -g root "${INSTALL_ROOT}" "${REPOSITORY}" "${RELEASES}"
install -d -m 0750 -o "${SERVICE_USER}" -g "${SERVICE_USER}" "${DATA_DIR}"
install -d -m 0750 -o root -g "${SERVICE_USER}" "${CONFIG_DIR}"

echo "[4/7] Copying and building the application"
if [[ "${SCRIPT_DIR}" != "${REPOSITORY}" ]]; then
  rsync -a --delete \
    --exclude node_modules \
    --exclude dist \
    --exclude .env \
    --exclude server/data \
    "${SCRIPT_DIR}/" "${REPOSITORY}/"
fi
chown -R root:root "${REPOSITORY}"
git config --system --add safe.directory "${REPOSITORY}" 2>/dev/null || true
branch="$(git -C "${REPOSITORY}" branch --show-current)"
if [[ -z "${branch}" ]]; then
  echo "The installation clone must be on a named Git branch." >&2
  exit 1
fi
printf '%s\n' "${branch}" > "${CONFIG_DIR}/update-branch"
chmod 0644 "${CONFIG_DIR}/update-branch"
update_user="${SUDO_USER:-$(stat -c '%U' "${SCRIPT_DIR}")}"
if ! id "${update_user}" >/dev/null 2>&1; then
  echo "Could not determine the local user that owns Git update credentials." >&2
  exit 1
fi
printf '%s\n' "${update_user}" > "${CONFIG_DIR}/update-user"
chmod 0600 "${CONFIG_DIR}/update-user"
chown -R "${update_user}":"$(id -gn "${update_user}")" "${REPOSITORY}/.git"

cd "${REPOSITORY}"
npm ci
npm run check
revision="$(git rev-parse HEAD)"
release="${RELEASES}/${revision}"
install -d -m 0755 -o root -g root "${release}"
rsync -a --delete --exclude .git "${REPOSITORY}/" "${release}/"
printf '%s\n' "${revision}" > "${release}/REVISION"
chown -R root:root "${release}"
ln -sfn "releases/${revision}" "${INSTALL_ROOT}/current.next"
mv -Tf "${INSTALL_ROOT}/current.next" "${CURRENT}"

echo "[5/7] Installing system services"
install -m 0755 "${SCRIPT_DIR}/scripts/pi-car-companion-network-setup" /usr/local/sbin/pi-car-companion-network-setup
install -m 0755 "${SCRIPT_DIR}/scripts/pi-car-companion-power" /usr/local/sbin/pi-car-companion-power
install -m 0755 "${SCRIPT_DIR}/scripts/pi-car-companion-bluetooth-control" /usr/local/sbin/pi-car-companion-bluetooth-control
install -m 0755 "${SCRIPT_DIR}/scripts/pi-car-companion-update" /usr/local/sbin/pi-car-companion-update
install -d -m 0755 /usr/local/libexec
install -m 0755 "${SCRIPT_DIR}/scripts/pi-car-companion-bluetooth" /usr/local/libexec/pi-car-companion-bluetooth
install -m 0644 "${SCRIPT_DIR}/systemd/pi-car-companion.service" /etc/systemd/system/pi-car-companion.service
install -m 0644 "${SCRIPT_DIR}/systemd/pi-car-companion-update-check.service" /etc/systemd/system/pi-car-companion-update-check.service
install -m 0644 "${SCRIPT_DIR}/systemd/pi-car-companion-update.service" /etc/systemd/system/pi-car-companion-update.service
install -m 0644 "${SCRIPT_DIR}/systemd/pi-car-companion-network.service" /etc/systemd/system/pi-car-companion-network.service
install -m 0644 "${SCRIPT_DIR}/systemd/pi-car-companion-network-configure.service" /etc/systemd/system/pi-car-companion-network-configure.service
install -m 0644 "${SCRIPT_DIR}/systemd/pi-car-companion-network-observer.service" /etc/systemd/system/pi-car-companion-network-observer.service
install -m 0644 "${SCRIPT_DIR}/systemd/pi-car-companion-bluetooth.service" /etc/systemd/system/pi-car-companion-bluetooth.service
install -m 0440 "${SCRIPT_DIR}/systemd/pi-car-companion.sudoers" /etc/sudoers.d/pi-car-companion
visudo -cf /etc/sudoers.d/pi-car-companion >/dev/null
install -d -m 0755 /etc/avahi/services
install -m 0644 "${SCRIPT_DIR}/config/pi-car-companion.service.xml" /etc/avahi/services/pi-car-companion.service
install -d -m 0755 /etc/NetworkManager/dnsmasq-shared.d
install -m 0644 "${SCRIPT_DIR}/config/dnsmasq-shared-pi-car-companion.conf" /etc/NetworkManager/dnsmasq-shared.d/pi-car-companion.conf

if [[ ! -f "${CONFIG_DIR}/companion.env" ]]; then
  cat > "${CONFIG_DIR}/companion.env" <<EOF
NODE_ENV=production
HOST=0.0.0.0
PORT=8787
DATABASE_PATH=${DATA_DIR}/companion.db
SESSION_TTL_HOURS=24
COOKIE_SECURE=false
ALLOWED_ORIGINS=http://mg4pi.local:8787
UPDATE_STATUS_PATH=${DATA_DIR}/update-status.json
VERSION_FILE=${CURRENT}/REVISION
ADB_ENABLED=false
ADB_PATH=adb
ADB_SERIAL=
ADB_TIMEOUT_MS=3000
NETWORK_MANAGER_ENABLED=false
NETWORK_STATUS_PATH=${DATA_DIR}/network-status.json
NETWORK_COMMAND_PATH=${DATA_DIR}/network-command.json
NETWORK_CONFIGURATION_REQUEST_PATH=${DATA_DIR}/network-configuration-request.json
NETWORK_ACTIVITY_PATH=${DATA_DIR}/network-activity.json
NETWORK_HOTSPOT_INTERFACE=wlan0
NETWORK_UPSTREAM_INTERFACE=wlan1
NETWORK_HOTSPOT_CONNECTION=pi-car-hotspot
NETWORK_PROBE_TIMEOUT_SECONDS=60
NETWORK_STABILITY_SECONDS=10
NETWORK_POLL_SECONDS=5
EOF
fi

ensure_env_default() {
  local key="$1"
  local value="$2"
  if ! grep -q "^${key}=" "${CONFIG_DIR}/companion.env"; then
    printf '%s=%s\n' "${key}" "${value}" >> "${CONFIG_DIR}/companion.env"
  fi
}
ensure_env_default NETWORK_MANAGER_ENABLED false
ensure_env_default NETWORK_STATUS_PATH "${DATA_DIR}/network-status.json"
ensure_env_default NETWORK_COMMAND_PATH "${DATA_DIR}/network-command.json"
ensure_env_default NETWORK_CONFIGURATION_REQUEST_PATH "${DATA_DIR}/network-configuration-request.json"
ensure_env_default NETWORK_ACTIVITY_PATH "${DATA_DIR}/network-activity.json"
ensure_env_default NETWORK_HOTSPOT_INTERFACE wlan0
ensure_env_default NETWORK_UPSTREAM_INTERFACE wlan1
ensure_env_default NETWORK_HOTSPOT_CONNECTION pi-car-hotspot
ensure_env_default NETWORK_PROBE_TIMEOUT_SECONDS 60
ensure_env_default NETWORK_STABILITY_SECONDS 10
ensure_env_default NETWORK_POLL_SECONDS 5
chown root:"${SERVICE_USER}" "${CONFIG_DIR}/companion.env"
chmod 0640 "${CONFIG_DIR}/companion.env"

if [[ ! -f "${DATA_DIR}/update-status.json" ]]; then
  printf '{"state":"idle","message":"Ready to check for updates.","fromRevision":null,"toRevision":null,"updatedAt":"%s"}\n' "$(date --iso-8601=seconds)" > "${DATA_DIR}/update-status.json"
fi
chown root:"${SERVICE_USER}" "${DATA_DIR}/update-status.json"
chmod 0644 "${DATA_DIR}/update-status.json"

echo "[6/7] Enabling start-on-boot services"
systemctl daemon-reload
systemctl enable --now avahi-daemon
systemctl enable pi-car-companion.service
systemctl enable --now bluetooth.service
systemctl enable pi-car-companion-bluetooth.service
systemctl enable pi-car-companion-network-observer.service
if grep -qx 'NETWORK_MANAGER_ENABLED=true' "${CONFIG_DIR}/companion.env"; then
  systemctl enable --now pi-car-companion-network.service
fi
systemctl restart pi-car-companion.service
systemctl restart pi-car-companion-bluetooth.service
systemctl restart pi-car-companion-network-observer.service

echo "[7/7] Verifying the service"
for _attempt in {1..20}; do
  if curl --fail --silent --show-error http://127.0.0.1:8787/healthz >/dev/null; then
    break
  fi
  sleep 1
done
if ! curl --fail --silent --show-error http://127.0.0.1:8787/healthz >/dev/null; then
  systemctl status --no-pager pi-car-companion.service || true
  echo "Installation finished, but the health check failed. Review the service status above." >&2
  exit 1
fi

host_name="$(hostname)"
ip_addresses="$(hostname -I 2>/dev/null | xargs || true)"
echo
echo "Pi Car Companion is installed and running."
echo "Dashboard: http://${host_name}.local:8787"
if [[ -n "${ip_addresses}" ]]; then
  echo "Pi addresses: ${ip_addresses} (port 8787)"
fi
echo "Future updates can be installed from Settings, or by running git pull and install.sh again."
