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

STEP_ID="STEP_050M07R14C_VM101_OPERATIONS_TOOLSET_BASELINE_R03_REBUILT"
EXPECTED_PUBLISHER_SHA256="e78430b57afbd0ae27fc575fc4de78bb267c76caf04ec935b01e9b2f89840e32"
EXPECTED_VM101_COMMIT="0a6998cfead6a1e8b820cce9c00171dd6e81c6a1"
EXPECTED_VM101_TREE="b2646e97025823532daa549235e1643f75f11c4e"
EXPECTED_VM101_FILE_COUNT="47"
REPOSITORY="https://github.com/DChuvelev/wg-paid-vm101.git"
PUBLISHER="/opt/router-ops/bin/router-publish-report"
PUBLIC_FS_ROOT="/opt/router-ops/public/r/e94a0859747d7b96f29c7fdafc2d0351ba603bb0a7e9e5a4"
PUBLIC_HOST="reports.secret-studio.ru"
PUBLIC_RESOLVE="reports.secret-studio.ru:443:10.71.100.84"
LATEST_ROOT_URL="https://reports.secret-studio.ru/latest/"
OLD_ROOT_URL="https://reports.secret-studio.ru/old/"

LOCAL_M07_PLAN_URL="https://reports.secret-studio.ru/latest/20260713-193942_local_architecture_plan_vm101_autonomous_hmn_recovery/"
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/"

BUNDLE_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
BUNDLE_ZIP="${BUNDLE_ZIP:?BUNDLE_ZIP is required}"
EXPECTED_BUNDLE_SHA256="${EXPECTED_BUNDLE_SHA256:?EXPECTED_BUNDLE_SHA256 is required}"
EXPECTED_INSTALLER_SHA256="${EXPECTED_INSTALLER_SHA256:?EXPECTED_INSTALLER_SHA256 is required}"

STAMP="$(date -u +%Y%m%d-%H%M%S)"
STEP_DIR="/opt/router-ops/steps/${STAMP}_${STEP_ID}"
mkdir -p "$STEP_DIR"
LOG="$STEP_DIR/main.log"
exec > >(tee -a "$LOG") 2>&1

RESULT="STOP_UNHANDLED_TOOLSET_ERROR"
STOP_REASON="unhandled"
COPY_VERIFY=PASS
RUNTIME_IMPACT=false
STEP_WAS_NOT_STARTED=false
STEP_REPORT_URL=
VM101_PUBLIC_URL=
VM101_MODEL_URL=
VM101_METHODS_URL=
FACTS_URL=facts.json
VERIFY_URL=verify.txt
ROLLBACK_URL=rollback.sh
PUBLIC_OBJECT_KIND=
PREVIOUS_MOVED_COUNT=
TARGET_CHANGE_STARTED=false
TARGET_RESULT=
REMOTE_STATE_DIR=
PACKAGE_MANAGER=
NEW_REQUESTED_PACKAGE_COUNT=0
NEW_ALL_PACKAGE_COUNT=0
MACHINE_GIT_SOURCE_CHANGED=false
ENDPOINTS_CHANGED=false
R15A_CAPABILITY_CONTRACT=UNKNOWN
PUBLISH_FAILURE=false

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

step_sha() {
    sha256sum "$STEP_DIR/step-code.sh" | awk '{print $1}'
}

nested_ssh() {
    local script="$1"
    printf '%s\n' "$script" | \
        ssh pve-mgts \
            "ssh -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2 'sh -s'"
}

nested_put() {
    local source="$1"
    local destination="$2"
    ssh pve-mgts \
        "ssh -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2 'cat > $destination'" \
        <"$source"
}

