#!/bin/sh
# -----------------------------------------------------------------------------
# FishMon365 Tailscale Fix – Production Installer
# -----------------------------------------------------------------------------
#
# Author:      Per Otto Opstad
# Company:     ITVakta AS / FishMon365
# Website:     https://www.fishmon365.com
# Contact:     per@itvakta.no
#
# Created:     2026-04
# Version:     1.0
#
# Purpose:
#   Workaround for Tailscale instability on pfSense where auth-key is reused
#   on every service start, causing nodes to enter a broken state
#   (e.g. "invalid key", "logged out", "NoState").
#
#   This installer:
#     - Deploys a watchdog script for automatic recovery
#     - Disables repeated use of auth-key in pfSense rc script
#     - Ensures persistent and self-healing Tailscale operation
#
# Usage:
#   cd /tmp && rm -f install_tailscale_fix.sh && \
#   fetch -qo install_tailscale_fix.sh \
#   https://your.domain.xxx/tailscale/install_tailscale_fix.sh && \
#   chmod 700 install_tailscale_fix.sh && \
#   sh ./install_tailscale_fix.sh
#
# Notes:
#   - Safe to run multiple times (idempotent)
#   - Designed specifically for pfSense environments
#   - Requires Tailscale package to be installed
#
# Changelog:
#   1.0 (2026-04)
#     - Initial production version
#     - Added watchdog-based recovery
#     - Added automatic rc-script patching
#
# -----------------------------------------------------------------------------
#
set -eu

# === Variabler ===
WATCHDOG_SCRIPT="/root/tailscale_watchdog.sh"
RCFILE="/usr/local/etc/rc.d/pfsense_tailscaled"
CRON_SCHEDULE="*/5 * * * *"
CRON_LINE="${CRON_SCHEDULE} ${WATCHDOG_SCRIPT}"
TMP_CRON="/tmp/tailscale_watchdog.cron.$$"
LOCKFILE="/tmp/tailscale_watchdog.lock"

cleanup() {
  rm -f "$TMP_CRON" 2>/dev/null || true
}
trap cleanup EXIT INT TERM

require_root() {
  if [ "$(id -u)" -ne 0 ]; then
    echo "ERROR: Dette skriptet må kjøres som root." >&2
    exit 1
  fi
}

check_environment() {
  echo "[1/6] Sjekker miljø..."

  if [ ! -f "$RCFILE" ]; then
    echo "ERROR: Fant ikke rc-script: $RCFILE" >&2
    echo "Er dette riktig pfSense-boks med Tailscale installert?" >&2
    exit 1
  fi

  if [ ! -x /usr/local/bin/tailscale ]; then
    echo "ERROR: Fant ikke /usr/local/bin/tailscale" >&2
    echo "Sørg for at Tailscale-pakken er installert først." >&2
    exit 1
  fi

  if [ ! -x /usr/sbin/service ]; then
    echo "ERROR: Fant ikke /usr/sbin/service" >&2
    exit 1
  fi

  if ! command -v crontab >/dev/null 2>&1; then
    echo "ERROR: Fant ikke crontab-kommandoen." >&2
    exit 1
  fi
}

