#!/bin/sh
#
# Installs the FileNest sync client on macOS and Linux.
#
#   curl -fsSL https://filenest.webyne.com/install.sh | sh
#   curl -fsSL https://filenest.webyne.com/install.sh | sh -s -- --uninstall
#
# Installs under ~/.local/share, puts `filenest` on PATH, and registers a
# per-user service (systemd on Linux, launchd on macOS) so syncing starts at
# login. Everything is per-user: no sudo, nothing written outside $HOME, and so
# nothing to undo system-wide.
#
# POSIX sh, not bash: macOS ships bash 3.2 from 2007, and this has to run
# identically there and on a minimal Linux container.

set -eu

SERVER="${FILENEST_SERVER:-https://filenest.webyne.com}"
FOLDER="${FILENEST_FOLDER:-$HOME/FileNest}"
PREFIX="$HOME/.local/share/filenest"
BINDIR="$HOME/.local/bin"
AUTOSTART=1
UNINSTALL=0

for arg in "$@"; do
    case "$arg" in
        --uninstall)     UNINSTALL=1 ;;
        --no-autostart)  AUTOSTART=0 ;;
        --server=*)      SERVER="${arg#--server=}" ;;
        --folder=*)      FOLDER="${arg#--folder=}" ;;
        *) echo "Unknown option: $arg" >&2; exit 1 ;;
    esac
done

say()  { printf '  %s\n' "$1"; }
ok()   { printf '  \033[32m%s\033[0m\n' "$1"; }
warn() { printf '  \033[33m%s\033[0m\n' "$1"; }
die()  { printf '  \033[31m%s\033[0m\n' "$1" >&2; exit 1; }

case "$(uname -s)" in
    Darwin) PLATFORM=macos ;;
    Linux)  PLATFORM=linux ;;
    *)      die "Unsupported platform: $(uname -s). Windows users want install.ps1." ;;
esac

SERVICE_LABEL="com.webyne.filenest-sync"
PLIST="$HOME/Library/LaunchAgents/$SERVICE_LABEL.plist"
UNIT="$HOME/.config/systemd/user/filenest-sync.service"

# ------------------------------------------------------------------ uninstall

if [ "$UNINSTALL" = 1 ]; then
    say 'Removing FileNest sync...'

    # Stop the service before deleting the files it is running.
    if [ "$PLATFORM" = macos ] && [ -f "$PLIST" ]; then
        launchctl unload "$PLIST" 2>/dev/null || true
        rm -f "$PLIST"
        ok 'Removed the launch agent.'
    fi

    if [ "$PLATFORM" = linux ] && [ -f "$UNIT" ]; then
        systemctl --user disable --now filenest-sync.service 2>/dev/null || true
        rm -f "$UNIT"
        systemctl --user daemon-reload 2>/dev/null || true
        ok 'Removed the systemd unit.'
    fi

    rm -rf "$PREFIX"
    rm -f "$BINDIR/filenest"
    rm -f "$HOME/.local/share/applications/filenest.desktop"
    ok 'Removed the program.'

    # Report the folder actually in use rather than the default, or the message
    # sends people looking for their files somewhere they never were.
    CONFIGURED="$FOLDER"
    if [ -f "$HOME/.filenest/config.json" ]; then
        SAVED=$(sed -n 's/.*"folder"[[:space:]]*:[[:space:]]*"\(.*\)".*/\1/p' "$HOME/.filenest/config.json" | head -1)
        [ -n "$SAVED" ] && CONFIGURED="$SAVED"
    fi

    printf '\n'
    warn "Your synced files in $CONFIGURED were left alone."
    warn "So was your device token in ~/.filenest. Delete that to sign out, or"
    warn 'revoke the device from the web app.'
    printf '\n'
    exit 0
fi

# -------------------------------------------------------------------- install

printf '\n  Installing FileNest sync\n\n'

command -v node >/dev/null 2>&1 || die 'Node.js is required. Install v20 or newer from https://nodejs.org and run this again.'

NODE_MAJOR=$(node -p 'process.versions.node.split(".")[0]')
[ "$NODE_MAJOR" -ge 20 ] || die "Node.js 20 or newer is required; found v$NODE_MAJOR."
ok "Node.js v$NODE_MAJOR found."

command -v curl >/dev/null 2>&1 || die 'curl is required.'
command -v tar  >/dev/null 2>&1 || die 'tar is required.'

TMP=$(mktemp -d)
# Cleans up on failure as well as success -- a partial download in /tmp is
# nobody's idea of a helpful leftover.
trap 'rm -rf "$TMP"' EXIT INT TERM

