#!/usr/bin/env bash
#
# auto-cpufreq daemon install script
# reference: https://codeberg.org/Zamanhuseyinli/auto-cpufreq-rust
# Thanks to https://github.com/errornonamer for openrc fix
export CURL_HTTP1_1=1
set -euo pipefail

MID="$((`tput cols` / 2))"
echo
printf "%0.s─" $(seq $(( (MID-2) / 2 )))
printf " Running auto-cpufreq daemon install script "
printf "%0.s─" $(seq $(( (MID-2) / 2 )))
echo; echo

# root check
if ((EUID != 0)); then
  echo; echo "Must be run as root (i.e: 'sudo $0')."; echo
  exit 1
fi

if ! command -v tar >/dev/null 2>&1; then
  echo; echo "Error: 'tar' is required but not installed."; echo
  exit 1
fi

# ---------------------------------------------------------------------------
# Config
# ---------------------------------------------------------------------------
REPO="Zamanhuseyinli/auto-cpufreq-rust"
BASE_URL="https://codeberg.org/${REPO}/releases/download"
API_URL="https://codeberg.org/api/v1/repos/${REPO}/releases/latest"

echo -e "\n* Checking latest release version"
if command -v curl >/dev/null 2>&1; then
  VERSION="$(curl -fsSL "$API_URL" | grep -o '"tag_name":"[^"]*"' | head -n1 | cut -d'"' -f4)"
elif command -v wget >/dev/null 2>&1; then
  VERSION="$(wget -qO- "$API_URL" | grep -o '"tag_name":"[^"]*"' | head -n1 | cut -d'"' -f4)"
else
  echo "Error: need curl or wget installed."; exit 1
fi

if [ -z "${VERSION:-}" ]; then
  echo "Error: could not determine latest release version from ${API_URL}"
  exit 1
fi
echo "  -> latest version: ${VERSION}"

fetch() {
  local url="$1" dest="$2"
  if command -v curl >/dev/null 2>&1; then
    curl -fL --http1.1 --retry 3 --retry-delay 2 --progress-bar -o "$dest" "$url"
  elif command -v wget >/dev/null 2>&1; then
    wget -q --show-progress -O "$dest" "$url"
  else
    echo "Error: need curl or wget installed."; exit 1
  fi
}

# scripts/ and images/ aren't published as standalone release assets - only
# the compiled binaries are. So we fetch Codeberg's auto-generated source
# tarball for this tag and pull scripts/ + images/ out of it. This makes the
# script fully self-contained: it works no matter where it's invoked from
# (curl | bash, a copy sitting in $HOME, /tmp, wherever) since it no longer
# depends on BASH_SOURCE[0]/dirname to find its own siblings.
SRC_TARBALL_URL="https://codeberg.org/${REPO}/archive/${VERSION}.tar.gz"
SRC_TMPDIR="$(mktemp -d)"
cleanup_src_tmpdir() {
  rm -rf "$SRC_TMPDIR"
}
trap cleanup_src_tmpdir EXIT

SELF_DIR=""
echo -e "\n* Fetching source tarball for ${VERSION}"
SRC_TARBALL="${SRC_TMPDIR}/source.tar.gz"
if fetch "$SRC_TARBALL_URL" "$SRC_TARBALL" && [ -s "$SRC_TARBALL" ]; then
  if tar -xzf "$SRC_TARBALL" -C "$SRC_TMPDIR" 2>/dev/null; then
    # Codeberg archives extract into a single top-level dir, typically
    # "<repo>-<tag>" or similar - find it rather than hardcoding the name.
    EXTRACTED_ROOT="$(find "$SRC_TMPDIR" -mindepth 1 -maxdepth 1 -type d | head -n1)"
    if [ -n "$EXTRACTED_ROOT" ] && [ -d "${EXTRACTED_ROOT}/scripts" ]; then
      SELF_DIR="${EXTRACTED_ROOT}/scripts"
      echo "  -> using ${SELF_DIR} as scripts source"
    else
      echo "  -> WARNING: extracted tarball has no scripts/ directory"
    fi
  else
    echo "  -> WARNING: failed to extract source tarball"
  fi
else
  echo "  -> WARNING: failed to download source tarball from ${SRC_TARBALL_URL}"
fi

# Final sanity check: even after a successful download+extract, make sure
# SELF_DIR actually contains the files we expect before trusting it as a
# source for `cp -r`.
if [ -n "$SELF_DIR" ]; then
  if [ ! -f "${SELF_DIR}/cpufreqctl.sh" ] && [ ! -f "${SELF_DIR}/auto-cpufreq-openrc" ] && [ ! -f "${SELF_DIR}/auto-cpufreq.service" ]; then
    echo "  -> WARNING: '${SELF_DIR}' does not contain the expected auto-cpufreq script files, refusing to use it as source"
    SELF_DIR=""
  fi
fi

BIN_DIR="/usr/local/bin"
BIN_LINK_DIR="/usr/bin"
# /usr/local/share/auto-cpufreq/scripts is the single source of truth - it's
# what the Rust binary's SCRIPTS_DIR constant reads from.
# /usr/local/scripts is kept only as a symlink to it, for the manual-install
# convention / muscle memory - there is no second copy to keep in sync.
SCRIPTS_DIR="/usr/local/share/auto-cpufreq/scripts"
SCRIPTS_DIR_LINK="/usr/local/scripts"
# images just get copied straight into place - no symlink needed here
IMAGES_DIR="/usr/local/share/auto-cpufreq/images"
APPS_DIR="/usr/share/applications"

mkdir -p "$BIN_DIR" "$SCRIPTS_DIR" "$IMAGES_DIR" "$APPS_DIR"

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

install_bin() {
  # install_bin <filename>
  local name="$1"
  local tmp
  # create the temp file inside BIN_DIR itself so the final `mv` is an
  # atomic rename on the same filesystem - this lets us replace a binary
  # that is currently running (e.g. when this script is invoked as a child
  # of `auto-cpufreq --update`) without hitting "Text file busy" from cp
  tmp="$(mktemp "${BIN_DIR}/.${name}.XXXXXX")"
  echo -e "\n* Downloading ${name}"
  fetch "${BASE_URL}/${VERSION}/${name}" "$tmp"
  chmod +x "$tmp"
  mv -f "$tmp" "${BIN_DIR}/${name}"
  echo "  -> ${BIN_DIR}/${name}"

  # keep /usr/bin/<name> as a symlink to the /usr/local/bin copy
  if [ -d "$BIN_LINK_DIR" ] && [ "$(readlink -f "$BIN_LINK_DIR")" != "$(readlink -f "$BIN_DIR")" ]; then
    if [ -L "${BIN_LINK_DIR}/${name}" ] || [ -e "${BIN_LINK_DIR}/${name}" ]; then
      rm -f "${BIN_LINK_DIR}/${name}"
    fi
    ln -s "${BIN_DIR}/${name}" "${BIN_LINK_DIR}/${name}"
    echo "  -> ${BIN_LINK_DIR}/${name} (symlink)"
  fi
}

# ---------------------------------------------------------------------------
# 0. Stop any currently running instance so binaries can be overwritten
# ---------------------------------------------------------------------------
echo -e "\n* Stopping any running auto-cpufreq service (if present)"
case "$(ps h -o comm 1)" in
  init)     rc-service auto-cpufreq stop 2>/dev/null || true ;;
  systemd)  systemctl stop auto-cpufreq 2>/dev/null || true ;;
  dinit)    dinitctl stop auto-cpufreq 2>/dev/null || true ;;
  runit)    sv down auto-cpufreq 2>/dev/null || true ;;
  s6-svscan) s6-rc -d change auto-cpufreq 2>/dev/null || true ;;
