#!/bin/bash
# ==============================================================================================
# ================================= Storage Migration ==========================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Migrates Varaverk between internal NVMe and USB flash storage modes.
#
# Internal mode: SCRIPTS_DIR = /boot/config/plugins/varaverk
#   All scripts, conf, state, and git repo live on fast internal storage.
#   Direct git pull/push. Zero write-wear concern.
#
# Flash mode:    SCRIPTS_DIR = /mnt/user/appdata/Varaverk
#   All scripts, conf, state, and git repo live in appdata.
#   Preserves USB flash lifetime. Array must be started for Varaverk to function.
#   git_pull_execute.sh syncs Plugin/ back to /boot/ after each pull so the
#   Unraid webUI always serves current PHP files.
#
# What this script updates:
#   varaverk.cfg      SCRIPTS_DIR
#   master.conf       TARGET_DIR
#   host*.conf        HOST*_STORAGE_MODE_INTERNAL
#   varaverk.cron     rebuilt via PHP (job paths regenerated for new SCRIPTS_DIR)
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
#   1. Determine current mode from varaverk.cfg SCRIPTS_DIR, and the requested target mode
#      Already in the target mode → exit cleanly, nothing to do
#   2. rsync -a --delete SRC → DST, then carry .git across so history survives the move
#   3. Rewrite the pointers, in this order:
#        varaverk.cfg   SCRIPTS_DIR
#        master.conf    TARGET_DIR
#        host*.conf     HOST*_STORAGE_MODE_INTERNAL
#   4. Rebuild varaverk.cron via PHP so every job path points at the new SCRIPTS_DIR
#   5. Flash mode only: sync Plugin/ back to /boot so the webUI keeps serving current PHP
#   6. Remove the old location once the new one is confirmed in place
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Atomic Path Transition
#   All four files (varaverk.cfg, master.conf, host*.conf, varaverk.cron) are
#   updated in a single pass. A partial migration would leave cron entries
#   pointing at the wrong SCRIPTS_DIR — all or nothing.
#
# PHP Rebuilds Cron
#   Job paths in varaverk.cron are derived from SCRIPTS_DIR. Rather than
#   text-substituting the cron file, the script regenerates it via PHP using
#   the new SCRIPTS_DIR as the source of truth.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
#   acquire_lock    — prevents concurrent migration attempts
#   dry-run mode    — shows all changes without touching any file
#   --status mode   — reports current mode without requiring a target
#   --to= required  — refuses to run without an explicit target mode
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# This script WRITES these rather than reading them — they are the migration's output:
#
#   varaverk.cfg
#     SCRIPTS_DIR                    the authoritative install path. Everything else in the
#                                    ecosystem derives from it, which is why it is written
#                                    first and the cron is rebuilt from it afterwards.
#
#   master.conf
#     TARGET_DIR                     kept in step with SCRIPTS_DIR
#
#   host*.conf
#     HOST*_STORAGE_MODE_INTERNAL    true = /boot/config/plugins/varaverk
#                                    false = /mnt/user/appdata/Varaverk
#
# Load-bearing: STATE_DIR, DATA_DIR, PERSISTENT_CONF_CACHE and the orchestrator job paths are
# all built from SCRIPTS_DIR. Changing storage mode moves every one of them, which is why the
# cron is regenerated rather than edited.
#
# In flash mode the array must be started before Varaverk can function at all — appdata is
# not mounted before that.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# storage_migrate.sh --to=internal
#     Migrate to /boot/config/plugins/varaverk
#
# storage_migrate.sh --to=flash
#     Migrate to /mnt/user/appdata/Varaverk
#
# storage_migrate.sh --dry-run --to=<mode>
#     Show what would happen — no changes made
#
# storage_migrate.sh --status
#     Show current mode, paths, and boot device info
#
# ==============================================================================================

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../../load_config.sh"

parse_args "$@"

# Relocates the entire Varaverk installation with rsync --delete and rm -rf, and rewrites
# varaverk.cfg, master.conf and host*.conf. Everything here needs root.
[[ "$EUID" -ne 0 ]] && { error "Must be run as root"; exit 1; }

acquire_lock
detect_hosts

VV_CFG="/boot/config/plugins/varaverk/varaverk.cfg"
INTERNAL_DIR="/boot/config/plugins/varaverk"
FLASH_DIR="/mnt/user/appdata/Varaverk"
CONF_FILE="$CONF_DIR/${MY_ID,,}.conf"

