#!/bin/bash
# ============================================================================
#  filesir - built-in management CLI for the Files.ir Backup module
#
#  Installed as /usr/local/sbin/filesir with underscore aliases:
#    filesir_update | filesir_repair | filesir_uninstall
#    filesir_status | filesir_version
#
#  filesir update     pull + verify + install a newer version from the repo
#  filesir repair     re-install the cached package (offline; falls back to repo)
#  filesir uninstall  remove the module (add --purge to wipe data/config/tokens)
#  filesir status     one-look health/version overview
#  filesir version    installed version
#
#  Repo source is read from $PREFIX/repo.conf (written at install time,
#  overridable with FILESIR_BASE_URL). Set FILESIR_DEPLOY_DRY=1 to rehearse.
# ============================================================================
set -euo pipefail

PREFIX="${FILESIR_PREFIX:-/usr/local/filesir_backup}"
DIST="$PREFIX/dist"
LOCK="${FILESIR_LOCK:-/var/run/filesir-cli.lock}"

say(){ printf '\033[1;36m[filesir]\033[0m %s\n' "$*"; }
fail(){ printf '\033[1;31m[filesir] ERROR:\033[0m %s\n' "$*" >&2; exit 1; }

usage(){
cat <<'U'
Files.ir Backup - management commands (run as root):

  filesir_update               update to the latest repo version (data kept)
  filesir_repair               re-copy files & re-register from the local cache
  filesir_uninstall            remove the module (backups/data stay)
  filesir_uninstall --purge    remove EVERYTHING incl. data, config, tokens
  filesir_status               versions, repo reachability, cron & data overview
  filesir_version              installed version

The same commands also work as: filesir update / repair / uninstall / status / version
U
}

# Two repos: the primary and a mirror. Whichever answers first (20s probe on
# latest.txt) is used for everything in that run; if the primary is down the
# mirror takes over transparently. FILESIR_BASE_URL pins a single source.
repo_candidates(){
    if [ -n "${FILESIR_BASE_URL:-}" ]; then echo "$FILESIR_BASE_URL"; return; fi
    [ -f "$PREFIX/repo.conf" ] && . "$PREFIX/repo.conf"
    echo "${REPO_URL:-https://whm.hercol.ir}"
    echo "${REPO_URL2:-https://whm.kinkhub.ir}"
}
repo_url(){
    local m
    for m in $(repo_candidates); do
        if curl -fsSL --connect-timeout 20 --max-time 25 -o /dev/null "$m/latest.txt" 2>/dev/null; then
            echo "$m"; return
        fi
        say "repo $m did not answer - trying the next one ..." >&2
    done
    # nothing answered the probe: still return the primary so the error message is meaningful
    repo_candidates | head -1
}

installed_version(){ cat "$PREFIX/VERSION" 2>/dev/null || echo "-"; }

fetch_verify(){  # $1=base_url -> extracts into $WORK, caches tarball in $DIST
    local base="$1" tb="filesir-backup-latest.tar.gz"
    mkdir -p "$WORK" "$DIST"; cd "$WORK"
    say "downloading $tb from $base ..."
    if ! curl -fsSL --connect-timeout 20 -o "$tb" "$base/$tb"; then
        local alt ok=0
        for alt in $(repo_candidates); do
            [ "$alt" = "$base" ] && continue
            say "download from $base failed - retrying from $alt ..."
            if curl -fsSL --connect-timeout 20 -o "$tb" "$alt/$tb"; then base="$alt"; ok=1; break; fi
        done
        [ "$ok" = "1" ] || fail "download failed from every repo: $tb"
    fi
    if curl -fsSL -o "$tb.sha256" "$base/$tb.sha256" 2>/dev/null; then
        local want got
        want=$(awk '{print $1}' "$tb.sha256"); got=$(sha256sum "$tb" | awk '{print $1}')
        [ "$want" = "$got" ] || fail "sha256 mismatch (want $want got $got) - aborting."
        say "sha256 OK."
    else
        say "no checksum published - skipping verification."
    fi
    tar -xzf "$tb" || fail "extraction failed."
    [ -d "$WORK/filesir-backup" ] || fail "unexpected tarball layout."
    local ver; ver=$(cat "$WORK/filesir-backup/VERSION")
    cp -f "$tb" "$DIST/filesir-backup-$ver.tar.gz"
    ln -sfn "filesir-backup-$ver.tar.gz" "$DIST/latest.tar.gz"
    [ -f "$tb.sha256" ] && cp -f "$tb.sha256" "$DIST/filesir-backup-$ver.tar.gz.sha256" || true
}

