#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

STEP_ID="STEP_050M07R15A_LEGACY_EXECUTION_FREEZE_AND_CLEAN_FOUNDATION_R01"
BUNDLE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUNDLE_ZIP="${BUNDLE_ZIP:-}"
EXPECTED_BUNDLE_SHA256="${EXPECTED_BUNDLE_SHA256:-}"
EXPECTED_INSTALLER_SHA256="${EXPECTED_INSTALLER_SHA256:-}"
PUBLISHER="/opt/router-ops/bin/router-publish-report"
EXPECTED_PUBLISHER_SHA256="e78430b57afbd0ae27fc575fc4de78bb267c76caf04ec935b01e9b2f89840e32"
TARGET_ARCHIVE_SHA256="ee7805cfcff7208fdfd8062d06ca457414b85c7ff15fc46e76c0c5a12d68a6c0"
EXPECTED_BEFORE_COMMIT="0a6998cfead6a1e8b820cce9c00171dd6e81c6a1"
EXPECTED_BEFORE_TREE="b2646e97025823532daa549235e1643f75f11c4e"
EXPECTED_BEFORE_COUNT=47
EXPECTED_AFTER_COUNT=29
PVE_ALIAS="pve-mgts"
VM101_IP="10.71.100.2"
PVE_KEY="/root/.ssh/pve_to_openwrt_mgts_ed25519"
PERMANENT_ROOT="https://reports.secret-studio.ru"
RESOLVE_ARG="reports.secret-studio.ru:443:10.71.100.84"
GLOBAL_PROJECT_PLAN_URL="https://reports.secret-studio.ru/latest/20260711-123348_global_project_plan_wg_paid/"
ACCESS_MAP_URL="https://reports.secret-studio.ru/latest/20260715-210236_access_map_canonical_current/"
VM100_PUBLIC_URL="https://reports.secret-studio.ru/latest/20260715-190653_vm100_git_source_1958200ccc1b/"
VM121_PUBLIC_URL="https://reports.secret-studio.ru/latest/20260713-182700_vm121_git_source_93e0e5a31f09/"
VM130_SOURCE_URL="https://reports.secret-studio.ru/latest/20260716-064558_vm130_router_ops_source_snapshot_r14b2/"
LATEST_ROOT_URL="${PERMANENT_ROOT}/latest/"
OLD_ROOT_URL="${PERMANENT_ROOT}/old/"

if [[ "$(id -un)" != "ops" ]]; then
    echo "RESULT=STOP_MUST_RUN_AS_OPS"
    echo "COPY_VERIFY=FAIL"
    echo "STEP_WAS_NOT_STARTED=true"
    exit 1
fi

TS="$(date -u +%Y%m%d-%H%M%S)"
STEP_BASE="/opt/router-ops/steps"
[[ -d "$STEP_BASE" && -w "$STEP_BASE" ]] || STEP_BASE="$HOME/router-ops-steps"
STEP_DIR="$STEP_BASE/${TS}_${STEP_ID}"
mkdir -p "$STEP_DIR"
chmod 700 "$STEP_DIR"

REPORT="$STEP_DIR/report.txt"
FACTS="$STEP_DIR/facts.json"
VERIFY="$STEP_DIR/verify.txt"
MAIN_LOG="$STEP_DIR/main.log"
TRANSFER_VERIFY="$STEP_DIR/transfer-verify.txt"
ROLLBACK_LOCAL="$STEP_DIR/rollback.sh"
MACHINE_LOG="$STEP_DIR/machine-close.txt"
MODEL_FILE="$STEP_DIR/vm101-model.md"
METHODS_FILE="$STEP_DIR/vm101-methods.md"
PLAN_FILE="$STEP_DIR/canonical-m07-plan.md"
ACCESS_FILE="$STEP_DIR/access-routes.md"
TARGET_BACKUP="/root/router-ops-backups/${TS}_${STEP_ID}"
TARGET_TRANSFER="/root/router-ops-transfers/${TS}_${STEP_ID}"
TARGET_ROLLBACK="$TARGET_BACKUP/rollback.sh"

RESULT="STOP_UNCLASSIFIED"
COPY_VERIFY="FAIL"
STEP_WAS_NOT_STARTED="true"
RUNTIME_IMPACT="false"
INSTALL_STARTED="false"
MACHINE_GIT_STARTED="false"
AUTO_ROLLBACK="NOT_NEEDED"
COMMIT=""
TREE=""
GIT_FILE_COUNT=""
VM101_PUBLIC_URL=""
VM101_MODEL_URL=""
VM101_METHODS_URL=""
LOCAL_M07_PLAN_URL=""
STEP_REPORT_URL=""
PUBLIC_OBJECT_KIND=""
PREVIOUS_MOVED_COUNT=""

exec > >(tee -a "$MAIN_LOG") 2>&1

progress() {
    printf '>>> [%s] %s\n' "$(date -u '+%H:%M:%S')" "$*"
}

remote_script() {
    ssh "$PVE_ALIAS" "ssh -i '$PVE_KEY' root@'$VM101_IP' 'sh -s'"
}

remote_capture() {
    printf '%s\n' "$1" | remote_script
}

remote_put() {
    local source="$1"
    local destination="$2"
    ssh "$PVE_ALIAS" \
        "ssh -i '$PVE_KEY' root@'$VM101_IP' 'cat > \"$destination\"'" \
        <"$source"
}

extract_var() {
    local log="$1"
    local key="$2"
    grep -E "^${key}=" "$log" 2>/dev/null | tail -n1 | cut -d= -f2- || true
}