publish_capture() {
    local stage="$1" name="$2" log="$3"
    local files=()
    while IFS= read -r -d '' file; do
        files+=("$file")
    done < <(
        find "$stage" -mindepth 1 -maxdepth 1 -type f -print0 |
        sort -z
    )
    [[ ${#files[@]} -gt 0 ]] || return 1
    "$PUBLISHER" "$name" "${files[@]}" >"$log" 2>&1
    progress "published $name: PASS"
}

value_from_log() {
    local key="$1" log="$2"
    awk -F= -v k="$key" '$1==k {v=substr($0,index($0,"=")+1)} END{print v}' "$log"
}

published_url_from_log() {
    local key="$1" log="$2" url rel dir
    url="$(value_from_log "$key" "$log")"
    [[ -n "$url" ]] || url="$(value_from_log PUBLIC_URL "$log")"
    if [[ -z "$url" ]]; then
        rel="$(value_from_log PUBLISHED_RELATIVE_PATH "$log")"
        [[ -n "$rel" ]] && url="https://reports.secret-studio.ru/${rel}/"
    fi
    if [[ -z "$url" ]]; then
        dir="$(value_from_log PUBLISHED_DIR "$log")"
        [[ -n "$dir" ]] && url="https://reports.secret-studio.ru/latest/$(basename "$dir")/"
    fi
    printf '%s\n' "$url"
}

http_get_retry() {
    local url="$1" out="$2"
    local i
    for i in 1 2 3 4 5 6; do
        if curl --fail --silent --show-error --location \
            --connect-timeout 5 --max-time 30 \
            --resolve "$PUBLIC_RESOLVE" \
            "$url" -o "$out"; then
            return 0
        fi
        sleep 2
    done
    return 1
}

prepare_reports() {
    local now
    now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"

    cat >"$STEP_DIR/report.txt" <<EOF
STEP_ID=$STEP_ID
GENERATED_AT_UTC=$now
RESULT=$RESULT
STOP_REASON=$STOP_REASON
PURPOSE=Install a stable VM101 OpenWrt operations toolset so rebuilt R15A R03 can be rerun.
PACKAGE_MANAGER=$PACKAGE_MANAGER
NEW_REQUESTED_PACKAGE_COUNT=$NEW_REQUESTED_PACKAGE_COUNT
NEW_ALL_PACKAGE_COUNT=$NEW_ALL_PACKAGE_COUNT
MACHINE_GIT_SOURCE_CHANGED=$MACHINE_GIT_SOURCE_CHANGED
ENDPOINTS_CHANGED=$ENDPOINTS_CHANGED
R15A_CAPABILITY_CONTRACT=$R15A_CAPABILITY_CONTRACT
RUNTIME_IMPACT=$RUNTIME_IMPACT
STEP_WAS_NOT_STARTED=$STEP_WAS_NOT_STARTED
TARGET_CHANGE_STARTED=$TARGET_CHANGE_STARTED
TARGET_RESULT=$TARGET_RESULT
REMOTE_STATE_DIR=$REMOTE_STATE_DIR
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$(step_sha)
EOF

    cat >"$STEP_DIR/verify.txt" <<EOF
RESULT=$RESULT
STOP_REASON=$STOP_REASON
COPY_VERIFY=$COPY_VERIFY
STEP_SHA256=$(step_sha)
PUBLISHER_SHA256=$(sha256sum "$PUBLISHER" 2>/dev/null | awk '{print $1}')
EXPECTED_VM101_COMMIT=$EXPECTED_VM101_COMMIT
EXPECTED_VM101_TREE=$EXPECTED_VM101_TREE
EXPECTED_VM101_FILE_COUNT=$EXPECTED_VM101_FILE_COUNT
PACKAGE_MANAGER=$PACKAGE_MANAGER
MACHINE_GIT_SOURCE_CHANGED=$MACHINE_GIT_SOURCE_CHANGED
ENDPOINTS_CHANGED=$ENDPOINTS_CHANGED
R15A_CAPABILITY_CONTRACT=$R15A_CAPABILITY_CONTRACT
RUNTIME_IMPACT=$RUNTIME_IMPACT
EOF

    python3 - "$STEP_DIR/facts.json" <<PY
import json, sys
facts = {
  "step_id": "$STEP_ID",
  "result": "$RESULT",
  "stop_reason": "$STOP_REASON",
  "copy_verify": "$COPY_VERIFY",
  "step_sha256": "$(step_sha)",
  "package_manager": "$PACKAGE_MANAGER",
  "new_requested_package_count": int("${NEW_REQUESTED_PACKAGE_COUNT:-0}"),
  "new_all_package_count": int("${NEW_ALL_PACKAGE_COUNT:-0}"),
  "machine_git_source_changed": "$MACHINE_GIT_SOURCE_CHANGED" == "true",
  "endpoints_changed": "$ENDPOINTS_CHANGED" == "true",
  "r15a_capability_contract": "$R15A_CAPABILITY_CONTRACT",
  "expected_commit": "$EXPECTED_VM101_COMMIT",
  "expected_tree": "$EXPECTED_VM101_TREE",
  "expected_file_count": int("$EXPECTED_VM101_FILE_COUNT"),
  "runtime_impact": False,
  "target_change_started": "$TARGET_CHANGE_STARTED" == "true",
  "target_result": "$TARGET_RESULT",
  "remote_state_dir": "$REMOTE_STATE_DIR"
}
with open(sys.argv[1], "w") as f:
    json.dump(facts, f, indent=2, sort_keys=True)
    f.write("\n")
PY

    cat >"$STEP_DIR/system-model.txt" <<EOF
VM101 operations toolset baseline:
- Package-managed utilities supplement BusyBox; production scripts remain /bin/ash compatible.
- No service, interface, route, endpoint or Machine Git source change is intended.
- Machine Git commit/tree must remain $EXPECTED_VM101_COMMIT / $EXPECTED_VM101_TREE.
- The rebuilt R15A R03 package is expected to pass base64/stat/timeout transfer and runtime checks afterward.
EOF

    cat >"$STEP_DIR/methods-and-limitations.txt" <<EOF
Access route:
ops@router-ops -> ssh pve-mgts -> ssh root@10.71.100.2 using /root/.ssh/pve_to_openwrt_mgts_ed25519.

Package policy:
- Detect opkg or apk from the live target.
- Refresh metadata and install only named missing packages.
- Never run opkg upgrade or apk upgrade.
- Verify command semantics, not only command presence.
- Preserve POSIX ash compatibility for production scripts.
- Package state is captured in this STEP/model/methods; Machine Git source intentionally remains unchanged so R15A R02 can be rerun byte-for-byte.
EOF

    cat >"$STEP_DIR/access-routes.txt" <<EOF
VM101_NESTED_ROUTE=ops@router-ops -> pve-mgts -> root@10.71.100.2
VM101_DIRECT_SSH_FROM_ROUTER_OPS=unsupported
VM101_PACKAGE_MANAGER=$PACKAGE_MANAGER
EOF

    cp "$BUNDLE_ROOT/package-list.txt" "$STEP_DIR/requested-packages.txt"
}

publish_failure_report() {
    prepare_reports
    local stage="/tmp/router-publish-${STEP_ID}-${STAMP}"
    rm -rf "$stage"
    mkdir -p "$stage"
    cp -a "$STEP_DIR/." "$stage/"
    (
        cd "$stage"
        find . -type f ! -name manifest.sha256 -print0 | sort -z | xargs -0 sha256sum >manifest.sha256
    )
    local log="$STEP_DIR/step-publish.log"
    if publish_capture "$stage" "step050m07r14c_vm101_operations_toolset_baseline" "$log"; then
        STEP_REPORT_URL="$(value_from_log STEP_REPORT_URL "$log")"
        PUBLIC_OBJECT_KIND="$(value_from_log PUBLIC_OBJECT_KIND "$log")"
        PREVIOUS_MOVED_COUNT="$(value_from_log PREVIOUS_MOVED_COUNT "$log")"
    else
        PUBLISH_FAILURE=true
    fi
}

finish() {
    prepare_reports
    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:+${STEP_REPORT_URL}facts.json}" \
        "VERIFY_URL=${STEP_REPORT_URL:+${STEP_REPORT_URL}verify.txt}" \
        "ROLLBACK_URL=${STEP_REPORT_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"
}

fail() {
    RESULT="$1"
    STOP_REASON="$2"
    publish_failure_report || true
    finish
    exit 1
}

trap 'rc=$?; if [ $rc -ne 0 ] && [[ "$RESULT" != PASS_* ]] && [[ "$RESULT" != STOP_* ]]; then fail STOP_UNHANDLED_TOOLSET_ERROR "line_${LINENO}_rc_${rc}"; fi' EXIT

progress "verify bundle and publisher"
[[ "$(id -un)" == "ops" ]] || fail STOP_MUST_RUN_AS_OPS user
cp "$0" "$STEP_DIR/step-code.sh"
cp "$BUNDLE_ROOT/target-install.sh" "$STEP_DIR/target-step-code.sh"
cp "$BUNDLE_ZIP" "$STEP_DIR/"
cp "$BUNDLE_ROOT/manifest.sha256" "$STEP_DIR/bundle-manifest.sha256"
cp "$BUNDLE_ROOT/package-list.txt" "$STEP_DIR/requested-packages.txt"

[[ "$(sha256sum "$BUNDLE_ZIP" | awk '{print $1}')" == "$EXPECTED_BUNDLE_SHA256" ]] || {
    COPY_VERIFY=FAIL
    STEP_WAS_NOT_STARTED=true
    fail STOP_COPY_OR_TRANSFER_ERROR bundle_sha
}
[[ "$(sha256sum "$BUNDLE_ROOT/install.sh" | awk '{print $1}')" == "$EXPECTED_INSTALLER_SHA256" ]] || {
    COPY_VERIFY=FAIL
    STEP_WAS_NOT_STARTED=true
    fail STOP_COPY_OR_TRANSFER_ERROR installer_sha
}
(
    cd "$BUNDLE_ROOT"
    sha256sum -c manifest.sha256
) >"$STEP_DIR/internal-manifest-check.txt" 2>&1 || {
    COPY_VERIFY=FAIL
    STEP_WAS_NOT_STARTED=true
    fail STOP_COPY_OR_TRANSFER_ERROR internal_manifest
}
bash -n "$BUNDLE_ROOT/install.sh"
sh -n "$BUNDLE_ROOT/target-install.sh"

[[ "$(sha256sum "$PUBLISHER" | awk '{print $1}')" == "$EXPECTED_PUBLISHER_SHA256" ]] || \
    fail STOP_PUBLISHER_SHA256_MISMATCH publisher

progress "preflight exact VM101 source and package manager"
nested_ssh "git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse HEAD; git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse 'HEAD^{tree}'; command -v opkg || command -v apk; df -Pk / /overlay 2>/dev/null || df -Pk /" \
    >"$STEP_DIR/target-preflight.txt" 2>&1 || fail STOP_VM101_ACCESS_PREFLIGHT_FAILED nested_ssh

grep -Fxq "$EXPECTED_VM101_COMMIT" "$STEP_DIR/target-preflight.txt" || fail STOP_VM101_EXACT_PREFLIGHT_FAILED commit
grep -Fxq "$EXPECTED_VM101_TREE" "$STEP_DIR/target-preflight.txt" || fail STOP_VM101_EXACT_PREFLIGHT_FAILED tree
grep -Eq '/(opkg|apk)$' "$STEP_DIR/target-preflight.txt" || fail STOP_PACKAGE_MANAGER_NOT_FOUND package_manager

progress "transfer target code and package contract without requiring base64"
REMOTE_TRANSFER="/root/router-toolset-transfer"
nested_ssh "rm -rf '$REMOTE_TRANSFER'; mkdir -p '$REMOTE_TRANSFER'"
nested_put "$BUNDLE_ROOT/target-install.sh" "$REMOTE_TRANSFER/target-install.sh"
nested_put "$BUNDLE_ROOT/package-list.txt" "$REMOTE_TRANSFER/package-list.txt"

TARGET_CODE_SHA="$(sha256sum "$BUNDLE_ROOT/target-install.sh" | awk '{print $1}')"
PACKAGE_LIST_SHA="$(sha256sum "$BUNDLE_ROOT/package-list.txt" | awk '{print $1}')"
{
    echo "TARGET_CODE_SHA256_EXPECTED=$TARGET_CODE_SHA"
    echo "PACKAGE_LIST_SHA256_EXPECTED=$PACKAGE_LIST_SHA"
    nested_ssh "sha256sum '$REMOTE_TRANSFER/target-install.sh' '$REMOTE_TRANSFER/package-list.txt'; sh -n '$REMOTE_TRANSFER/target-install.sh'"
} >"$STEP_DIR/transfer-verify.txt" 2>&1

grep -Fq "$TARGET_CODE_SHA" "$STEP_DIR/transfer-verify.txt" || {
    COPY_VERIFY=FAIL
    STEP_WAS_NOT_STARTED=true
    fail STOP_COPY_OR_TRANSFER_ERROR target_code_sha
}
grep -Fq "$PACKAGE_LIST_SHA" "$STEP_DIR/transfer-verify.txt" || {
    COPY_VERIFY=FAIL
    STEP_WAS_NOT_STARTED=true
    fail STOP_COPY_OR_TRANSFER_ERROR package_list_sha
}

progress "install and verify VM101 operations toolset"
TARGET_CHANGE_STARTED=true
set +e
nested_ssh "STEP_TAG='$STEP_ID' EXPECTED_COMMIT='$EXPECTED_VM101_COMMIT' EXPECTED_TREE='$EXPECTED_VM101_TREE' PACKAGE_FILE='$REMOTE_TRANSFER/package-list.txt' sh '$REMOTE_TRANSFER/target-install.sh'" \
    >"$STEP_DIR/target-run.log" 2>&1
target_rc=$?
set -e

TARGET_RESULT="$(awk -F= '$1=="RESULT"{v=substr($0,index($0,"=")+1)} END{print v}' "$STEP_DIR/target-run.log")"
REMOTE_STATE_DIR="$(awk -F= '$1=="STATE_DIR"{v=substr($0,index($0,"=")+1)} END{print v}' "$STEP_DIR/target-run.log")"
PACKAGE_MANAGER="$(awk -F= '$1=="PACKAGE_MANAGER"{v=substr($0,index($0,"=")+1)} END{print v}' "$STEP_DIR/target-run.log")"
NEW_REQUESTED_PACKAGE_COUNT="$(awk -F= '$1=="NEW_REQUESTED_PACKAGE_COUNT"{v=$2} END{print v+0}' "$STEP_DIR/target-run.log")"
NEW_ALL_PACKAGE_COUNT="$(awk -F= '$1=="NEW_ALL_PACKAGE_COUNT"{v=$2} END{print v+0}' "$STEP_DIR/target-run.log")"
MACHINE_GIT_SOURCE_CHANGED="$(awk -F= '$1=="MACHINE_GIT_SOURCE_CHANGED"{v=$2} END{print v}' "$STEP_DIR/target-run.log")"
ENDPOINTS_CHANGED="$(awk -F= '$1=="ENDPOINTS_CHANGED"{v=$2} END{print v}' "$STEP_DIR/target-run.log")"
R15A_CAPABILITY_CONTRACT="$(awk -F= '$1=="R15A_CAPABILITY_CONTRACT"{v=$2} END{print v}' "$STEP_DIR/target-run.log")"

if [[ -n "$REMOTE_STATE_DIR" ]]; then
    nested_ssh "tar -czf - -C '$REMOTE_STATE_DIR' ." >"$STEP_DIR/target-artifacts.tar.gz" 2>"$STEP_DIR/target-artifacts-transfer.err" || true
    if gzip -t "$STEP_DIR/target-artifacts.tar.gz" 2>/dev/null; then
        mkdir -p "$STEP_DIR/target-artifacts"
        tar -xzf "$STEP_DIR/target-artifacts.tar.gz" -C "$STEP_DIR/target-artifacts"
        [[ -f "$STEP_DIR/target-artifacts/rollback.sh" ]] && cp "$STEP_DIR/target-artifacts/rollback.sh" "$STEP_DIR/rollback.sh"
        [[ -f "$STEP_DIR/target-artifacts/packages-before.txt" ]] && cp "$STEP_DIR/target-artifacts/packages-before.txt" "$STEP_DIR/"
        [[ -f "$STEP_DIR/target-artifacts/packages-after.txt" ]] && cp "$STEP_DIR/target-artifacts/packages-after.txt" "$STEP_DIR/"
        [[ -f "$STEP_DIR/target-artifacts/packages-added-all.names" ]] && cp "$STEP_DIR/target-artifacts/packages-added-all.names" "$STEP_DIR/"
        [[ -f "$STEP_DIR/target-artifacts/capabilities-before.tsv" ]] && cp "$STEP_DIR/target-artifacts/capabilities-before.tsv" "$STEP_DIR/"
        [[ -f "$STEP_DIR/target-artifacts/capabilities-after.tsv" ]] && cp "$STEP_DIR/target-artifacts/capabilities-after.tsv" "$STEP_DIR/"
        [[ -f "$STEP_DIR/target-artifacts/endpoints-before.tsv" ]] && cp "$STEP_DIR/target-artifacts/endpoints-before.tsv" "$STEP_DIR/"
        [[ -f "$STEP_DIR/target-artifacts/endpoints-after.tsv" ]] && cp "$STEP_DIR/target-artifacts/endpoints-after.tsv" "$STEP_DIR/"
    fi
fi

[[ $target_rc -eq 0 ]] || fail "${TARGET_RESULT:-STOP_VM101_TOOLSET_INSTALL_FAILED}" target_rc
[[ "$TARGET_RESULT" == "PASS_VM101_OPERATIONS_TOOLSET_BASELINE_R03_REBUILT" ]] || fail STOP_VM101_TOOLSET_INSTALL_FAILED target_result
[[ "$MACHINE_GIT_SOURCE_CHANGED" == "false" ]] || fail STOP_MACHINE_GIT_SOURCE_CHANGED postcheck
[[ "$ENDPOINTS_CHANGED" == "false" ]] || fail STOP_RUNTIME_ENDPOINTS_CHANGED postcheck
[[ "$R15A_CAPABILITY_CONTRACT" == "PASS" ]] || fail STOP_CAPABILITY_CONTRACT_FAILED postcheck

progress "verify exact GitHub source and publish through Machine Git workflow"
CLONE_DIR="$STEP_DIR/github-clone"
git clone --quiet "$REPOSITORY" "$CLONE_DIR" >"$STEP_DIR/git-clone.log" 2>&1
git -C "$CLONE_DIR" checkout --quiet --detach "$EXPECTED_VM101_COMMIT"
CLONE_COMMIT="$(git -C "$CLONE_DIR" rev-parse HEAD)"
CLONE_TREE="$(git -C "$CLONE_DIR" rev-parse 'HEAD^{tree}')"
CLONE_COUNT="$(git -C "$CLONE_DIR" ls-tree -r --name-only HEAD | wc -l | tr -d ' ')"
[[ "$CLONE_COMMIT" == "$EXPECTED_VM101_COMMIT" ]] || fail STOP_GITHUB_EXACT_CLONE_FAILED commit
[[ "$CLONE_TREE" == "$EXPECTED_VM101_TREE" ]] || fail STOP_GITHUB_EXACT_CLONE_FAILED tree
[[ "$CLONE_COUNT" == "$EXPECTED_VM101_FILE_COUNT" ]] || fail STOP_GITHUB_EXACT_CLONE_FAILED count

SOURCE_LOG="$STEP_DIR/router-machine-close.log"
set +e
router-machine-close vm101 \
    "$STEP_ID: operations toolset installed; managed source unchanged" \
    >"$SOURCE_LOG" 2>&1
machine_close_rc=$?
set -e

MC_COMMIT="$(awk -F= '/^(VM130_COMMIT|COMMIT|COMMIT_SHA|GIT_COMMIT)=/{v=substr($0,index($0,"=")+1)} END{print v}' "$SOURCE_LOG")"
MC_TREE="$(awk -F= '/^(VM130_TREE|TREE|COMMIT_TREE|GIT_TREE)=/{v=substr($0,index($0,"=")+1)} END{print v}' "$SOURCE_LOG")"
MC_COUNT="$(awk -F= '/^(VM130_FILE_COUNT|GIT_FILE_COUNT|FILE_COUNT)=/{v=substr($0,index($0,"=")+1)} END{print v}' "$SOURCE_LOG")"
VM101_PUBLIC_URL="$(published_url_from_log VM101_PUBLIC_URL "$SOURCE_LOG")"

machine_close_proof=true
[[ "$MC_COMMIT" == "$EXPECTED_VM101_COMMIT" ]] || machine_close_proof=false
[[ "$MC_TREE" == "$EXPECTED_VM101_TREE" ]] || machine_close_proof=false
[[ "$MC_COUNT" == "$EXPECTED_VM101_FILE_COUNT" ]] || machine_close_proof=false
[[ "$VM101_PUBLIC_URL" == "https://reports.secret-studio.ru/latest/"*/ ]] || machine_close_proof=false
grep -q '^PUBLISHED_DIR=' "$SOURCE_LOG" || machine_close_proof=false

if [[ "$machine_close_rc" -ne 0 && "$machine_close_proof" != true ]]; then
    fail STOP_MACHINE_CLOSE_FAILED "rc_${machine_close_rc}"
fi
[[ "$machine_close_proof" == true ]] || fail STOP_MACHINE_CLOSE_PROOF_FAILED source

MODEL_STAGE="$STEP_DIR/publish-vm101-model"
mkdir -p "$MODEL_STAGE"
cat >"$MODEL_STAGE/vm101-model.md" <<EOF
# VM101 model — operations toolset baseline

- OpenWrt package manager: $PACKAGE_MANAGER
- Machine Git commit: $EXPECTED_VM101_COMMIT
- Machine Git tree: $EXPECTED_VM101_TREE
- Machine Git source changed: false
- Active endpoints changed: false
- New requested packages: $NEW_REQUESTED_PACKAGE_COUNT
- New packages including dependencies: $NEW_ALL_PACKAGE_COUNT
- R15A capability contract: PASS
- Runtime traffic interruption: none
EOF
cp "$STEP_DIR/requested-packages.txt" "$MODEL_STAGE/"
[[ -f "$STEP_DIR/packages-after.txt" ]] && cp "$STEP_DIR/packages-after.txt" "$MODEL_STAGE/"
[[ -f "$STEP_DIR/capabilities-after.tsv" ]] && cp "$STEP_DIR/capabilities-after.tsv" "$MODEL_STAGE/"
MODEL_LOG="$STEP_DIR/publish-vm101-model.log"
publish_capture "$MODEL_STAGE" "vm101_model_operations_toolset_baseline_r14c" "$MODEL_LOG"
VM101_MODEL_URL="$(published_url_from_log VM101_MODEL_URL "$MODEL_LOG")"

METHODS_STAGE="$STEP_DIR/publish-vm101-methods"
mkdir -p "$METHODS_STAGE"
cat >"$METHODS_STAGE/vm101-methods.md" <<EOF
# VM101 methods — operations toolset baseline

Production scripts remain BusyBox ash compatible. The package baseline supplies full utilities that repeatedly caused STEP failures or are used for diagnostics.

Guaranteed after this STEP:
base64, stat, timeout, realpath, readlink, sort, nl, diff, gawk/awk, jq, curl, tcpdump, dig, ip-full, pgrep, pkill and watch.

Package policy:
- install named missing packages only;
- never perform whole-system package upgrades;
- verify exact command behavior;
- preserve Machine Git commit/tree so the rebuilt R15A R03 bundle can be rerun.

Approved access:
ops@router-ops -> pve-mgts -> root@10.71.100.2.
EOF
cp "$STEP_DIR/requested-packages.txt" "$METHODS_STAGE/"
[[ -f "$STEP_DIR/capabilities-before.tsv" ]] && cp "$STEP_DIR/capabilities-before.tsv" "$METHODS_STAGE/"
[[ -f "$STEP_DIR/capabilities-after.tsv" ]] && cp "$STEP_DIR/capabilities-after.tsv" "$METHODS_STAGE/"
METHODS_LOG="$STEP_DIR/publish-vm101-methods.log"
publish_capture "$METHODS_STAGE" "vm101_methods_operations_toolset_baseline_r14c" "$METHODS_LOG"
VM101_METHODS_URL="$(published_url_from_log VM101_METHODS_URL "$METHODS_LOG")"

progress "prepare and publish formal STEP report"
RESULT="PASS_VM101_OPERATIONS_TOOLSET_BASELINE_R15A_R03_READY"
STOP_REASON=none
prepare_reports
REPORT_STAGE="/tmp/router-publish-${STEP_ID}-${STAMP}"
rm -rf "$REPORT_STAGE"
mkdir -p "$REPORT_STAGE"
cp -a "$STEP_DIR/." "$REPORT_STAGE/"
(
    cd "$REPORT_STAGE"
    find . -type f ! -name manifest.sha256 -print0 | sort -z | xargs -0 sha256sum >manifest.sha256
)
REPORT_LOG="$STEP_DIR/step-publish.log"
publish_capture "$REPORT_STAGE" "step050m07r14c_vm101_operations_toolset_baseline" "$REPORT_LOG"
STEP_REPORT_URL="$(published_url_from_log STEP_REPORT_URL "$REPORT_LOG")"
PUBLIC_OBJECT_KIND="$(value_from_log PUBLIC_OBJECT_KIND "$REPORT_LOG")"
PREVIOUS_MOVED_COUNT="$(value_from_log PREVIOUS_MOVED_COUNT "$REPORT_LOG")"

progress "external HTTPS and canonical-count verification"
http_get_retry "$STEP_REPORT_URL" "$STEP_DIR/http-step-index.html" || fail STOP_EXTERNAL_HTTP_VERIFY_FAILED step_index
http_get_retry "${STEP_REPORT_URL}facts.json" "$STEP_DIR/http-facts.json" || fail STOP_EXTERNAL_HTTP_VERIFY_FAILED facts
http_get_retry "$VM101_PUBLIC_URL" "$STEP_DIR/http-vm101-index.html" || fail STOP_EXTERNAL_HTTP_VERIFY_FAILED vm101
http_get_retry "$VM101_MODEL_URL" "$STEP_DIR/http-model-index.html" || fail STOP_EXTERNAL_HTTP_VERIFY_FAILED model
http_get_retry "$VM101_METHODS_URL" "$STEP_DIR/http-methods-index.html" || fail STOP_EXTERNAL_HTTP_VERIFY_FAILED methods

for kind in vm101 vm101-model vm101-methods; do
    count="$(find "$PUBLIC_FS_ROOT/latest" -mindepth 1 -maxdepth 1 -type d -name "*_${kind//-/_}*" | wc -l | tr -d ' ')"
    echo "${kind}_candidate_count=$count" >>"$STEP_DIR/canonical-counts.txt"
done

finish
trap - EXIT