esac

# Build the set of PIDs we must never kill: this script's own ancestry.
# This matters because `auto-cpufreq --update` runs this very script as a
# child process and blocks on it - if we pkill -x auto-cpufreq here, we'd
# kill our own parent (or grandparent) and get SIGTERM'd out from under it.
SAFE_PIDS=""
p="$$"
while [ -n "$p" ] && [ "$p" != "1" ]; do
  SAFE_PIDS="${SAFE_PIDS} ${p}"
  p="$(ps -o ppid= -p "$p" 2>/dev/null | tr -d ' ')"
done

kill_by_name_except_self() {
  # kill_by_name_except_self <process-name>
  local name="$1" pid skip
  for pid in $(pgrep -x "$name" 2>/dev/null || true); do
    skip=0
    for s in $SAFE_PIDS; do
      [ "$pid" = "$s" ] && skip=1 && break
    done
    [ "$skip" -eq 1 ] && continue
    kill -TERM "$pid" 2>/dev/null || true
  done
}

kill_by_name_except_self "auto-cpufreq"
kill_by_name_except_self "auto-cpufreq-tray"
kill_by_name_except_self "auto-cpufreq-gtk"
sleep 1

# ---------------------------------------------------------------------------
# 1. Download + install binaries into /usr/local/bin
# ---------------------------------------------------------------------------
install_bin "auto-cpufreq"
install_bin "auto-cpufreq-gtk"
install_bin "auto-cpufreq-tray"