publish_quiet() {
    local label="$1"
    local log="$2"
    shift 2
    "$PUBLISHER" "$label" "$@" >"$log" 2>&1
    progress "published $label: PASS"
}

http_get() {
    local url="$1"
    local i
    for i in 1 2 3 4 5; do
        curl -fsS \
            --resolve "$RESOLVE_ARG" \
            --connect-timeout 5 \
            --max-time 25 \
            "$url" -o /dev/null && return 0
        sleep 2
    done
    return 1
}

write_artifacts() {
    local step_sha
    step_sha="$(sha256sum "$STEP_DIR/step-code.sh" 2>/dev/null | awk '{print $1}')"

    cat >"$REPORT" <<EOF
RESULT=$RESULT
STEP_ID=$STEP_ID
TIMESTAMP_UTC=$TS

PURPOSE:
Freeze and remove VM101 legacy execution paths and establish one clean five-slot LOCAL_REPAIR foundation.

BEFORE:
- Machine Git commit $EXPECTED_BEFORE_COMMIT, tree $EXPECTED_BEFORE_TREE, 47 files.
- Old table-200/two-slot refresh, rebalance, emergency, validator, cron and state paths remained present.

AFTER:
- One production loop: health watcher -> dispatcher -> LOCAL_REPAIR.
- HMN provider boundary: download -> low-level vpn_test -> unified-quarantine ranker.
- Unified state under /var/lib/router-egress-recovery.
- Table 200 and legacy entry points absent.
- Machine Git expected file count: 29.

ACTUAL:
COMMIT=$COMMIT
TREE=$TREE
GIT_FILE_COUNT=$GIT_FILE_COUNT
AUTO_ROLLBACK=$AUTO_ROLLBACK
RUNTIME_IMPACT=$RUNTIME_IMPACT
VM101_PUBLIC_URL=$VM101_PUBLIC_URL
VM101_MODEL_URL=$VM101_MODEL_URL
VM101_METHODS_URL=$VM101_METHODS_URL
LOCAL_M07_PLAN_URL=$LOCAL_M07_PLAN_URL

RUNTIME:
- health-repair watcher stopped and restarted;
- legacy services disabled and removed;
- table 200 routes/rules flushed;
- vpn_test left down;
- vpn1..vpn5 endpoints must remain byte-for-byte unchanged.

ROLLBACK:
TARGET_BACKUP=$TARGET_BACKUP
TARGET_ROLLBACK=$TARGET_ROLLBACK
LOCAL_ROLLBACK=$ROLLBACK_LOCAL

LIMITATIONS:
FULL_POOL_REFRESH, DEGRADED_POOL, consolidation, Direct emergency and bootstrap recovery remain disabled and future work.
EOF

    python3 - "$FACTS" <<PY
import json
facts = {
  "schema": "router-step-facts-v1",
  "step": "$STEP_ID",
  "result": "$RESULT",
  "timestamp_utc": "$TS",
  "copy_verify": "$COPY_VERIFY",
  "runtime_impact": "$RUNTIME_IMPACT" == "true",
  "auto_rollback": "$AUTO_ROLLBACK",
  "before": {
    "commit": "$EXPECTED_BEFORE_COMMIT",
    "tree": "$EXPECTED_BEFORE_TREE",
    "file_count": $EXPECTED_BEFORE_COUNT
  },
  "after": {
    "commit": "$COMMIT",
    "tree": "$TREE",
    "file_count": "$GIT_FILE_COUNT"
  },
  "urls": {
    "vm101": "$VM101_PUBLIC_URL",
    "model": "$VM101_MODEL_URL",
    "methods": "$VM101_METHODS_URL",
    "local_plan": "$LOCAL_M07_PLAN_URL"
  },
  "step_sha256": "$step_sha",
  "target_backup": "$TARGET_BACKUP",
  "target_rollback": "$TARGET_ROLLBACK"
}
with open("$FACTS", "w", encoding="utf-8") as fh:
    json.dump(facts, fh, indent=2)
PY
}

publish_step_report() {
    local stage="/tmp/router-step-publish-${TS}-$$"
    local log="$STEP_DIR/step-publish.txt"
    local rel
    local source

    rm -rf "$stage"
    mkdir -p "$stage"

    for source in \
        "$REPORT" "$FACTS" "$VERIFY" "$TRANSFER_VERIFY" \
        "$ROLLBACK_LOCAL" "$MAIN_LOG" "$MACHINE_LOG" \
        "$MODEL_FILE" "$METHODS_FILE" "$PLAN_FILE" "$ACCESS_FILE" \
        "$STEP_DIR/step-code.sh" \
        "$BUNDLE_ROOT/managed-paths.txt" \
        "$BUNDLE_ROOT/delete-list.txt" \
        "$BUNDLE_ROOT/dynamic-delete-spec.txt" \
        "$BUNDLE_ROOT/payload-manifest.tsv" \
        "$STEP_DIR/fixture-tests.txt" \
        "$STEP_DIR/target-transfer-verify.txt" \
        "$STEP_DIR/target-install.txt"
    do
        [[ -f "$source" ]] || continue
        cp -a "$source" "$stage/$(basename "$source")"
        [[ "$(sha256sum "$source" | awk '{print $1}')" == \
           "$(sha256sum "$stage/$(basename "$source")" | awk '{print $1}')" ]] || return 1
    done

    publish_quiet \
        "step050m07r15a_legacy_execution_freeze_clean_foundation" \
        "$log" \
        "$stage"/* || return 1

    STEP_REPORT_URL="$(extract_var "$log" STEP_REPORT_URL)"
    if [[ "$STEP_REPORT_URL" != "${PERMANENT_ROOT}/latest/"*/ ]]; then
        rel="$(extract_var "$log" PUBLISHED_RELATIVE_PATH)"
        [[ -n "$rel" ]] && STEP_REPORT_URL="${PERMANENT_ROOT}/${rel}/"
    fi
    PUBLIC_OBJECT_KIND="$(extract_var "$log" PUBLIC_OBJECT_KIND)"
    PREVIOUS_MOVED_COUNT="$(extract_var "$log" PREVIOUS_MOVED_COUNT)"
    [[ "$STEP_REPORT_URL" == "${PERMANENT_ROOT}/latest/"*/ ]]
}