# ──────────────────────────────────────────────────────────────────────────────
# Parse --to= from raw args (parse_args doesn't handle this flag)
TO_MODE=""
for arg in "$@"; do
    case "$arg" in
        --to=internal) TO_MODE="internal" ;;
        --to=flash)    TO_MODE="flash"    ;;
    esac
done

# ──────────────────────────────────────────────────────────────────────────────
# Boot device detection
detect_boot_storage() {
    local boot_part boot_disk transport
    boot_part=$(findmnt -n -o SOURCE /boot 2>/dev/null)
    boot_disk=$(lsblk -no pkname "$boot_part" 2>/dev/null)
    transport=$(lsblk -dno TRAN "/dev/$boot_disk" 2>/dev/null | tr '[:upper:]' '[:lower:]')
    echo "${transport:-unknown}"
}

# ──────────────────────────────────────────────────────────────────────────────
# Status
if [[ "$SHOW_STATUS" == true ]]; then
    TRANSPORT=$(detect_boot_storage)
    DETECTED=$([[ "$TRANSPORT" == "usb" ]] && echo "flash" || echo "internal")
    echo ""
    echo "━━━━━ $ICON_SUMMARY STORAGE STATUS ━━━━━"
    echo "$ICON_GEAR  SCRIPTS_DIR:  $SCRIPTS_DIR"
    echo "$ICON_GEAR  varaverk.cfg: $VV_CFG"
    echo "$ICON_HOST  Boot device:  transport=$TRANSPORT → detected=$DETECTED"
    echo "$ICON_GEAR  Target dirs:"
    echo "    internal: $INTERNAL_DIR"
    echo "    flash:    $FLASH_DIR"
    CONF_MODE=$(grep -m1 "${MY_ID}_STORAGE_MODE_INTERNAL" "$CONF_FILE" 2>/dev/null | cut -d= -f2 | tr -d '"' | tr -d '[:space:]')
    echo "$ICON_GEAR  conf setting: ${MY_ID}_STORAGE_MODE_INTERNAL=${CONF_MODE:-not set}"
    if [[ "$SCRIPTS_DIR" == "$INTERNAL_DIR" ]]; then
        echo "$ICON_DONE  Current mode: INTERNAL ✅"
    elif [[ "$SCRIPTS_DIR" == "$FLASH_DIR" ]]; then
        echo "$ICON_DONE  Current mode: FLASH ✅"
    else
        echo "$ICON_WARN  Current mode: CUSTOM ($SCRIPTS_DIR)"
    fi
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

# ──────────────────────────────────────────────────────────────────────────────
if [[ -z "$TO_MODE" ]]; then
    error "Usage: storage_migrate.sh --to=internal|flash [--dry-run] [--log]"
    exit 1
fi

SRC="$SCRIPTS_DIR"
DST=$([[ "$TO_MODE" == "internal" ]] && echo "$INTERNAL_DIR" || echo "$FLASH_DIR")
NEW_INTERNAL=$([[ "$TO_MODE" == "internal" ]] && echo "true" || echo "false")

log "$ICON_GEAR Config: to=${TO_MODE} src=${SRC} dst=${DST} dry-run=${DRY_RUN}"

echo ""
echo "━━━━━ $ICON_SYNC Storage Migration ━━━━━"
echo "$ICON_GEAR  From: $SRC"
echo "$ICON_GEAR  To:   $DST"
echo "$ICON_GEAR  Mode: $TO_MODE"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
echo ""

# ──────────────────────────────────────────────────────────────────────────────
# Pre-flight checks
if [[ "$SRC" == "$DST" ]]; then
    info "Already in $TO_MODE mode — nothing to do"
    exit 0
fi

if [[ "$TO_MODE" == "flash" ]]; then
    if ! mountpoint -q /mnt/user 2>/dev/null; then
        error "Array not started — /mnt/user is not mounted. Start the array before migrating to flash."
        exit 1
    fi
fi

if [[ ! -f "$SRC/load_config.sh" ]]; then
    error "Source directory looks invalid: $SRC (load_config.sh not found)"
    exit 1
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 1: Git push — ensure remote has everything before we touch the local repo
echo "━━━ $ICON_SYNC Step 1: Git push ━━━"
if [[ "$DRY_RUN" == false ]]; then
    if [[ -d "$SRC/.git" ]]; then
        log "Pushing to Gitea before migration..."
        if GIT_SSH_COMMAND="ssh -i $GITEA_SSH_KEY -p $SSH_PORT" \
           git -C "$SRC" push origin main 2>&1 | while IFS= read -r line; do echo "  $line"; done; then
            echo "  Git push complete ✅"
        else
            warn "Git push failed — continuing (data safe locally, push manually after migration)"
        fi
    else
        warn "No .git directory in $SRC — skipping push"
    fi