# ---------------------------------------------------------------------------
# 2. Copy everything else from the local scripts/ dir into SCRIPTS_DIR,
#    then make SCRIPTS_DIR_LINK a symlink pointing at it.
# ---------------------------------------------------------------------------
echo -e "\n* Installing scripts to ${SCRIPTS_DIR}"
if [ -z "$SELF_DIR" ]; then
  echo "  -> WARNING: no valid script source directory detected, leaving ${SCRIPTS_DIR} untouched"
elif [ "$(readlink -f "$SELF_DIR")" = "$(readlink -f "$SCRIPTS_DIR")" ]; then
  echo "  -> already running from ${SCRIPTS_DIR}, skipping copy"
else
  # wipe first so files removed upstream don't linger from a previous version
  rm -rf "${SCRIPTS_DIR:?}"/*
  cp -ruv "${SELF_DIR}/." "${SCRIPTS_DIR}/"
fi
chmod +x "${SCRIPTS_DIR}"/auto-cpufreq-* 2>/dev/null || true
chmod +x "${SCRIPTS_DIR}"/*.sh 2>/dev/null || true

echo -e "\n* Linking ${SCRIPTS_DIR_LINK} -> ${SCRIPTS_DIR}"
if [ -L "$SCRIPTS_DIR_LINK" ]; then
  # already a symlink - make sure it points to the right place
  if [ "$(readlink -f "$SCRIPTS_DIR_LINK")" != "$(readlink -f "$SCRIPTS_DIR")" ]; then
    rm -f "$SCRIPTS_DIR_LINK"
    ln -s "$SCRIPTS_DIR" "$SCRIPTS_DIR_LINK"
  fi
elif [ -d "$SCRIPTS_DIR_LINK" ]; then
  # a real directory from a previous (pre-symlink) install - migrate it once
  echo "  -> found existing directory at ${SCRIPTS_DIR_LINK}, migrating contents"
  cp -ruv "${SCRIPTS_DIR_LINK}/." "${SCRIPTS_DIR}/" || true
  rm -rf "$SCRIPTS_DIR_LINK"
  ln -s "$SCRIPTS_DIR" "$SCRIPTS_DIR_LINK"
elif [ -e "$SCRIPTS_DIR_LINK" ]; then
  # something odd (a file) sitting at that path
  rm -f "$SCRIPTS_DIR_LINK"
  ln -s "$SCRIPTS_DIR" "$SCRIPTS_DIR_LINK"
else
  ln -s "$SCRIPTS_DIR" "$SCRIPTS_DIR_LINK"
fi

# images/ sits alongside scripts/ at the repo root - copy it straight into
# place under /usr/local/share/auto-cpufreq/images, no symlink involved.
# images/ sits alongside scripts/ at the repo root - copy it straight into
# place under /usr/local/share/auto-cpufreq/images, no symlink involved.
if [ -n "$SELF_DIR" ]; then
  IMAGES_SRC="${SELF_DIR}/../images"
  if [ -d "$IMAGES_SRC" ]; then
    echo -e "\n* Installing images to ${IMAGES_DIR}"
    # wipe first so files removed upstream don't linger from a previous version
    rm -rf "${IMAGES_DIR:?}"/*
    cp -ruv "${IMAGES_SRC}/." "${IMAGES_DIR}/"
  fi
fi

# cpufreqctl.sh also gets a callable copy in /usr/local/bin (matches existing convention)
if [ -f "${SCRIPTS_DIR}/cpufreqctl.sh" ]; then
  cp "${SCRIPTS_DIR}/cpufreqctl.sh" "${BIN_DIR}/cpufreqctl.auto-cpufreq"
  chmod +x "${BIN_DIR}/cpufreqctl.auto-cpufreq"
  echo "  -> ${BIN_DIR}/cpufreqctl.auto-cpufreq"
fi

# ---------------------------------------------------------------------------
# 3. Desktop entry
# ---------------------------------------------------------------------------
if [ -f "${SCRIPTS_DIR}/auto-cpufreq-gtk.desktop" ]; then
  echo -e "\n* Installing desktop entry to ${APPS_DIR}"
  cp "${SCRIPTS_DIR}/auto-cpufreq-gtk.desktop" "${APPS_DIR}/auto-cpufreq-gtk.desktop"
fi

# pkexec policy (install if polkit is present)
if [ -d /usr/share/polkit-1/actions ] && [ -f "${SCRIPTS_DIR}/org.auto-cpufreq.pkexec.policy" ]; then
  echo -e "\n* Installing polkit policy"
  cp "${SCRIPTS_DIR}/org.auto-cpufreq.pkexec.policy" /usr/share/polkit-1/actions/org.auto-cpufreq.pkexec.policy
fi

# udev rule
if [ -d /etc/udev/rules.d ] && [ -f "${SCRIPTS_DIR}/50-auto-cpufreq.rules" ]; then
  echo -e "\n* Installing udev rule"
  cp "${SCRIPTS_DIR}/50-auto-cpufreq.rules" /etc/udev/rules.d/50-auto-cpufreq.rules
  udevadm control --reload-rules 2>/dev/null || true
fi

# ---------------------------------------------------------------------------
# 4. Init system service deployment
# ---------------------------------------------------------------------------
function auto_cpufreq_install {
    echo -e "\n* Starting auto-cpufreq daemon ($1) service"
    $2
    echo -e "\n* Enabling auto-cpufreq daemon ($1) at boot"
    $3
}

case "$(ps h -o comm 1)" in
  dinit)
    echo -e "\n* Deploying auto-cpufreq (dinit) unit file"
    cp "${SCRIPTS_DIR}/auto-cpufreq-dinit" /etc/dinit.d/auto-cpufreq
    auto_cpufreq_install "dinit" "dinitctl start auto-cpufreq" "dinitctl enable auto-cpufreq"
  ;;
  init)
    echo -e "\n* Deploying auto-cpufreq openrc unit file"
    cp "${SCRIPTS_DIR}/auto-cpufreq-openrc" /etc/init.d/auto-cpufreq
    chmod +x /etc/init.d/auto-cpufreq
    auto_cpufreq_install "openrc" "rc-service auto-cpufreq start" "rc-update add auto-cpufreq"
  ;;
  runit)
    # First argument is the "sv" path, second argument is the "service" path
    runit_ln() {
      echo -e "\n* Deploying auto-cpufreq (runit) unit file"
      mkdir -p "$1"/sv/auto-cpufreq
      cp "${SCRIPTS_DIR}/auto-cpufreq-runit" "$1"/sv/auto-cpufreq/run
      chmod +x "$1"/sv/auto-cpufreq/run
      echo -e "\n* Creating symbolic link ($2/service/auto-cpufreq -> $1/sv/auto-cpufreq)"
      ln -s "$1"/sv/auto-cpufreq "$2"/service
      sv start auto-cpufreq
      sv up auto-cpufreq
    }
    if [ -f /etc/os-release ]; then
      eval "$(cat /etc/os-release)"
      case $ID in
        void) runit_ln /etc /var;;
        artix) runit_ln /etc/runit /run/runit;;
        *)
          echo -e "\n* Runit init detected but your distro is not supported\n"
          echo -e "\n* Please open an issue on https://codeberg.org/${REPO}\n"
        ;;
      esac
    fi
  ;;
  systemd)
    echo -e "\n* Deploying auto-cpufreq systemd unit file"
    cp "${SCRIPTS_DIR}/auto-cpufreq.service" /etc/systemd/system/auto-cpufreq.service
    echo -e "\n* Reloading systemd manager configuration"
    systemctl daemon-reload
    auto_cpufreq_install "systemd" "systemctl start auto-cpufreq" "systemctl enable auto-cpufreq"
  ;;
  s6-svscan)
    echo -e "\n* Deploying auto-cpufreq (s6) unit file"
    cp -r "${SCRIPTS_DIR}/auto-cpufreq-s6" /etc/s6/sv/auto-cpufreq
    echo -e "\n* Add auto-cpufreq service (s6) to default bundle"
    s6-service add default auto-cpufreq
    auto_cpufreq_install "s6" "s6-rc -u change auto-cpufreq default" "true"
    echo -e "\n* Update daemon service bundle (s6)"
    s6-db-reload
  ;;
  *)
    echo -e "\n* Unsupported init system detected, could not install the daemon\n"
    echo -e "\n* Please open an issue on https://codeberg.org/${REPO}\n"
  ;;
esac

# ---------------------------------------------------------------------------
# 5. Final cleanup - remove any leftover temporary files from this install
# ---------------------------------------------------------------------------
echo -e "\n* Cleaning up temporary files"
# install_bin creates its temp file inside BIN_DIR as .<name>.XXXXXX and
# mv's it into place right after download, so normally nothing is left.
# This is just a safety net in case a run was interrupted mid-download.
for leftover in "${BIN_DIR}"/.auto-cpufreq*.??????; do
  [ -e "$leftover" ] || continue
  rm -f "$leftover"
done

echo
echo "* auto-cpufreq ${VERSION} installation complete."
echo