finalize() {
    set +e
    write_artifacts

    if publish_step_report; then
        if ! http_get "$STEP_REPORT_URL"; then
            [[ "$RESULT" == PASS_* ]] && RESULT="STOP_STEP_REPORT_HTTP_FAILED"
            write_artifacts
        fi
    else
        [[ "$RESULT" == PASS_* ]] && RESULT="STOP_STEP_REPORT_PUBLICATION_FAILED"
        write_artifacts
    fi

    trap - EXIT
    local step_sha
    step_sha="$(sha256sum "$STEP_DIR/step-code.sh" 2>/dev/null | awk '{print $1}')"

    printf '%s\n' \
        "RESULT=$RESULT" \
        "STEP_REPORT_URL=$STEP_REPORT_URL" \
        "LOCAL_M07_PLAN_URL=$LOCAL_M07_PLAN_URL" \
        "GLOBAL_PROJECT_PLAN_URL=$GLOBAL_PROJECT_PLAN_URL" \
        "ACCESS_MAP_URL=$ACCESS_MAP_URL" \
        "VM100_PUBLIC_URL=$VM100_PUBLIC_URL" \
        "VM101_PUBLIC_URL=$VM101_PUBLIC_URL" \
        "VM121_PUBLIC_URL=$VM121_PUBLIC_URL" \
        "VM101_MODEL_URL=$VM101_MODEL_URL" \
        "VM101_METHODS_URL=$VM101_METHODS_URL" \
        "VM130_SOURCE_URL=$VM130_SOURCE_URL" \
        "LATEST_ROOT_URL=$LATEST_ROOT_URL" \
        "OLD_ROOT_URL=$OLD_ROOT_URL" \
        "FACTS_URL=${STEP_REPORT_URL}facts.json" \
        "VERIFY_URL=${STEP_REPORT_URL}verify.txt" \
        "ROLLBACK_URL=${STEP_REPORT_URL}rollback.sh" \
        "PUBLIC_OBJECT_KIND=$PUBLIC_OBJECT_KIND" \
        "PREVIOUS_MOVED_COUNT=$PREVIOUS_MOVED_COUNT" \
        "COPY_VERIFY=$COPY_VERIFY" \
        "STEP_SHA256=$step_sha" \
        "RUNTIME_IMPACT=$RUNTIME_IMPACT"

    [[ "$RESULT" == PASS_* ]]
}

fail() {
    local result="$1"
    local reason="${2:-unknown}"
    RESULT="$result"
    printf 'STOP_REASON=%s\n' "$reason" >>"$VERIFY"

    if [[ "$INSTALL_STARTED" == "true" && "$MACHINE_GIT_STARTED" == "false" ]]; then
        set +e
        remote_capture "[ -x '$TARGET_ROLLBACK' ] && '$TARGET_ROLLBACK'" \
            >"$STEP_DIR/auto-rollback.txt" 2>&1
        if [[ $? -eq 0 ]]; then
            AUTO_ROLLBACK="PASS"
        else
            AUTO_ROLLBACK="FAIL"
            RESULT="STOP_AUTO_ROLLBACK_FAILED"
        fi
        set -e
    fi

    finalize
    exit 1
}

trap 'rc=$?; if [[ $rc -ne 0 && "$RESULT" == "STOP_UNCLASSIFIED" ]]; then RESULT="STOP_UNHANDLED_INSTALLER_ERROR"; finalize; fi' EXIT

progress "verify bundle, code, manifests and offline fixtures"
cp -a "$BUNDLE_ROOT/install.sh" "$STEP_DIR/step-code.sh"
chmod 700 "$STEP_DIR/step-code.sh"

[[ -n "$BUNDLE_ZIP" && -f "$BUNDLE_ZIP" ]] || fail "STOP_BUNDLE_INPUT_MISSING" "bundle"
[[ -n "$EXPECTED_BUNDLE_SHA256" ]] || fail "STOP_EXPECTED_BUNDLE_SHA_MISSING" "bundle"
[[ "$(sha256sum "$BUNDLE_ZIP" | awk '{print $1}')" == "$EXPECTED_BUNDLE_SHA256" ]] || \
    fail "STOP_COPY_OR_TRANSFER_ERROR" "zip_sha"
[[ -z "$EXPECTED_INSTALLER_SHA256" || \
   "$(sha256sum "$BUNDLE_ROOT/install.sh" | awk '{print $1}')" == "$EXPECTED_INSTALLER_SHA256" ]] || \
    fail "STOP_COPY_OR_TRANSFER_ERROR" "installer_sha"

