#!/bin/bash

# Apply a cross-host migrate. Wraps restoreAppStart with the migrate-specific
# scaffolding (pre-backup of destination, URL rewrite of host-bound CFG_*
# fields, structured progress) so the WebUI can drive it cleanly.
#
# Two entry points:
#   migrateApplyApp     <source_host> <app> [loc_idx] [opts...]
#   migrateApplySystem  <source_host>        [loc_idx] [opts...]
#
# Opts (any order, all defaults match the WebUI defaults):
#   --no-pre-backup        Skip the safety snapshot of destination's <app>
#   --keep-urls            Don't rewrite host-bound CFG_<APP>_* fields
#   --json-progress        Emit one JSON record per stage on fd 3 / stdout
#                          (sets MIGRATE_JSON_PROGRESS=1 for nested helpers)
#
# Exit: 0 on success, non-zero if the migrate failed at any stage.

# Strip our --foo opts out of the positional args and set MIGRATE_OPT_* flags.
# Mutates the caller's $1.. via printf-and-reread; returns the cleaned list
# via the global _MIGRATE_REMAINING_ARGS array.
_migrateParseOpts()
{
    MIGRATE_OPT_PRE_BACKUP=1
    MIGRATE_OPT_KEEP_URLS=0
    MIGRATE_OPT_JSON_PROGRESS=0
    _MIGRATE_REMAINING_ARGS=()

    local a
    for a in "$@"; do
        case "$a" in
            --no-pre-backup)   MIGRATE_OPT_PRE_BACKUP=0 ;;
            --keep-urls)       MIGRATE_OPT_KEEP_URLS=1 ;;
            --json-progress)   MIGRATE_OPT_JSON_PROGRESS=1; export MIGRATE_JSON_PROGRESS=1 ;;
            --)                : ;;  # arg separator, ignore
            *)                 _MIGRATE_REMAINING_ARGS+=("$a") ;;
        esac
    done
}

migrateApplyApp()
{
    _migrateParseOpts "$@"
    local source_host="${_MIGRATE_REMAINING_ARGS[0]}"
    local app="${_MIGRATE_REMAINING_ARGS[1]}"
    local idx="${_MIGRATE_REMAINING_ARGS[2]}"

    if [[ -z "$source_host" || -z "$app" ]]; then
        isError "migrateApplyApp: source_host and app required"
        return 1
    fi

    [[ -z "$idx" ]] && idx=$(resticEnabledLocations | head -1)
    if [[ -z "$idx" ]]; then
        isError "No backup locations enabled — cannot migrate"
        return 1
    fi

    local started_at
    started_at=$(date +%s)
    isHeader "Migrate $app from host=$source_host → this host"
    migrateEmit phase=start status=running app="$app" source_host="$source_host" loc_idx="$idx"

    # ---- 1. Preflight (informational; we already trust the caller) ------------
    migrateEmit phase=preflight status=running
    local pf
    pf=$(migratePreflight "$source_host" "$app" "$idx")
    local pf_rc=$?
    if (( pf_rc != 0 )); then
        isError "Preflight failed: $pf"
        migrateEmit phase=preflight status=failed detail="$pf"
        return 1
    fi
    migrateEmit phase=preflight status=complete

    # ---- 2. Pre-migrate backup of destination (default ON) --------------------
    if (( MIGRATE_OPT_PRE_BACKUP )); then
        migratePreBackupDestination "$app" "$idx"
    else
        isNotice "Pre-migrate backup skipped (--no-pre-backup)"
        migrateEmit phase=pre-backup status=skipped reason=user-opt-out app="$app"
    fi

    # ---- 3. The actual restore (reuses existing restoreAppStart) --------------
    # restoreAppStart already wipes the app folder, restores the snapshot,
    # re-runs the install-time tag pipeline, and starts the container. The
    # host_filter arg makes it pull the SOURCE's snapshot, not this host's.
    migrateEmit phase=restore status=running app="$app" source_host="$source_host"
    if ! restoreAppStart "$app" "latest" "$idx" "$source_host"; then
        isError "Restore stage failed for $app"
        migrateEmit phase=restore status=failed app="$app"
        return 1
    fi
    migrateEmit phase=restore status=complete app="$app"

    # ---- 4. URL rewrite (default ON) ------------------------------------------
    if (( MIGRATE_OPT_KEEP_URLS )); then
        isNotice "URL rewrite skipped (--keep-urls)"
        migrateEmit phase=url-rewrite status=skipped reason=user-opt-out app="$app"
    else
        migrateEmit phase=url-rewrite status=running app="$app"
        migrateApplyUrlRewrite "$app"
        # restoreAppStart already restarted the app once; if URL rewrite
        # changed anything, a quick re-deploy of the compose picks them up.
        # dockerComposeUpdateAndStartApp is idempotent (per the project's
        # idempotent-setup preference) so re-running is cheap.
        if declare -f dockerComposeUpdateAndStartApp >/dev/null 2>&1; then
            dockerComposeUpdateAndStartApp "$app" install >/dev/null 2>&1 || true
        fi
    fi

    local finished_at
    finished_at=$(date +%s)
    local duration=$((finished_at - started_at))
    isSuccessful "Migrate of $app from $source_host complete in ${duration}s"
    migrateEmit phase=done status=complete app="$app" duration_seconds="$duration"
    return 0
}