say "Downloading from $SERVER..."
curl -fsSL "$SERVER/downloads/filenest-sync-latest.tar.gz" -o "$TMP/client.tar.gz" \
    || die "Could not download from $SERVER."
curl -fsSL "$SERVER/downloads/SHA256SUMS" -o "$TMP/SHA256SUMS" \
    || die 'Could not download the checksum file.'

# You are about to give this a token and write access to your files. Check it
# is what the server published before running any of it.
say 'Verifying checksum...'
EXPECTED=$(awk '/filenest-sync-latest\.tar\.gz/ { print $1; exit }' "$TMP/SHA256SUMS")
[ -n "$EXPECTED" ] || die 'The server published no checksum for this build.'

if command -v sha256sum >/dev/null 2>&1; then
    ACTUAL=$(sha256sum "$TMP/client.tar.gz" | awk '{print $1}')
else
    # macOS has shasum, not sha256sum.
    ACTUAL=$(shasum -a 256 "$TMP/client.tar.gz" | awk '{print $1}')
fi

[ "$ACTUAL" = "$EXPECTED" ] || die "Checksum mismatch. Expected $EXPECTED, got $ACTUAL. Not installing."
ok 'Checksum matches.'

say 'Unpacking...'
tar -xzf "$TMP/client.tar.gz" -C "$TMP"
UNPACKED=$(find "$TMP" -maxdepth 1 -type d -name 'filenest-sync-*' | head -1)
[ -n "$UNPACKED" ] || die 'The archive did not contain what was expected.'

rm -rf "$PREFIX"
mkdir -p "$PREFIX" "$BINDIR"
cp -R "$UNPACKED/." "$PREFIX/"
ok "Installed to $PREFIX"

# ---- a `filenest` command on PATH -------------------------------------------

cat > "$BINDIR/filenest" <<EOF
#!/bin/sh
exec node "$PREFIX/bin/filenest.js" "\$@"
EOF
chmod +x "$BINDIR/filenest"
ok "Installed $BINDIR/filenest"

case ":$PATH:" in
    *":$BINDIR:"*) ;;
    *) warn "$BINDIR is not on your PATH. Add this to your shell profile:"
       printf '\n      export PATH="%s:$PATH"\n\n' "$BINDIR" ;;
esac

# ---- start at login ---------------------------------------------------------

if [ "$AUTOSTART" = 1 ]; then
    if [ "$PLATFORM" = macos ]; then
        mkdir -p "$(dirname "$PLIST")"
        cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>$SERVICE_LABEL</string>
  <key>ProgramArguments</key>
  <array>
    <string>$(command -v node)</string>
    <string>$PREFIX/bin/filenest.js</string>
    <string>watch</string>
    <string>--folder=$FOLDER</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>StandardOutPath</key><string>$HOME/.filenest/sync.log</string>
  <key>StandardErrorPath</key><string>$HOME/.filenest/sync.log</string>
</dict>
</plist>
EOF
        mkdir -p "$HOME/.filenest"
        launchctl unload "$PLIST" 2>/dev/null || true
        launchctl load "$PLIST" 2>/dev/null || true
        ok 'Registered to start at login (launchd).'
    else
        mkdir -p "$(dirname "$UNIT")"
        cat > "$UNIT" <<EOF
[Unit]
Description=FileNest sync
After=network-online.target

[Service]
Type=simple
ExecStart=$(command -v node) $PREFIX/bin/filenest.js watch --folder=$FOLDER
Restart=on-failure
RestartSec=30

[Install]
WantedBy=default.target
EOF
        systemctl --user daemon-reload 2>/dev/null || true
        systemctl --user enable filenest-sync.service 2>/dev/null || true
        ok 'Registered to start at login (systemd user unit).'
        say 'Run "systemctl --user start filenest-sync" to begin now.'
        say 'For syncing without being logged in: sudo loginctl enable-linger "$USER"'
    fi

    # A desktop entry so the control panel is launchable from the applications
    # menu -- the closest this build gets to a tray icon.
    if [ "$PLATFORM" = linux ]; then
        mkdir -p "$HOME/.local/share/applications"
        cat > "$HOME/.local/share/applications/filenest.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=FileNest sync
Comment=Open the FileNest sync control panel
Exec=$BINDIR/filenest ui
Terminal=true
Categories=Utility;Network;
EOF
        ok 'Added a desktop entry.'
    fi
fi

printf '\n  Done. Next:\n\n'
printf '    filenest login %s\n' "$SERVER"
printf '    filenest ui\n\n'
printf '  Syncing will not start until you sign in once.\n\n'
warn 'Deletions sync both ways. Try a scratch folder before pointing it at real work.'
printf '\n'