(
    cd "$BUNDLE_ROOT"
    sha256sum -c manifest.sha256
) >"$STEP_DIR/manifest-check.txt" 2>&1 || fail "STOP_COPY_OR_TRANSFER_ERROR" "internal_manifest"

bash -n "$BUNDLE_ROOT/install.sh"
bash "$BUNDLE_ROOT/tests/run-fixture-tests.sh" >"$STEP_DIR/fixture-tests.txt"
grep -q '^FIXTURE_TEST_RESULT=PASS$' "$STEP_DIR/fixture-tests.txt" || \
    fail "STOP_FIXTURE_TEST_FAILED" "fixture"

[[ "$(sha256sum "$BUNDLE_ROOT/target-step.tar.gz" | awk '{print $1}')" == "$TARGET_ARCHIVE_SHA256" ]] || \
    fail "STOP_COPY_OR_TRANSFER_ERROR" "target_gzip_sha"
gzip -t "$BUNDLE_ROOT/target-step.tar.gz"
base64 -d "$BUNDLE_ROOT/target-step.tar.gz.b64.txt" >"$STEP_DIR/decoded-target.tar.gz"
cmp "$BUNDLE_ROOT/target-step.tar.gz" "$STEP_DIR/decoded-target.tar.gz" || \
    fail "STOP_COPY_OR_TRANSFER_ERROR" "base64_roundtrip"

COPY_VERIFY="PASS"
STEP_WAS_NOT_STARTED="false"
cat >"$TRANSFER_VERIFY" <<EOF
COPY_VERIFY=PASS
BUNDLE_SHA256=$EXPECTED_BUNDLE_SHA256
TARGET_GZIP_SHA256=$TARGET_ARCHIVE_SHA256
BASE64_DECODE_COMPARE=PASS
GZIP_TEST=PASS
OFFLINE_FIXTURE=PASS
EOF

[[ -x "$PUBLISHER" ]] || fail "STOP_PUBLISHER_MISSING" "$PUBLISHER"
[[ "$(sha256sum "$PUBLISHER" | awk '{print $1}')" == "$EXPECTED_PUBLISHER_SHA256" ]] || \
    fail "STOP_PUBLISHER_SHA256_MISMATCH" "$(sha256sum "$PUBLISHER" | awk '{print $1}')"
sh -n "$PUBLISHER" || fail "STOP_PUBLISHER_SYNTAX_INVALID" "$PUBLISHER"

progress "preflight exact VM101 source and approved access route"
ssh -G "$PVE_ALIAS" >/dev/null 2>&1 || fail "STOP_OPS_SSH_ALIAS_MISSING" "$PVE_ALIAS"
{
    printf 'EXPECTED_COMMIT=%q\n' "$EXPECTED_BEFORE_COMMIT"
    printf 'EXPECTED_TREE=%q\n' "$EXPECTED_BEFORE_TREE"
    printf 'EXPECTED_COUNT=%q\n' "$EXPECTED_BEFORE_COUNT"
    cat <<'REMOTE'
set -eu
commit="$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse HEAD)"
tree="$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse 'HEAD^{tree}')"
count="$(git --git-dir=/root/.vm101-source.git --work-tree=/ ls-tree -r --name-only HEAD | wc -l | tr -d ' ')"
echo "BEFORE_COMMIT=$commit"
echo "BEFORE_TREE=$tree"
echo "BEFORE_COUNT=$count"
test "$commit" = "$EXPECTED_COMMIT"
test "$tree" = "$EXPECTED_TREE"
test "$count" = "$EXPECTED_COUNT"
test -x /usr/local/sbin/router-vm101-git-publish.sh
REMOTE
} | remote_script >"$STEP_DIR/target-preflight.txt" || \
    fail "STOP_VM101_EXACT_PREFLIGHT_FAILED" "source_contract"

INSTALL_STARTED="true"
RUNTIME_IMPACT="true"

progress "backup files, state, services, endpoints and legacy routing"
{
    printf 'B=%q\n' "$TARGET_BACKUP"
    cat <<'REMOTE'
set -eu
rm -rf "$B"
mkdir -p "$B/files"
chmod 700 "$B"

for svc in router-egress-health-repair router-egress-emergency-decision router-egress-slot-health; do
    [ -x "/etc/init.d/$svc" ] || continue
    /etc/init.d/$svc status >/dev/null 2>&1 && running=1 || running=0
    find /etc/rc.d -maxdepth 1 -type l -name "*$svc" 2>/dev/null | grep -q . && enabled=1 || enabled=0
    echo "$svc $running $enabled" >>"$B/services.before"
done

for i in 1 2 3 4 5; do
    echo "vpn$i=$(uci -q get network.vpn$i.hmn_endpoint || true)"
done >"$B/endpoints.before"

ip route show table 200 >"$B/table200.routes" 2>/dev/null || true
ip rule show | grep -E 'lookup[[:space:]]+200([[:space:]]|$)' >"$B/table200.rules" 2>/dev/null || true
cp -a /var/lib/router-egress-recovery "$B/recovery-state" 2>/dev/null || true
REMOTE
} | remote_script >"$STEP_DIR/backup-base.txt" || fail "STOP_VM101_BACKUP_FAILED" "base"

cut -f1 "$BUNDLE_ROOT/payload-manifest.tsv" | sed 's#^#/#' >"$STEP_DIR/paths-to-backup.txt"
cat "$BUNDLE_ROOT/delete-list.txt" >>"$STEP_DIR/paths-to-backup.txt"
printf '%s\n' \
    /root/hmn/hmn-download-all-awg.sh \
    /root/hmn/hmn-code-test.sh \
    /root/hmn/hmn-load-vpn-test.sh \
    /root/hmn/hmn-test-all-awg.sh \
    >>"$STEP_DIR/paths-to-backup.txt"