migrateApplySystem()
{
    _migrateParseOpts "$@"
    local source_host="${_MIGRATE_REMAINING_ARGS[0]}"
    local idx="${_MIGRATE_REMAINING_ARGS[1]}"

    if [[ -z "$source_host" ]]; then
        isError "migrateApplySystem: source_host required"
        return 1
    fi
    [[ -z "$idx" ]] && idx=$(resticEnabledLocations | head -1)
    if [[ -z "$idx" ]]; then
        isError "No backup locations enabled — cannot migrate"
        return 1
    fi

    isHeader "Migrate every app from host=$source_host → this host"
    migrateEmit phase=system-start status=running source_host="$source_host" loc_idx="$idx"

    local apps=()
    while IFS= read -r a; do
        [[ -n "$a" ]] && apps+=("$a")
    done < <(migrateDiscoverApps "$source_host" "$idx")

    if (( ${#apps[@]} == 0 )); then
        isError "No apps found for host=$source_host in $(resticLocationName "$idx")"
        migrateEmit phase=system-start status=failed reason=no-apps
        return 1
    fi

    isNotice "Apps to migrate: ${apps[*]}"
    migrateEmit phase=system-plan status=info apps_count="${#apps[@]}"

    # Forward the same opts to each per-app run. Note we re-pass the parsed
    # flags as literal --foo so each migrateApplyApp re-parses cleanly.
    local forwarded=()
    (( MIGRATE_OPT_PRE_BACKUP == 0 )) && forwarded+=(--no-pre-backup)
    (( MIGRATE_OPT_KEEP_URLS == 1 )) && forwarded+=(--keep-urls)
    (( MIGRATE_OPT_JSON_PROGRESS == 1 )) && forwarded+=(--json-progress)

    local ok=0 fail=0
    local a
    for a in "${apps[@]}"; do
        if migrateApplyApp "${forwarded[@]}" "$source_host" "$a" "$idx"; then
            ((ok++))
        else
            ((fail++))
        fi
    done

    isSuccessful "System migrate finished: $ok ok / $fail failed"
    migrateEmit phase=system-done status=complete ok="$ok" failed="$fail"
    (( fail == 0 ))
}

# ---- Backwards-compat shims ---------------------------------------------------
# Pre-existing callers (cli_restore_commands.sh, restoreFirstRunBulk, etc.)
# used the old names with the old arg order. Forward to the new functions.
migrateApp()
{
    # Old signature: migrateApp <app_name> <source_host> [idx]
    local app="$1"; local source_host="$2"; local idx="$3"
    migrateApplyApp "$source_host" "$app" "$idx"
}

migrateSystem()
{
    # Old signature: migrateSystem <source_host> [idx]
    migrateApplySystem "$@"
}
