#!/bin/bash

# Preflight for a cross-host migrate. Read-only: probes the destination, asks
# the backup location what it knows about the source/app, and produces a JSON
# object the WebUI (or a thoughtful CLI user) consumes before pressing "go."
#
# Args: <source_host> <app> [loc_idx]
#
# Output (single JSON object, one line):
#   {
#     "source_host":      "homelab",
#     "app":              "linkding",
#     "loc_idx":          1,
#     "snapshot":         {"id":"abc12345","date":"2026-05-26T10:30:00Z"} | null,
#     "destination": {
#       "app_installed":  true,
#       "app_running":    true,
#       "disk_free_kb":   50000000
#     },
#     "collision": {
#       "occurs":              true,
#       "default_action":      "replace",
#       "pre_backup_default":  true
#     },
#     "url_rewrite": {
#       "default_action":  "rewrite" | "keep",
#       "per_app_opt_out": false
#     },
#     "warnings": [...],
#     "errors":   [...]
#   }
#
# Exit codes: 0 on a usable preflight (even if warnings); 1 if the migrate
# can't proceed at all (no location, no snapshot, etc.) — errors[] is filled.

# Append a string to a JSON-array variable held by name. Used by migratePreflight
# to grow its warnings[]/errors[] without dragging in jq. Defined at file scope
# because bash doesn't allow `local funcname() {}` nested inside another function.
_migratePreflightAppend()
{
    local __varname="$1"
    local msg="$2"
    local escaped="${msg//\\/\\\\}"
    escaped="${escaped//\"/\\\"}"
    local current="${!__varname}"
    if [[ "$current" == "[]" ]]; then
        printf -v "$__varname" '[%s]' "\"$escaped\""
    else
        printf -v "$__varname" '%s,%s]' "${current%]}" "\"$escaped\""
    fi
}

migratePreflight()
{
    local source_host="$1"
    local app="$2"
    local idx="$3"

    local warnings='[]'
    local errors='[]'

    if [[ -z "$source_host" || -z "$app" ]]; then
        _migratePreflightAppend errors "source_host and app required"
        printf '{"source_host":null,"app":null,"loc_idx":null,"snapshot":null,"destination":null,"collision":null,"url_rewrite":null,"warnings":%s,"errors":%s}\n' \
            "$warnings" "$errors"
        return 1
    fi

    if [[ -z "$idx" ]]; then
        idx=$(resticEnabledLocations | head -1)
    fi
    if [[ -z "$idx" ]]; then
        _migratePreflightAppend errors "No backup locations enabled — nowhere to pull from"
        printf '{"source_host":"%s","app":"%s","loc_idx":null,"snapshot":null,"destination":null,"collision":null,"url_rewrite":null,"warnings":%s,"errors":%s}\n' \
            "$source_host" "$app" "$warnings" "$errors"
        return 1
    fi

    # ---- Snapshot probe -------------------------------------------------------
    local snap_id="" snap_date=""
    local snap_json
    snap_json=$(engineSnapshotsJson "$idx" "$app" "$source_host" 2>/dev/null)
    if [[ -n "$snap_json" && "$snap_json" != "[]" ]]; then
        snap_id=$(printf '%s' "$snap_json" | grep -o '"short_id":"[^"]*"' | tail -1 | cut -d'"' -f4)
        snap_date=$(printf '%s' "$snap_json" | grep -o '"time":"[^"]*"' | tail -1 | cut -d'"' -f4)
    fi
    local snapshot_json
    if [[ -n "$snap_id" ]]; then
        snapshot_json="{\"id\":\"$snap_id\",\"date\":\"$snap_date\"}"
    else
        snapshot_json="null"
        _migratePreflightAppend errors "No snapshots found for app=$app on host=$source_host in $(resticLocationName "$idx")"
    fi

    # ---- Destination probe ----------------------------------------------------
    local app_installed=false
    local app_running=false
    if [[ -d "$containers_dir$app" ]]; then
        app_installed=true
        if dockerCommandRun "docker ps --filter name=^${app}\$ --format '{{.Names}}' 2>/dev/null" | grep -q "^$app\$"; then
            app_running=true
        fi
    fi

    # df -P on the containers root, fourth col = free 1K-blocks. Best-effort.
    local disk_free_kb
    disk_free_kb=$(df -Pk "$containers_dir" 2>/dev/null | awk 'NR==2 {print $4}')
    [[ -z "$disk_free_kb" ]] && disk_free_kb=0

    local dest_json
    dest_json="{\"app_installed\":$app_installed,\"app_running\":$app_running,\"disk_free_kb\":$disk_free_kb}"

    # ---- Collision policy (we always replace; pre-backup defaults ON) ---------
    local pre_backup_default=true
    [[ "$app_installed" == "false" ]] && pre_backup_default=false   # nothing to back up
    local collision_json
    collision_json="{\"occurs\":$app_installed,\"default_action\":\"replace\",\"pre_backup_default\":$pre_backup_default}"

    # ---- URL rewrite policy ---------------------------------------------------
    local url_default="rewrite"
    local opt_out=false
    if [[ "$(migrateUrlRewriteEnabled "$app")" == "false" ]]; then
        url_default="keep"
        opt_out=true
    fi
    local url_json
    url_json="{\"default_action\":\"$url_default\",\"per_app_opt_out\":$opt_out}"

    # ---- Soft warnings --------------------------------------------------------
    # Disk free below 2× the destination's existing app folder is concerning.
    if [[ "$app_installed" == "true" ]]; then
        local existing_kb
        existing_kb=$(du -sk "$containers_dir$app" 2>/dev/null | awk '{print $1}')
        if [[ -n "$existing_kb" && "$disk_free_kb" -lt $((existing_kb * 2)) ]]; then
            _migratePreflightAppend warnings "Disk free ($((disk_free_kb / 1024)) MB) is less than 2× existing $app size ($((existing_kb / 1024)) MB)"
        fi
    fi
    if [[ "$app_running" == "true" ]]; then
        _migratePreflightAppend warnings "$app is currently running on this host; migrate will stop it"
    fi

    printf '{"source_host":"%s","app":"%s","loc_idx":%s,"snapshot":%s,"destination":%s,"collision":%s,"url_rewrite":%s,"warnings":%s,"errors":%s}\n' \
        "$source_host" "$app" "$idx" "$snapshot_json" "$dest_json" "$collision_json" "$url_json" \
        "$warnings" "$errors"

    [[ "$errors" == "[]" ]] && return 0 || return 1
}