sort -u -o "$STEP_DIR/paths-to-backup.txt" "$STEP_DIR/paths-to-backup.txt"
remote_put "$STEP_DIR/paths-to-backup.txt" "$TARGET_BACKUP/paths-to-backup.txt"

{
    printf 'B=%q\n' "$TARGET_BACKUP"
    cat <<'REMOTE'
set -eu
while IFS= read -r path; do
    [ -n "$path" ] || continue
    if [ -e "$path" ] || [ -L "$path" ]; then
        mkdir -p "$B/files$(dirname "$path")"
        cp -a "$path" "$B/files$path"
        echo "$path" >>"$B/existed-before.txt"
    else
        echo "$path" >>"$B/missing-before.txt"
    fi
done <"$B/paths-to-backup.txt"
REMOTE
} | remote_script >"$STEP_DIR/backup-paths.txt" || fail "STOP_VM101_BACKUP_FAILED" "paths"

progress "create and install rollback before changing VM101"
cat >"$STEP_DIR/target-rollback.sh" <<EOF
#!/bin/sh
set -eu
B='$TARGET_BACKUP'
/etc/init.d/router-egress-health-repair stop >/dev/null 2>&1 || true

if [ -f "\$B/existed-before.txt" ]; then
    while IFS= read -r path; do
        [ -n "\$path" ] || continue
        if [ -e "\$B/files\$path" ] || [ -L "\$B/files\$path" ]; then
            mkdir -p "\$(dirname "\$path")"
            rm -rf "\$path"
            cp -a "\$B/files\$path" "\$path"
        fi
    done <"\$B/existed-before.txt"
fi

if [ -f "\$B/missing-before.txt" ]; then
    while IFS= read -r path; do
        [ -n "\$path" ] && rm -rf "\$path"
    done <"\$B/missing-before.txt"
fi

rm -rf /var/lib/router-egress-recovery
[ ! -d "\$B/recovery-state" ] || cp -a "\$B/recovery-state" /var/lib/router-egress-recovery

ip route flush table 200 2>/dev/null || true
while IFS= read -r line; do
    [ -n "\$line" ] && ip route add table 200 \$line 2>/dev/null || true
done <"\$B/table200.routes"
while IFS= read -r line; do
    pref="\$(printf '%s' "\$line" | cut -d: -f1)"
    spec="\$(printf '%s' "\$line" | cut -d: -f2-)"
    ip rule add pref "\$pref" \$spec 2>/dev/null || true
done <"\$B/table200.rules"

/etc/init.d/router-egress-slots start >/dev/null 2>&1 || true
if [ -f "\$B/services.before" ]; then
    while read -r svc running enabled; do
        [ -x "/etc/init.d/\$svc" ] || continue
        if [ "\$enabled" = 1 ]; then /etc/init.d/\$svc enable; else /etc/init.d/\$svc disable >/dev/null 2>&1 || true; fi
        if [ "\$running" = 1 ]; then /etc/init.d/\$svc start; else /etc/init.d/\$svc stop >/dev/null 2>&1 || true; fi
    done <"\$B/services.before"
fi

echo RESULT=PASS_R15A_TARGET_ROLLBACK
EOF
chmod 700 "$STEP_DIR/target-rollback.sh"
sh -n "$STEP_DIR/target-rollback.sh"
remote_put "$STEP_DIR/target-rollback.sh" "$TARGET_ROLLBACK"
remote_capture "chmod 700 '$TARGET_ROLLBACK'; sh -n '$TARGET_ROLLBACK'" >/dev/null

cat >"$ROLLBACK_LOCAL" <<EOF
#!/usr/bin/env bash
set -Eeuo pipefail
[[ "\$(id -un)" == "ops" ]] || { echo RESULT=STOP_MUST_RUN_AS_OPS; exit 1; }
ssh pve-mgts "ssh -i '$PVE_KEY' root@'$VM101_IP' '$TARGET_ROLLBACK'"
/opt/router-ops/bin/router-machine-close vm101 "$STEP_ID: rollback" >"\$HOME/r15a-rollback-machine-close.log" 2>&1
echo RESULT=PASS_R15A_ROLLBACK_AND_MACHINE_GIT_CLOSE
EOF
chmod 700 "$ROLLBACK_LOCAL"