else
    warn "DRY RUN — would push $SRC to Gitea"
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 2: Rsync content to destination
echo ""
echo "━━━ $ICON_SYNC Step 2: Copy files ━━━"
if [[ "$DRY_RUN" == false ]]; then
    mkdir -p "$DST"
    echo "  rsync: $SRC/ → $DST/"
    if rsync -av --delete \
        --exclude='.git' \
        "$SRC/" "$DST/" 2>&1 | \
        grep -v "/$" | \
        while IFS= read -r line; do log "$line"; done; then
        echo "  Files copied ✅"
    else
        error "rsync failed — aborting migration"
        exit 1
    fi

    # Copy .git separately (rsync --exclude='.git' above skipped it)
    echo "  Copying .git..."
    if cp -a "$SRC/.git" "$DST/.git" 2>/dev/null || \
       rsync -a "$SRC/.git/" "$DST/.git/" 2>/dev/null; then
        echo "  .git copied ✅"
    else
        error ".git copy failed — aborting"
        exit 1
    fi

    # Mark git safe directory
    git config --global --add safe.directory "$DST" 2>/dev/null
else
    warn "DRY RUN — would rsync $SRC/ → $DST/ (including .git)"
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 3: Update varaverk.cfg
echo ""
echo "━━━ $ICON_GEAR Step 3: Update varaverk.cfg ━━━"
if [[ "$DRY_RUN" == false ]]; then
    if grep -q '^SCRIPTS_DIR' "$VV_CFG"; then
        sed -i "s|^SCRIPTS_DIR=.*|SCRIPTS_DIR=\"$DST\"|" "$VV_CFG"
    else
        echo "SCRIPTS_DIR=\"$DST\"" >> "$VV_CFG"
    fi
    echo "  SCRIPTS_DIR → $DST ✅"
else
    warn "DRY RUN — would set SCRIPTS_DIR=\"$DST\" in $VV_CFG"
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 4: Update TARGET_DIR, DATA_DIR, STATE_DIR in master.conf (new location)
echo ""
echo "━━━ $ICON_GEAR Step 4: Update master.conf paths ━━━"
NEW_MASTER="$DST/Configurations/master.conf"
if [[ "$DRY_RUN" == false ]]; then
    if [[ -f "$NEW_MASTER" ]]; then
        sed -i "s|^\(\s*TARGET_DIR\s*=\s*\)\"[^\"]*\"|\1\"$DST\"|"                              "$NEW_MASTER"
        sed -i "s|^\(\s*DATA_DIR\s*=\s*\)\"[^\"]*\"|\1\"$DST/data\"|"                           "$NEW_MASTER"
        sed -i "s|^\(\s*STATE_DIR\s*=\s*\)\"[^\"]*\"|\1\"$DST/State_Files\"|"                   "$NEW_MASTER"
        echo "  TARGET_DIR → $DST ✅"
        echo "  DATA_DIR   → $DST/data ✅"
        echo "  STATE_DIR  → $DST/State_Files ✅"
    else
        error "master.conf not found at $NEW_MASTER"
        exit 1
    fi
else
    warn "DRY RUN — would update TARGET_DIR, DATA_DIR, STATE_DIR in master.conf"
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 5: Update STORAGE_MODE_INTERNAL in host*.conf (new location)
echo ""
echo "━━━ $ICON_GEAR Step 5: Update STORAGE_MODE_INTERNAL ━━━"
NEW_CONF="$DST/Configurations/${MY_ID,,}.conf"
if [[ "$DRY_RUN" == false ]]; then
    if [[ -f "$NEW_CONF" ]]; then
        if grep -q "${MY_ID}_STORAGE_MODE_INTERNAL" "$NEW_CONF"; then
            sed -i "s|^\(\s*${MY_ID}_STORAGE_MODE_INTERNAL\s*=\s*\).*|\1${NEW_INTERNAL}|" "$NEW_CONF"
        else
            sed -i "/# ━━━ Storage mode/a\\    ${MY_ID}_STORAGE_MODE_INTERNAL=${NEW_INTERNAL}" "$NEW_CONF"
        fi
        echo "  ${MY_ID}_STORAGE_MODE_INTERNAL → $NEW_INTERNAL ✅"
    else
        warn "${MY_ID,,}.conf not found at $NEW_CONF — skipping conf update"
    fi
