#!/bin/bash
# =============================================================================
# WOOO AIOps - Configure provider-neutral rclone offsite target
# 2026-05-19 ogt + Codex: Google Drive 成為優先 offsite 目標。
#
# 安全邊界：
#   - 這支腳本只寫 /backup/scripts/offsite.env 的 provider/remote/path 設定。
#   - Google Drive OAuth token 由 rclone 自己保存在 host-local rclone.conf。
#   - 不把 token、refresh token、password 或 recovery code 印到畫面。
# =============================================================================

set -euo pipefail

BACKUP_BASE="${BACKUP_BASE:-/backup}"
OFFSITE_ENV_FILE="${BACKUP_OFFSITE_ENV_FILE:-${BACKUP_BASE}/scripts/offsite.env}"
REQUESTED_REMOTE_NAME="${OFFSITE_RCLONE_REMOTE:-}"
REQUESTED_REMOTE_ROOT="${OFFSITE_REMOTE_ROOT:-}"
if [ -f "${OFFSITE_ENV_FILE}" ]; then
    # shellcheck disable=SC1090
    source "${OFFSITE_ENV_FILE}"
fi
REMOTE_NAME="${REQUESTED_REMOTE_NAME:-${OFFSITE_RCLONE_REMOTE:-gdrive}}"
REMOTE_ROOT="${REQUESTED_REMOTE_ROOT:-${OFFSITE_REMOTE_ROOT:-${REMOTE_NAME}:awoooi-backups/restic}}"
SOURCE_REMOTE="${OFFSITE_RCLONE_SOURCE_REMOTE:-gdrive}"
ROOT_REMOTE_NAME="${OFFSITE_RCLONE_ROOT_REMOTE:-gdrive_awoooi_restic}"
ROOT_REMOTE_PATH="${OFFSITE_RCLONE_ROOT_PATH:-awoooi-backups/restic}"
MODE="status"

usage() {
    cat <<'USAGE'
Usage:
  configure-offsite-rclone.sh --status
  configure-offsite-rclone.sh --interactive
  OFFSITE_RCLONE_REMOTE=gdrive OFFSITE_REMOTE_ROOT=gdrive:awoooi-backups/restic configure-offsite-rclone.sh --write-from-env
  OFFSITE_RCLONE_SOURCE_REMOTE=gdrive OFFSITE_RCLONE_ROOT_REMOTE=gdrive_awoooi_restic configure-offsite-rclone.sh --create-root-remote

Notes:
  - Google Drive 請先用 --interactive 進入 rclone config，建立 remote，例如 gdrive。
  - --create-root-remote 會用既有 OAuth remote 建立 root-scoped remote，避免每次從整個 Drive 查找路徑。
  - /backup/scripts/offsite.env 只保存 remote 名稱與路徑，不保存 OAuth token。
  - rclone.conf 是 host-local secret，必須納入 credential escrow，不可進 repo。
USAGE
}

while [ "$#" -gt 0 ]; do
    case "$1" in
        --status)
            MODE="status"
            shift
            ;;
        --interactive)
            MODE="interactive"
            shift
            ;;
        --write-from-env)
            MODE="write-from-env"
            shift
            ;;
        --create-root-remote)
            MODE="create-root-remote"
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            echo "Unknown argument: $1" >&2
            usage >&2
            exit 2
            ;;
    esac
done

quote_shell() {
    printf "%s" "$1" | sed "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"
}

rclone_present() {
    command -v rclone >/dev/null 2>&1
}

remote_configured() {
    rclone_present || return 1
    rclone listremotes 2>/dev/null | grep -Fxq "${REMOTE_NAME}:"
}

source_remote_configured() {
    rclone_present || return 1
    rclone listremotes 2>/dev/null | grep -Fxq "${SOURCE_REMOTE}:"
}