progress "transfer Base64, gzip and extracted STEP separately; verify before install"
remote_capture "rm -rf '$TARGET_TRANSFER'; mkdir -p '$TARGET_TRANSFER'; chmod 700 '$TARGET_TRANSFER'" >/dev/null
remote_put "$BUNDLE_ROOT/target-step.tar.gz.b64.txt" "$TARGET_TRANSFER/target-step.tar.gz.b64.txt"
{
    printf 'TRANSFER=%q\n' "$TARGET_TRANSFER"
    printf 'EXPECTED_SHA=%q\n' "$TARGET_ARCHIVE_SHA256"
    cat <<'REMOTE'
set -eu
cd "$TRANSFER"
base64 -d target-step.tar.gz.b64.txt >target-step.tar.gz
test "$(sha256sum target-step.tar.gz | awk '{print $1}')" = "$EXPECTED_SHA"
gzip -t target-step.tar.gz
mkdir extracted
tar -xzf target-step.tar.gz -C extracted
cd extracted/target-step
sha256sum -c target-manifest.sha256
while IFS="$(printf '\t')" read -r relative mode expected; do
    [ -n "$relative" ] || continue
    test "$(sha256sum "payload/rootfs/$relative" | awk '{print $1}')" = "$expected"
    case "$relative" in
        *.sh|etc/init.d/*) sh -n "payload/rootfs/$relative" ;;
    esac
done <payload-manifest.tsv
sh -n transforms/apply-provider-cleanup.sh
echo COPY_VERIFY=PASS
REMOTE
} | remote_script >"$STEP_DIR/target-transfer-verify.txt" || \
    fail "STOP_COPY_OR_TRANSFER_ERROR" "target_transfer"

progress "stop legacy execution and install clean foundation atomically"
{
    printf 'TRANSFER=%q\n' "$TARGET_TRANSFER"
    printf 'B=%q\n' "$TARGET_BACKUP"
    cat <<'REMOTE'
set -eu
T="$TRANSFER/extracted/target-step"

for svc in router-egress-health-repair router-egress-emergency-decision router-egress-slot-health; do
    [ -x "/etc/init.d/$svc" ] || continue
    /etc/init.d/$svc stop >/dev/null 2>&1 || true
    /etc/init.d/$svc disable >/dev/null 2>&1 || true
done

while IFS="$(printf '\t')" read -r relative mode expected; do
    [ -n "$relative" ] || continue
    src="$T/payload/rootfs/$relative"
    dst="/$relative"
    test "$(sha256sum "$src" | awk '{print $1}')" = "$expected"
    mkdir -p "$(dirname "$dst")"
    temp="${dst}.new.$$"
    cp "$src" "$temp"
    chmod "$mode" "$temp"
    chown root:root "$temp"
    mv "$temp" "$dst"
    test "$(sha256sum "$dst" | awk '{print $1}')" = "$expected"
done <"$T/payload-manifest.tsv"

"$T/transforms/apply-provider-cleanup.sh"

while IFS= read -r path; do
    [ -n "$path" ] && rm -rf "$path"
done <"$T/delete-list.txt"

find /root/hmn/state -maxdepth 1 -type f -name 'bad-endpoints-*.txt' -exec rm -f {} \; 2>/dev/null || true
find /var/lib/router-egress-recovery/fail-counter -maxdepth 1 -type f -name 'repairs_*.count' -exec rm -f {} \; 2>/dev/null || true

for path in $(grep -RIlE 'vpn-egress-manager|vpn-table200|router-egress-emergency|router-egress-slot-health|hmn-refresh-pool|hmn-rebalance-top5' /etc/hotplug.d/iface 2>/dev/null || true); do
    mkdir -p "$B/files$(dirname "$path")"
    [ -e "$B/files$path" ] || cp -a "$path" "$B/files$path"
    echo "$path" >>"$B/dynamic-hotplug-before.txt"
    echo "$path" >>"$B/existed-before.txt"
    rm -f "$path"
done

ifdown vpn_test >/dev/null 2>&1 || true
ip route show | awk '/dev vpn_test/{print $1}' | while read -r destination; do
    ip route del "$destination" dev vpn_test 2>/dev/null || true
done

ip route flush table 200 2>/dev/null || true
while ip rule show | grep -qE 'lookup[[:space:]]+200([[:space:]]|$)'; do
    pref="$(ip rule show | awk '/lookup 200/{sub(":","",$1); print $1; exit}')"
    [ -n "$pref" ] || break
    ip rule del pref "$pref" 2>/dev/null || break
done

. /usr/local/lib/router-egress-recovery-state.sh
reg_init_state
find "$REG_COUNTER_DIR" -maxdepth 1 -type f -name 'repairs_*.count' -delete 2>/dev/null || true
reg_unset_state daily_repair_count >/dev/null 2>&1 || true
reg_set_state mode LOCAL_REPAIR_READY
count="$(reg_repair_events_get)"
reg_set_state repair_events_since_full_refresh "$count"
if [ "$count" -ge 5 ] 2>/dev/null; then
    reg_set_state full_refresh_due true
else
    reg_set_state full_refresh_due false
fi

/etc/init.d/router-egress-slots enable
/etc/init.d/router-egress-slots start
/etc/init.d/router-egress-health-repair enable
/etc/init.d/router-egress-health-repair start
/etc/init.d/cron restart >/dev/null 2>&1 || true
REMOTE
} | remote_script >"$STEP_DIR/target-install.txt" || \
    fail "STOP_VM101_INSTALL_FAILED" "install"

progress "independent runtime, endpoint, syntax and negative proof checks"
{
    printf 'B=%q\n' "$TARGET_BACKUP"
    cat <<'REMOTE'
set -eu
for file in \
    /usr/local/lib/router-egress-*.sh \
    /usr/local/sbin/router-egress-*.sh \
    /root/hmn/hmn-*.sh \
    /etc/init.d/router-egress-*
do
    [ -f "$file" ] && sh -n "$file"
done

/usr/local/sbin/router-egress-slots-apply.sh start
for spec in \
    '201 vpn1 0x201' \
    '202 vpn2 0x202' \
    '203 vpn3 0x203' \
    '204 vpn4 0x204' \
    '205 vpn5 0x205'
do
    set -- $spec
    ip route show table "$1" | grep -Eq "default[[:space:]].*dev[[:space:]]+$2"
    ip rule show | grep -E "fwmark $3.*lookup $1|fwmark $3/0xffffffff.*lookup $1" >/dev/null
done

for i in 1 2 3 4 5; do
    before="$(grep "^vpn$i=" "$B/endpoints.before" | cut -d= -f2-)"
    after="$(uci -q get network.vpn$i.hmn_endpoint || true)"
    test "$before" = "$after"
done

/etc/init.d/router-egress-health-repair status >/dev/null 2>&1
/usr/local/sbin/router-egress-health-repair-watch.sh --once --dry-run >/tmp/r15a-watch.json
/usr/local/sbin/router-egress-legacy-proof.sh
/usr/local/sbin/router-egress-architecture-status.sh

echo ENDPOINTS_UNCHANGED=true
echo ROUTES_201_205=PASS
echo HEALTH_WATCHER=PASS
REMOTE
} | remote_script >"$VERIFY" || fail "STOP_VM101_POSTCHECK_FAILED" "negative_proof"

progress "Machine Git phase 1: commit deletions with a temporary retired-path validator"
MACHINE_GIT_STARTED="true"
remote_capture 'cat >>/etc/router-machine-git-source.conf <<"PROFILE_EXTRA"
profile_path_allowed_extra() {
    case "$1" in
        etc/init.d/router-egress-emergency-decision|\
        etc/init.d/router-egress-slot-health|\
        root/hmn/hmn-apply-selected.sh|\
        root/hmn/hmn-load-egress-slot.sh|\
        root/hmn/hmn-load-vpn-slot.sh|\
        root/hmn/hmn-load-vpn-user.sh|\
        root/hmn/hmn-plan-selected.sh|\
        root/hmn/hmn-pool-low-watermark-check.sh|\
        root/hmn/hmn-refill-slot.sh|\
        root/hmn/hmn-refresh-awg.sh|\
        root/hmn/hmn-refresh-pool-cron.sh|\
        root/hmn/hmn-refresh-pool-safe.sh|\
        root/hmn/hmn-refresh-retry-cron.sh|\
        root/hmn/hmn-validate-current-pool.sh|\
        root/hmn/hmn-vpn-egress-revive.sh|\
        root/hmn/hmn-vpn-user-override.sh|\
        usr/local/sbin/router-egress-emergency-decision-hook.sh|\
        usr/local/sbin/router-egress-emergency-refresh.sh|\
        usr/local/sbin/router-egress-hmn-plan-top5.sh|\
        usr/local/sbin/router-egress-hmn-rebalance-top5-apply.sh|\
        usr/local/sbin/router-egress-slot-health.sh) return 0 ;;
    esac
    return 1
}
PROFILE_EXTRA
sh -n /etc/router-machine-git-source.conf' >/dev/null || fail "STOP_TRANSITION_PROFILE_FAILED" "append"

PHASE1_LOG="$STEP_DIR/machine-close-phase1.txt"
set +e
/opt/router-ops/bin/router-machine-close \
    vm101 \
    "$STEP_ID: phase1 remove legacy tracked paths" \
    >"$PHASE1_LOG" 2>&1
phase1_rc=$?
set -e
[[ "$phase1_rc" -eq 0 ]] || fail "STOP_VM101_MACHINE_CLOSE_PHASE1_FAILED" "rc_$phase1_rc"

progress "Machine Git phase 2: remove temporary retired-path validator and publish final clean source"
{
    printf 'T=%q\n' "$TARGET_TRANSFER/extracted/target-step"
    cat <<'REMOTE'
set -eu
cp "$T/payload/rootfs/etc/router-machine-git-source.conf" /etc/router-machine-git-source.conf
chmod 600 /etc/router-machine-git-source.conf
sh -n /etc/router-machine-git-source.conf
/usr/local/sbin/router-egress-legacy-proof.sh >/tmp/r15a-final-proof.txt
REMOTE
} | remote_script >/dev/null || fail "STOP_FINAL_PROFILE_RESTORE_FAILED" "profile"

set +e
/opt/router-ops/bin/router-machine-close \
    vm101 \
    "$STEP_ID: phase2 final clean 29-file source" \
    >"$MACHINE_LOG" 2>&1
machine_rc=$?
set -e
[[ "$machine_rc" -eq 0 ]] || fail "STOP_VM101_MACHINE_CLOSE_PHASE2_FAILED" "rc_$machine_rc"

COMMIT="$(grep -E '^(COMMIT|COMMIT_SHA|GIT_COMMIT)=' "$MACHINE_LOG" | tail -n1 | cut -d= -f2- || true)"
TREE="$(grep -E '^(TREE|COMMIT_TREE|GIT_TREE)=' "$MACHINE_LOG" | tail -n1 | cut -d= -f2- || true)"
GIT_FILE_COUNT="$(grep -E '^(GIT_FILE_COUNT|FILE_COUNT)=' "$MACHINE_LOG" | tail -n1 | cut -d= -f2- || true)"
VM101_PUBLIC_URL="$(grep '^VM101_PUBLIC_URL=' "$MACHINE_LOG" | tail -n1 | cut -d= -f2- || true)"

[[ "$GIT_FILE_COUNT" == "$EXPECTED_AFTER_COUNT" ]] || \
    fail "STOP_VM101_GIT_FILE_COUNT_MISMATCH" "$GIT_FILE_COUNT"
[[ "$VM101_PUBLIC_URL" == "${PERMANENT_ROOT}/latest/"*/ ]] || \
    fail "STOP_VM101_PUBLIC_URL_MISSING" "$VM101_PUBLIC_URL"