else
    warn "DRY RUN — would set ${MY_ID}_STORAGE_MODE_INTERNAL=$NEW_INTERNAL"
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 6: Rebuild cron (paths must reference new SCRIPTS_DIR)
echo ""
echo "━━━ $ICON_GEAR Step 6: Rebuild cron ━━━"
if [[ "$DRY_RUN" == false ]]; then
    RESULT=$(php -r "
        define('PLUGIN_CFG',  '/boot/config/plugins/varaverk/varaverk.cfg');
        \$_c = @parse_ini_file(PLUGIN_CFG) ?: [];
        define('SCRIPTS_DIR', \$_c['SCRIPTS_DIR'] ?? '/boot/config/plugins/varaverk');
        define('CONF_DIR',    SCRIPTS_DIR . '/Configurations');
        define('DATA_DIR',    SCRIPTS_DIR . '/data');
        define('STATE_DIR',   SCRIPTS_DIR . '/State_Files');
        define('LOG_DIR',     '/var/log/varaverk');
        define('VV_SETUP_STATE_FILE', STATE_DIR . '/varaverk_setup.db');
        require_once '/usr/local/emhttp/plugins/varaverk/include/confform.php';
        require_once '/usr/local/emhttp/plugins/varaverk/include/scheduler.php';
        \$ok = vv_cron_rebuild(vv_schedule_load());
        echo \$ok ? 'ok' : 'fail';
    " 2>/dev/null)
    if [[ "$RESULT" == "ok" ]]; then
        echo "  Cron rebuilt ✅"
    else
        warn "Cron rebuild failed — run Settings → Scheduler → Save to regenerate"
    fi
else
    warn "DRY RUN — would rebuild cron with new SCRIPTS_DIR paths"
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 7: Flash mode — sync Plugin/ to /boot/ so webUI is current
if [[ "$TO_MODE" == "flash" && "$DRY_RUN" == false ]]; then
    echo ""
    echo "━━━ $ICON_SYNC Step 7: Sync Plugin/ → /boot/ ━━━"
    if rsync -a --delete "$DST/Plugin/" "$INTERNAL_DIR/Plugin/" 2>/dev/null; then
        echo "  Plugin/ synced to /boot/ ✅"
    else
        warn "Plugin/ sync to /boot/ failed — webUI may be stale"
    fi
fi

# ──────────────────────────────────────────────────────────────────────────────
# Step 8: Delete old location
echo ""
echo "━━━ $ICON_GEAR Step $([[ "$TO_MODE" == "flash" ]] && echo 8 || echo 7): Clean up old location ━━━"
if [[ "$DRY_RUN" == false ]]; then
    if [[ "$SRC" == "$INTERNAL_DIR" ]]; then
        # Migrating internal→flash: keep varaverk.cfg and Plugin/ in /boot/, remove everything else
        echo "  Removing scripts/conf/state from /boot/ (keeping Plugin/ and varaverk.cfg)..."
        find "$SRC" -mindepth 1 -maxdepth 1 \
            ! -name 'Plugin' \
            ! -name 'varaverk.cfg' \
            ! -name '*.plg' \
            ! -name '*.txz' \
            -exec rm -rf {} + 2>/dev/null
        echo "  /boot/ cleaned ✅ (Plugin/ and varaverk.cfg preserved)"
    else
        # Migrating flash→internal: remove appdata copy entirely
        echo "  Removing $SRC..."
        rm -rf "$SRC"
        echo "  $SRC removed ✅"
    fi
else
    if [[ "$SRC" == "$INTERNAL_DIR" ]]; then
        warn "DRY RUN — would remove scripts/conf/state from /boot/ (keeping Plugin/ and varaverk.cfg)"
    else
        warn "DRY RUN — would remove $SRC"
    fi
fi

# ──────────────────────────────────────────────────────────────────────────────
echo ""
echo "━━━━━ $ICON_DONE Migration complete ━━━━━"
echo "$ICON_GEAR  Mode:       $TO_MODE"
echo "$ICON_GEAR  SCRIPTS_DIR: $DST"
if [[ "$TO_MODE" == "flash" ]]; then
    echo ""
    warn "IMPORTANT: Varaverk requires the array to be started to function in flash mode."
    warn "The webUI plugin tab will load normally at all times (Plugin/ stays in /boot/)."
fi
echo ""
echo "  Reload the Varaverk plugin tab to apply changes."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