run_installer(){
    cd "$WORK/filesir-backup"
    if [ "${FILESIR_DEPLOY_DRY:-0}" = "1" ]; then
        say "DRY RUN: would execute ./install.sh (version $(cat VERSION))"
    else
        FILESIR_REPO_URL="$(repo_url)" bash ./install.sh || fail "installer failed."
    fi
}

cmd_update(){
    local base cur new
    base=$(repo_url); cur=$(installed_version)
    new=$(curl -fsSL "$base/latest.txt" 2>/dev/null || true)
    if [ -z "$new" ]; then
        say "cannot read $base/latest.txt - trying a direct download instead."
    elif [ "$new" = "$cur" ]; then
        say "already on the latest version ($cur) - nothing to do."
        return 0
    else
        say "update available: $cur -> $new"
    fi
    fetch_verify "$base"
    say "updating (data and settings are kept) ..."
    run_installer
    say "update complete. Now on $(installed_version)."
}

cmd_repair(){
    if [ -f "$DIST/latest.tar.gz" ]; then
        say "repairing from the local cache ($(readlink "$DIST/latest.tar.gz")) ..."
        mkdir -p "$WORK"; cd "$WORK"
        tar -xzf "$DIST/latest.tar.gz" || fail "cached package is unreadable - run filesir_update."
    else
        say "no cached package yet - fetching from the repo once."
        fetch_verify "$(repo_url)"
    fi
    run_installer
    say "repair complete (data untouched)."
}

cmd_uninstall(){
    local un="$PREFIX/uninstall.sh"
    [ -f "$un" ] || fail "uninstaller not found at $un - run filesir_repair first."
    if [ "${FILESIR_DEPLOY_DRY:-0}" = "1" ]; then
        say "DRY RUN: would execute $un $*"
    else
        bash "$un" "$@" || fail "uninstaller failed."
    fi
    say "uninstalled.${*:+ (options: $*)}"
}

cmd_status(){
    local base cur new
    base=$(repo_url); cur=$(installed_version)
    new=$(curl -fsSL --max-time 8 "$base/latest.txt" 2>/dev/null || echo "unreachable")
    echo "Files.ir Backup status"
    echo "  installed version : $cur"
    echo "  repo              : $base"
    echo "  repo latest       : $new"
    if [ "$new" != "unreachable" ] && [ -n "$new" ] && [ "$new" != "$cur" ]; then
        echo "  -> update available: run filesir_update"
    fi
    echo "  install dir       : $PREFIX $( [ -d "$PREFIX" ] && echo '(present)' || echo '(MISSING)' )"
    echo "  cached package    : $( [ -f "$DIST/latest.tar.gz" ] && readlink "$DIST/latest.tar.gz" || echo 'none (repair will fetch)' )"
    local cron="none"
    { crontab -l 2>/dev/null | grep -q filesir && cron="root crontab"; } || true
    ls /etc/cron.d/filesir* > /dev/null 2>&1 && cron="/etc/cron.d"
    echo "  cron              : $cron"
    local ddir="${FILESIR_DATA_DIR:-/var/cpanel/filesir_backup}"
    if [ -d "$ddir" ]; then
        echo "  data dir          : $ddir ($(du -sh "$ddir" 2>/dev/null | cut -f1))"
        [ -f "$ddir/logs/engine.log" ] && echo "  last log line     : $(tail -1 "$ddir/logs/engine.log")"
    else
        echo "  data dir          : $ddir (not created yet)"
    fi
}

# ---------------------------------------------------------------------------
CMD="$(basename "$0")"
case "$CMD" in
  filesir_update)    CMD=update ;;
  filesir_repair)    CMD=repair ;;
  filesir_uninstall) CMD=uninstall ;;
  filesir_status)    CMD=status ;;
  filesir_version)   CMD=version ;;
  *)                 CMD="${1:-help}"; shift || true ;;
esac

[ "$CMD" = help ] || [ "$CMD" = "-h" ] || [ "$CMD" = "--help" ] && { usage; exit 0; }
[ "$CMD" = version ] && { installed_version; exit 0; }
[ "$CMD" = status ] && { cmd_status; exit 0; }
[ "$(id -u)" = 0 ] || fail "run as root."

WORK=$(mktemp -d /tmp/filesir-cli.XXXXXX)
trap 'rm -rf "$WORK"' EXIT

exec 9>"$LOCK"
flock -n 9 || fail "another filesir command is already running."

case "$CMD" in
  update)    cmd_update ;;
  repair)    cmd_repair ;;
  uninstall) cmd_uninstall "$@" ;;
  *)         usage; exit 1 ;;
esac