progress "publish fresh canonical plan, model and methods with quiet publisher logs"
cp "$BUNDLE_ROOT/docs/canonical-m07-plan.md" "$PLAN_FILE"
cp "$BUNDLE_ROOT/docs/access-routes.md" "$ACCESS_FILE"

cat >"$MODEL_FILE" <<EOF
# VM101 model — R15A clean foundation

Timestamp: $TS
Commit: $COMMIT
Tree: $TREE
Managed files: $GIT_FILE_COUNT

Production loop: health watcher -> dispatcher -> five-slot LOCAL_REPAIR.
Provider boundary: HMN download -> low-level vpn_test -> unified-quarantine ranker; no slot assignment.
Routing: vpn1..vpn5, tables 201..205. Table 200 absent.
State: /var/lib/router-egress-recovery.
FULL_POOL_REFRESH and Direct remain disabled.
EOF

cat >"$METHODS_FILE" <<EOF
# VM101 methods — R15A

Access: ops@router-ops -> pve-mgts -> root@10.71.100.2 using $PVE_KEY.
Transfer: ZIP verified on VM130; target archive retained as Base64, gzip and extracted STEP; decode/gzip/manifest/syntax verified before individual atomic installation.
Runtime impact: watcher stopped/restarted, legacy services disabled/deleted, table 200 flushed, vpn_test left down; active vpn1..vpn5 endpoints unchanged.
Rollback: $TARGET_ROLLBACK and published rollback.sh.
EOF