env_mode_ok() {
    [ -f "${OFFSITE_ENV_FILE}" ] || return 1
    mode="$(stat -c '%a' "${OFFSITE_ENV_FILE}" 2>/dev/null || stat -f '%Lp' "${OFFSITE_ENV_FILE}" 2>/dev/null || echo unknown)"
    [ "${mode}" = "600" ]
}

write_env() {
    install -d -m 750 "$(dirname "${OFFSITE_ENV_FILE}")"
    umask 077
    cat > "${OFFSITE_ENV_FILE}" <<EOF
# AWOOOI host-local offsite backup target.
# Created by configure-offsite-rclone.sh.
# No OAuth token or provider secret is stored in this file.
export OFFSITE_PROVIDER=rclone
export OFFSITE_RCLONE_REMOTE=$(quote_shell "${REMOTE_NAME}")
export OFFSITE_REMOTE_ROOT=$(quote_shell "${REMOTE_ROOT}")
export RCLONE_BWLIMIT=\${RCLONE_BWLIMIT:-8M}
export RCLONE_TRANSFERS=\${RCLONE_TRANSFERS:-2}
export RCLONE_CHECKERS=\${RCLONE_CHECKERS:-4}
export OFFSITE_RCLONE_BACKEND=\${OFFSITE_RCLONE_BACKEND:-drive}
export RCLONE_FAST_LIST=\${RCLONE_FAST_LIST:-1}
export RCLONE_DRIVE_USE_TRASH=\${RCLONE_DRIVE_USE_TRASH:-false}
export OFFSITE_SYNC_DELETE_OLD=\${OFFSITE_SYNC_DELETE_OLD:-1}
export OFFSITE_SYNC_MAX_LOAD_1=\${OFFSITE_SYNC_MAX_LOAD_1:-12}
export OFFSITE_SYNC_MAX_BACKUP_DISK_USED_PCT=\${OFFSITE_SYNC_MAX_BACKUP_DISK_USED_PCT:-92}
export OFFSITE_SYNC_REQUIRE_ENABLE_MARKER_FOR_FULL=\${OFFSITE_SYNC_REQUIRE_ENABLE_MARKER_FOR_FULL:-1}
export OFFSITE_SYNC_FULL_MIN_RUNWAY_MINUTES=\${OFFSITE_SYNC_FULL_MIN_RUNWAY_MINUTES:-270}
export OFFSITE_SYNC_NOTIFY_SKIPPED=\${OFFSITE_SYNC_NOTIFY_SKIPPED:-0}
export OFFSITE_SYNC_NOTIFY_SUCCESS=\${OFFSITE_SYNC_NOTIFY_SUCCESS:-0}
EOF
    chmod 0600 "${OFFSITE_ENV_FILE}"
}

print_status() {
    rclone_present && echo "RCLONE_PRESENT=1" || echo "RCLONE_PRESENT=0"
    echo "OFFSITE_PROVIDER=rclone"
    echo "OFFSITE_RCLONE_REMOTE=${REMOTE_NAME}"
    echo "OFFSITE_REMOTE_ROOT=${REMOTE_ROOT}"
    remote_configured && echo "RCLONE_REMOTE_CONFIGURED=1" || echo "RCLONE_REMOTE_CONFIGURED=0"
    [ -f "${OFFSITE_ENV_FILE}" ] && echo "OFFSITE_ENV_PRESENT=1" || echo "OFFSITE_ENV_PRESENT=0"
    env_mode_ok && echo "OFFSITE_ENV_MODE_OK=1" || echo "OFFSITE_ENV_MODE_OK=0"
}