write_watchdog_script() {
  echo "[2/6] Oppretter/oppdaterer watchdog-script..."

  cat > "$WATCHDOG_SCRIPT" <<'EOF'
#!/bin/sh

LOCKFILE="/tmp/tailscale_watchdog.lock"
RCFILE="/usr/local/etc/rc.d/pfsense_tailscaled"
TS="/usr/local/bin/tailscale"
SERVICE="/usr/sbin/service"
RC_SERVICE="pfsense_tailscaled"
LOGTAG="tailscale-watchdog"

log() {
  logger -t "$LOGTAG" "$1"
}

cleanup() {
  rm -f "$LOCKFILE"
}
trap cleanup EXIT INT TERM

[ -e "$LOCKFILE" ] && exit 0
touch "$LOCKFILE"

get_status() {
  "$TS" status 2>&1 || true
}

is_known_error() {
  echo "$1" | grep -Eiq 'logged out|invalid key|API key does not exist|NoState'
}

is_healthy() {
  "$TS" ip -4 >/dev/null 2>&1
}

patch_rcfile_if_needed() {
  if grep -q '^pfsense_tailscaled_up_flags="--auth-key=' "$RCFILE"; then
    cp "$RCFILE" "${RCFILE}.bak_watchdog" 2>/dev/null || true
    sed -i '' 's/^pfsense_tailscaled_up_flags="--auth-key=/#pfsense_tailscaled_up_flags="--auth-key=/' "$RCFILE"
    log "Patched rc file (disabled auth-key)"
  fi
}

# Enforce patch every run
patch_rcfile_if_needed

STATUS="$(get_status)"

if is_healthy && ! is_known_error "$STATUS"; then
  exit 0
fi

if ! is_known_error "$STATUS"; then
  exit 0
fi

log "Detected known Tailscale error"
log "Restarting $RC_SERVICE"
$SERVICE "$RC_SERVICE" restart >/dev/null 2>&1
sleep 15

STATUS="$(get_status)"

if is_healthy && ! is_known_error "$STATUS"; then
  log "Recovered after restart"
  exit 0
fi

log "Still failing after restart"
exit 1
EOF

  chmod 700 "$WATCHDOG_SCRIPT"
  chown root:wheel "$WATCHDOG_SCRIPT" 2>/dev/null || true
}

install_or_update_cron() {
  echo "[3/6] Oppretter/oppdaterer cron..."

  {
    crontab -l 2>/dev/null | grep -v "$WATCHDOG_SCRIPT" || true
    echo "$CRON_LINE"
  } > "$TMP_CRON"

  crontab "$TMP_CRON"
}

run_fix_now() {
  echo "[4/6] Kjører fix nå..."
  rm -f "$LOCKFILE" 2>/dev/null || true
  "$WATCHDOG_SCRIPT" || true
}

verify_installation() {
  echo "[5/6] Verifiserer..."

  CRON_MATCH="$(crontab -l 2>/dev/null | grep -F "$WATCHDOG_SCRIPT" || true)"

  if [ -n "$CRON_MATCH" ]; then
    echo "OK: Cron-linje funnet:"
    echo "  $CRON_MATCH"
  else
    echo "WARNING: Fant ikke cron-linje for watchdog."
  fi

  echo
  echo "Auth-key linjer i rc-script:"
  grep -n 'auth-key' "$RCFILE" || true

  echo
  echo "Tailscale status (kort):"
  if /usr/local/bin/tailscale ip -4 >/dev/null 2>&1; then
    TSIP="$(/usr/local/bin/tailscale ip -4 | head -n 1)"
    echo "  IPv4: $TSIP"
  else
    echo "  IPv4: utilgjengelig"
  fi
  /usr/local/bin/tailscale status 2>/dev/null | head -n 5 || true

  echo
  echo "Siste watchdog-linjer i system.log:"
  grep 'tailscale-watchdog' /var/log/system.log 2>/dev/null | tail -n 10 || true
}

print_summary() {
  echo "--------------------------------------"
  echo "Installasjon fullført"
  echo "Watchdog:     $WATCHDOG_SCRIPT"
  echo "RC-script:    $RCFILE"
  echo "Cron:         $CRON_SCHEDULE"
  echo
  echo "Nyttige kommandoer:"
  echo "  Sjekk cron:      crontab -l"
  echo "  Kjør manuelt:    $WATCHDOG_SCRIPT"
  echo "  Sjekk status:    tailscale status"
  echo "  Sjekk IP:        tailscale ip -4"
  echo "  Sjekk logg:      grep tailscale-watchdog /var/log/system.log | tail -n 50"
  echo "  Restart TS:      service pfsense_tailscaled restart"
  echo "  Sjekk auth-key:  grep -n auth-key $RCFILE"
}

echo "=== FishMon365 Tailscale Fix Installer ==="

require_root
check_environment
write_watchdog_script
install_or_update_cron
run_fix_now
verify_installation
print_summary