publish_quiet \
    "local_architecture_plan_vm101_autonomous_hmn_recovery_r15a" \
    "$STEP_DIR/plan-publish.txt" \
    "$PLAN_FILE" || fail "STOP_LOCAL_PLAN_PUBLICATION_FAILED" "plan"
LOCAL_M07_PLAN_URL="$(extract_var "$STEP_DIR/plan-publish.txt" LOCAL_M07_PLAN_URL)"
if [[ "$LOCAL_M07_PLAN_URL" != "${PERMANENT_ROOT}/latest/"*/ ]]; then
    rel="$(extract_var "$STEP_DIR/plan-publish.txt" PUBLISHED_RELATIVE_PATH)"
    LOCAL_M07_PLAN_URL="${PERMANENT_ROOT}/${rel}/"
fi

publish_quiet \
    "vm101_model_legacy_freeze_clean_foundation_r15a" \
    "$STEP_DIR/model-publish.txt" \
    "$MODEL_FILE" || fail "STOP_VM101_MODEL_PUBLICATION_FAILED" "model"
VM101_MODEL_URL="$(extract_var "$STEP_DIR/model-publish.txt" VM101_MODEL_URL)"
if [[ "$VM101_MODEL_URL" != "${PERMANENT_ROOT}/latest/"*/ ]]; then
    rel="$(extract_var "$STEP_DIR/model-publish.txt" PUBLISHED_RELATIVE_PATH)"
    VM101_MODEL_URL="${PERMANENT_ROOT}/${rel}/"
fi

publish_quiet \
    "vm101_methods_legacy_freeze_clean_foundation_r15a" \
    "$STEP_DIR/methods-publish.txt" \
    "$METHODS_FILE" || fail "STOP_VM101_METHODS_PUBLICATION_FAILED" "methods"
VM101_METHODS_URL="$(extract_var "$STEP_DIR/methods-publish.txt" VM101_METHODS_URL)"
if [[ "$VM101_METHODS_URL" != "${PERMANENT_ROOT}/latest/"*/ ]]; then
    rel="$(extract_var "$STEP_DIR/methods-publish.txt" PUBLISHED_RELATIVE_PATH)"
    VM101_METHODS_URL="${PERMANENT_ROOT}/${rel}/"
fi

progress "verify permanent HTTPS, canonical counts and freshness"
for url in \
    "$VM101_PUBLIC_URL" \
    "$VM101_MODEL_URL" \
    "$VM101_METHODS_URL" \
    "$LOCAL_M07_PLAN_URL" \
    "$GLOBAL_PROJECT_PLAN_URL" \
    "$ACCESS_MAP_URL" \
    "$VM100_PUBLIC_URL" \
    "$VM121_PUBLIC_URL" \
    "$VM130_SOURCE_URL"
do
    http_get "$url" || fail "STOP_EXTERNAL_HTTP_VERIFY_FAILED" "$url"
done

TOKEN="$(cat /opt/router-ops/public/.router-public-token)"
LATEST_DIR="/opt/router-ops/public/r/$TOKEN/latest"
for pattern in \
    '*_vm101_git_source_*' \
    '*_vm101_model_*' \
    '*_vm101_methods_*' \
    '*_local_architecture_plan_vm101_autonomous_hmn_recovery*'
do
    count="$(find "$LATEST_DIR" -mindepth 1 -maxdepth 1 -type d -name "$pattern" | wc -l | tr -d ' ')"
    [[ "$count" == 1 ]] || fail "STOP_LATEST_DUPLICATE_KIND" "$pattern:$count"
done

cat >>"$VERIFY" <<EOF
MACHINE_GIT_COMMIT=$COMMIT
MACHINE_GIT_TREE=$TREE
MACHINE_GIT_FILE_COUNT=$GIT_FILE_COUNT
PUBLICATION_HTTP=PASS
LATEST_KIND_COUNTS=PASS
COPY_VERIFY=PASS
EOF

RESULT="PASS_VM101_LEGACY_EXECUTION_FROZEN_AND_CLEAN_FOUNDATION_ACTIVE"
finalize