root_remote_parent_path() {
    if [[ "${ROOT_REMOTE_PATH}" == */* ]]; then
        printf '%s' "${ROOT_REMOTE_PATH%/*}"
    fi
}

root_remote_leaf_name() {
    printf '%s/' "${ROOT_REMOTE_PATH##*/}"
}

create_root_scoped_remote() {
    local parent_path
    local parent_target
    local leaf_name
    local root_folder_id
    local rclone_conf

    if ! rclone_present; then
        echo "rclone command is missing; install rclone first." >&2
        exit 1
    fi
    if ! source_remote_configured; then
        echo "source rclone remote missing: ${SOURCE_REMOTE}:" >&2
        exit 1
    fi
    if ! command -v python3 >/dev/null 2>&1; then
        echo "python3 command is missing; cannot safely update rclone.conf without exposing token." >&2
        exit 1
    fi

    parent_path="$(root_remote_parent_path)"
    leaf_name="$(root_remote_leaf_name)"
    if [ -n "${parent_path}" ]; then
        parent_target="${SOURCE_REMOTE}:${parent_path}"
    else
        parent_target="${SOURCE_REMOTE}:"
    fi

    root_folder_id="$(rclone lsf --format pi "${parent_target}" --max-depth 1 \
        | awk -F';' -v leaf="${leaf_name}" '$1 == leaf {print $2; exit}')"
    if [ -z "${root_folder_id}" ]; then
        echo "target Google Drive folder not found below ${SOURCE_REMOTE}: ${ROOT_REMOTE_PATH}" >&2
        exit 1
    fi

    rclone_conf="$(rclone config file | awk 'previous {print; exit} /Configuration file is stored at:/ {previous=1}')"
    if [ -z "${rclone_conf}" ] || [ ! -f "${rclone_conf}" ]; then
        echo "rclone config file not found" >&2
        exit 1
    fi

    SOURCE_REMOTE="${SOURCE_REMOTE}" ROOT_REMOTE_NAME="${ROOT_REMOTE_NAME}" ROOT_FOLDER_ID="${root_folder_id}" RCLONE_CONF="${rclone_conf}" python3 - <<'PY'
import configparser
import os

path = os.environ["RCLONE_CONF"]
src = os.environ["SOURCE_REMOTE"]
dst = os.environ["ROOT_REMOTE_NAME"]
root_id = os.environ["ROOT_FOLDER_ID"]

cp = configparser.ConfigParser()
cp.read(path)
if not cp.has_section(src):
    raise SystemExit("source remote missing")
if not cp.has_section(dst):
    cp.add_section(dst)
for key, value in cp.items(src):
    cp.set(dst, key, value)
cp.set(dst, "root_folder_id", root_id)
with open(path, "w") as fh:
    cp.write(fh)
os.chmod(path, 0o600)
PY

    REMOTE_NAME="${ROOT_REMOTE_NAME}"
    REMOTE_ROOT="${ROOT_REMOTE_NAME}:"
    write_env
    echo "ROOT_SCOPED_REMOTE_READY=${ROOT_REMOTE_NAME}:"
    echo "ROOT_SCOPED_PATH=${ROOT_REMOTE_PATH}"
    print_status
}

case "${MODE}" in
    status)
        print_status
        ;;
    write-from-env)
        write_env
        print_status
        ;;
    create-root-remote)
        create_root_scoped_remote
        ;;
    interactive)
        if ! rclone_present; then
            echo "rclone command is missing; install rclone first." >&2
            exit 1
        fi

        echo "Current target remote name: ${REMOTE_NAME}"
        read -r -p "Google Drive rclone remote name [${REMOTE_NAME}]: " remote_input
        REMOTE_NAME="${remote_input:-${REMOTE_NAME}}"
        REMOTE_ROOT="${OFFSITE_REMOTE_ROOT:-${REMOTE_NAME}:awoooi-backups/restic}"

        if ! remote_configured; then
            echo "rclone remote ${REMOTE_NAME}: 尚未存在，接著會進入 rclone config。"
            echo "請選 Google Drive，完成 OAuth；不要把 token 貼到聊天或 repo。"
            rclone config
        fi

        read -r -p "Offsite remote root [${REMOTE_ROOT}]: " root_input
        REMOTE_ROOT="${root_input:-${REMOTE_ROOT}}"
        write_env
        print_status
        ;;
esac
