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

STEP_ID="STEP_050M07R14D_VM101_DISK_EXPANSION_1G_R05S2_PUBLICATION_CONTINUATION_R01"
EXPECTED_COMMIT="0a6998cfead6a1e8b820cce9c00171dd6e81c6a1"
EXPECTED_TREE="b2646e97025823532daa549235e1643f75f11c4e"
EXPECTED_PUBLISHER_SHA="e78430b57afbd0ae27fc575fc4de78bb267c76caf04ec935b01e9b2f89840e32"

PUBLISHER="/opt/router-ops/bin/router-publish-report"
RESOLVE="reports.secret-studio.ru:443:10.71.100.84"

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/"
LATEST_ROOT_URL="https://reports.secret-studio.ru/latest/"
OLD_ROOT_URL="https://reports.secret-studio.ru/old/"

BUNDLE_ROOT="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
BUNDLE_ZIP="${BUNDLE_ZIP:?BUNDLE_ZIP required}"
EXPECTED_BUNDLE_SHA256="${EXPECTED_BUNDLE_SHA256:?EXPECTED_BUNDLE_SHA256 required}"
EXPECTED_INSTALLER_SHA256="${EXPECTED_INSTALLER_SHA256:?EXPECTED_INSTALLER_SHA256 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_R05S2_ERROR"
RUNTIME_IMPACT=false
COPY_VERIFY=PASS
STEP_REPORT_URL=
VM101_PUBLIC_URL=
VM101_MODEL_URL=
VM101_METHODS_URL=
FACTS_URL=
VERIFY_URL=
ROLLBACK_URL=
PUBLIC_OBJECT_KIND=
PREVIOUS_MOVED_COUNT=

progress() { printf '>>> [%s] %s\n' "$(date -u +%H:%M:%S)" "$*"; }
step_sha() { sha256sum "$STEP_DIR/step-code.sh" | awk '{print $1}'; }
value() { awk -F= -v k="$1" '$1==k {v=substr($0,index($0,"=")+1)} END{print v}' "$2"; }

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

publish_files() {
    local label="$1" log="$2"
    shift 2
    "$PUBLISHER" "$label" "$@" >"$log" 2>&1
}

http_get() {
    curl --fail --silent --show-error --location \
      --resolve "$RESOLVE" --connect-timeout 5 --max-time 30 \
      "$1" -o "$2"
}

finish() {
    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=$FACTS_URL" \
      "VERIFY_URL=$VERIFY_URL" \
      "ROLLBACK_URL=$ROLLBACK_URL" \
      "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"
    finish
    exit 1
}

progress "verify exact transferred code and approved publisher"
cp "$0" "$STEP_DIR/step-code.sh"
cp "$BUNDLE_ZIP" "$STEP_DIR/"
cp "$BUNDLE_ROOT/manifest.sha256" "$STEP_DIR/bundle-manifest.sha256"

ACTUAL_BUNDLE_SHA="$(sha256sum "$BUNDLE_ZIP" | awk '{print $1}')"
ACTUAL_INSTALLER_SHA="$(sha256sum "$BUNDLE_ROOT/install.sh" | awk '{print $1}')"

cat >"$STEP_DIR/transfer-verify.txt" <<EOF
EXPECTED_BUNDLE_SHA256=$EXPECTED_BUNDLE_SHA256
ACTUAL_BUNDLE_SHA256=$ACTUAL_BUNDLE_SHA
EXPECTED_INSTALLER_SHA256=$EXPECTED_INSTALLER_SHA256
ACTUAL_INSTALLER_SHA256=$ACTUAL_INSTALLER_SHA
EOF

[[ "$ACTUAL_BUNDLE_SHA" == "$EXPECTED_BUNDLE_SHA256" ]] || { COPY_VERIFY=FAIL; fail STOP_COPY_OR_TRANSFER_ERROR; }
[[ "$ACTUAL_INSTALLER_SHA" == "$EXPECTED_INSTALLER_SHA256" ]] || { COPY_VERIFY=FAIL; fail STOP_COPY_OR_TRANSFER_ERROR; }
(
    cd "$BUNDLE_ROOT"
    sha256sum -c manifest.sha256
) >>"$STEP_DIR/transfer-verify.txt" 2>&1 || { COPY_VERIFY=FAIL; fail STOP_COPY_OR_TRANSFER_ERROR; }
bash -n "$STEP_DIR/step-code.sh"
[[ "$(sha256sum "$PUBLISHER" | awk '{print $1}')" == "$EXPECTED_PUBLISHER_SHA" ]] || fail STOP_PUBLISHER_SHA256_MISMATCH
echo "COPY_VERIFY=PASS" >>"$STEP_DIR/transfer-verify.txt"
echo "STEP_SHA256=$(step_sha)" >>"$STEP_DIR/transfer-verify.txt"

progress "recover already published current VM101 source URL"
PREV="$(
  find /opt/router-ops/steps -maxdepth 1 -type d \
    -name '*R05S_PUBLICATION_SALVAGE_R01' | sort | tail -n1
)"
[[ -n "$PREV" ]] || fail STOP_PREVIOUS_SALVAGE_NOT_FOUND
MCLOG="$PREV/router-machine-close.log"
[[ -f "$MCLOG" ]] || fail STOP_MACHINE_CLOSE_LOG_NOT_FOUND
VM101_PUBLIC_URL="$(value VM101_PUBLIC_URL "$MCLOG")"
[[ -n "$VM101_PUBLIC_URL" ]] || fail STOP_VM101_SOURCE_URL_NOT_FOUND

progress "independently verify live VM101 storage, source and runtime"
ssh pve-mgts \
  "ssh -o BatchMode=yes -o ConnectTimeout=10 -i /root/.ssh/pve_to_openwrt_mgts_ed25519 root@10.71.100.2 'sh -s'" \
  >"$STEP_DIR/vm101-live-state.txt" <<'VM101'
set +e
echo "COMMIT=$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse HEAD)"
echo "TREE=$(git --git-dir=/root/.vm101-source.git --work-tree=/ rev-parse 'HEAD^{tree}')"
echo STATUS_BEGIN
git --git-dir=/root/.vm101-source.git --work-tree=/ status --short
echo STATUS_END
echo "DISK_SECTORS=$(cat /sys/class/block/sda/size)"
echo "PART_SECTORS=$(cat /sys/class/block/sda2/size)"
df -Pk /
echo ENDPOINTS_BEGIN
for iface in vpn1 vpn2 vpn3 vpn4 vpn5; do
    endpoint="$(amneziawg show "$iface" endpoints 2>/dev/null | awk 'NR==1 {print $2}')"
    printf '%s\t%s\n' "$iface" "$endpoint"
done
echo ENDPOINTS_END
/etc/init.d/router-egress-health-repair enabled >/dev/null 2>&1
echo "WATCHER_ENABLED_RC=$?"
/etc/init.d/router-egress-health-repair running >/dev/null 2>&1
echo "WATCHER_RUNNING_RC=$?"
VM101

COMMIT="$(value COMMIT "$STEP_DIR/vm101-live-state.txt")"
TREE="$(value TREE "$STEP_DIR/vm101-live-state.txt")"
DISK_SECTORS="$(value DISK_SECTORS "$STEP_DIR/vm101-live-state.txt")"
PART_SECTORS="$(value PART_SECTORS "$STEP_DIR/vm101-live-state.txt")"
ROOT_KB="$(awk '$6=="/"{print $2}' "$STEP_DIR/vm101-live-state.txt")"
ROOT_AVAIL_KB="$(awk '$6=="/"{print $4}' "$STEP_DIR/vm101-live-state.txt")"
ENDPOINT_COUNT="$(awk '/ENDPOINTS_BEGIN/{f=1;next}/ENDPOINTS_END/{f=0}f && $2!=""{n++} END{print n+0}' "$STEP_DIR/vm101-live-state.txt")"

[[ "$COMMIT" == "$EXPECTED_COMMIT" ]] || fail STOP_VM101_COMMIT_MISMATCH
[[ "$TREE" == "$EXPECTED_TREE" ]] || fail STOP_VM101_TREE_MISMATCH
[[ "$DISK_SECTORS" == 2097152 ]] || fail STOP_DISK_POSTCHECK_FAILED
[[ "$PART_SECTORS" == 2063360 ]] || fail STOP_PARTITION_POSTCHECK_FAILED
[[ "$ROOT_KB" -ge 980000 ]] || fail STOP_FILESYSTEM_POSTCHECK_FAILED
[[ "$ROOT_AVAIL_KB" -ge 850000 ]] || fail STOP_FILESYSTEM_POSTCHECK_FAILED
[[ "$ENDPOINT_COUNT" -eq 5 ]] || fail STOP_ENDPOINTS_NOT_READY
grep -q '^WATCHER_ENABLED_RC=0$' "$STEP_DIR/vm101-live-state.txt" || fail STOP_WATCHER_NOT_ENABLED
grep -q '^WATCHER_RUNNING_RC=0$' "$STEP_DIR/vm101-live-state.txt" || fail STOP_WATCHER_NOT_RUNNING
awk '/STATUS_BEGIN/{f=1;next}/STATUS_END/{f=0}f && NF{bad=1} END{exit bad?1:0}' "$STEP_DIR/vm101-live-state.txt" || fail STOP_VM101_WORKTREE_NOT_CLEAN

ssh pve-mgts '
qm status 101
qm config 101 | grep -E "^(sata1|parent):"
qm listsnapshot 101 | tail -n 8
' >"$STEP_DIR/pve-live-state.txt"
grep -q '^status: running$' "$STEP_DIR/pve-live-state.txt" || fail STOP_PVE_STATUS_FAILED
grep -Eq '^sata1: .*size=1G([,[:space:]]|$)' "$STEP_DIR/pve-live-state.txt" || fail STOP_PVE_DISK_FAILED
grep -q 'R14D5_20260716-125155' "$STEP_DIR/pve-live-state.txt" || fail STOP_SNAPSHOT_PROOF_FAILED

cat >"$STEP_DIR/system-model.txt" <<EOF
VM101_DISK_SIZE=1G
DISK_SECTORS=$DISK_SECTORS
PARTITION_SECTORS=$PART_SECTORS
ROOT_KB=$ROOT_KB
ROOT_AVAILABLE_KB=$ROOT_AVAIL_KB
GIT_COMMIT=$COMMIT
GIT_TREE=$TREE
ENDPOINT_COUNT=$ENDPOINT_COUNT
WATCHER_ENABLED=true
WATCHER_RUNNING=true
ROLLBACK_SNAPSHOT=R14D5_20260716-125155
EOF

cat >"$STEP_DIR/vm101-model.md" <<EOF
# VM101 current model after R14D R05
- sata1: 1 GiB
- disk sectors: $DISK_SECTORS
- root partition sectors: $PART_SECTORS
- ext4 root: $ROOT_KB KiB
- available root space: $ROOT_AVAIL_KB KiB
- Machine Git: $COMMIT
- tree: $TREE
- VPN endpoints: $ENDPOINT_COUNT/5
- watcher: enabled and running
- retained rollback snapshot: R14D5_20260716-125155
EOF

cat >"$STEP_DIR/vm101-methods.md" <<'EOF'
# VM101 verified disk expansion method
VM stop → snapshot name <=40 chars → qm resize → qemu-nbd → parted →
e2fsck → offline resize2fs → e2fsck → NBD disconnect → VM start.
Online resize of mounted OpenWrt root returned EINVAL.
After successful offline resize and NBD disconnect, late runtime or publication checks
must never trigger automatic storage rollback.
EOF

cat >"$STEP_DIR/access-routes.txt" <<'EOF'
PVE_ROUTE=ops@router-ops -> ssh pve-mgts
VM101_ROUTE=ops@router-ops -> pve-mgts -> root@10.71.100.2
ACCESS_MAP_CHANGED=false
EOF

cat >"$STEP_DIR/rollback.sh" <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
echo 'RESULT=STOP_MANUAL_APPROVAL_REQUIRED'
echo 'SNAPSHOT=R14D5_20260716-125155'
echo 'Rollback restores the previous 124M disk and interrupts VM101 traffic.'
exit 1
EOF
chmod 700 "$STEP_DIR/rollback.sh"

progress "publish fresh VM101 model"
publish_files \
  "vm101_model_disk_expansion_1g_r14d_r05" \
  "$STEP_DIR/publish-model.log" \
  "$STEP_DIR/vm101-model.md" \
  "$STEP_DIR/vm101-live-state.txt" \
  "$STEP_DIR/pve-live-state.txt" \
  "$STEP_DIR/system-model.txt"
VM101_MODEL_URL="$(url_from_log VM101_MODEL_URL "$STEP_DIR/publish-model.log")"

progress "publish fresh VM101 methods"
publish_files \
  "vm101_methods_disk_expansion_1g_r14d_r05" \
  "$STEP_DIR/publish-methods.log" \
  "$STEP_DIR/vm101-methods.md" \
  "$STEP_DIR/access-routes.txt" \
  "$STEP_DIR/rollback.sh"
VM101_METHODS_URL="$(url_from_log VM101_METHODS_URL "$STEP_DIR/publish-methods.log")"

RESULT="PASS_VM101_DISK_EXPANSION_R05_SALVAGED_AND_PUBLISHED"

cat >"$STEP_DIR/facts.json" <<EOF
{
  "step_id": "$STEP_ID",
  "result": "$RESULT",
  "core_change_repeated": false,
  "runtime_impact": false,
  "disk_sectors": $DISK_SECTORS,
  "partition_sectors": $PART_SECTORS,
  "root_kb": $ROOT_KB,
  "root_available_kb": $ROOT_AVAIL_KB,
  "commit": "$COMMIT",
  "tree": "$TREE",
  "endpoint_count": $ENDPOINT_COUNT,
  "watcher_enabled": true,
  "watcher_running": true,
  "late_rollback_disabled": true
}
EOF

cat >"$STEP_DIR/report.txt" <<EOF
STEP_ID=$STEP_ID
RESULT=$RESULT
CORE_RESULT=PASS_VM101_DISK_EXPANDED_TO_1G_OFFLINE_EXT4
CORE_CHANGE_REPEATED=false
DISK_SECTORS=$DISK_SECTORS
PARTITION_SECTORS=$PART_SECTORS
ROOT_KB=$ROOT_KB
ROOT_AVAILABLE_KB=$ROOT_AVAIL_KB
GIT_COMMIT=$COMMIT
GIT_TREE=$TREE
ENDPOINT_COUNT=$ENDPOINT_COUNT
WATCHER_ENABLED=true
WATCHER_RUNNING=true
LATE_ROLLBACK_DISABLED=true
RUNTIME_IMPACT=false
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
GLOBAL_PROJECT_PLAN_URL=$GLOBAL_PROJECT_PLAN_URL
ACCESS_MAP_URL=$ACCESS_MAP_URL
VM100_PUBLIC_URL=$VM100_PUBLIC_URL
VM121_PUBLIC_URL=$VM121_PUBLIC_URL
VM130_SOURCE_URL=$VM130_SOURCE_URL
COPY_VERIFY=PASS
STEP_SHA256=$(step_sha)
EOF

cat >"$STEP_DIR/verify.txt" <<EOF
RESULT=$RESULT
COPY_VERIFY=PASS
STEP_SHA256=$(step_sha)
PUBLISHER_SHA256=$(sha256sum "$PUBLISHER" | awk '{print $1}')
EXPECTED_COMMIT=$EXPECTED_COMMIT
ACTUAL_COMMIT=$COMMIT
EXPECTED_TREE=$EXPECTED_TREE
ACTUAL_TREE=$TREE
DISK_CHECK=PASS
PARTITION_CHECK=PASS
FILESYSTEM_CHECK=PASS
GIT_CHECK=PASS
ENDPOINT_CHECK=PASS
WATCHER_CHECK=PASS
CORE_CHANGE_REPEATED=false
RUNTIME_IMPACT=false
EOF

progress "publish formal STEP report"
publish_files \
  "step050m07r14d_vm101_disk_expansion_1g_r05s2_publish_continuation" \
  "$STEP_DIR/publish-step.log" \
  "$STEP_DIR/step-code.sh" \
  "$STEP_DIR/report.txt" \
  "$STEP_DIR/facts.json" \
  "$STEP_DIR/verify.txt" \
  "$STEP_DIR/rollback.sh" \
  "$STEP_DIR/system-model.txt" \
  "$STEP_DIR/vm101-model.md" \
  "$STEP_DIR/vm101-methods.md" \
  "$STEP_DIR/access-routes.txt" \
  "$STEP_DIR/vm101-live-state.txt" \
  "$STEP_DIR/pve-live-state.txt" \
  "$STEP_DIR/main.log" \
  "$STEP_DIR/transfer-verify.txt"

STEP_REPORT_URL="$(url_from_log STEP_REPORT_URL "$STEP_DIR/publish-step.log")"
PUBLIC_OBJECT_KIND="$(value PUBLIC_OBJECT_KIND "$STEP_DIR/publish-step.log")"
PREVIOUS_MOVED_COUNT="$(value PREVIOUS_MOVED_COUNT "$STEP_DIR/publish-step.log")"
FACTS_URL="${STEP_REPORT_URL}facts.json"
VERIFY_URL="${STEP_REPORT_URL}verify.txt"
ROLLBACK_URL="${STEP_REPORT_URL}rollback.sh"

progress "external HTTPS verification"
http_get "$STEP_REPORT_URL" "$STEP_DIR/http-step-index.html"
http_get "$FACTS_URL" "$STEP_DIR/http-facts.json"
http_get "$VERIFY_URL" "$STEP_DIR/http-verify.txt"
http_get "$ROLLBACK_URL" "$STEP_DIR/http-rollback.sh"
http_get "$VM101_PUBLIC_URL" "$STEP_DIR/http-source-index.html"
http_get "$VM101_MODEL_URL" "$STEP_DIR/http-model-index.html"
http_get "$VM101_METHODS_URL" "$STEP_DIR/http-methods-index.html"

cmp "$STEP_DIR/facts.json" "$STEP_DIR/http-facts.json"
cmp "$STEP_DIR/verify.txt" "$STEP_DIR/http-verify.txt"
cmp "$STEP_DIR/rollback.sh" "$STEP_DIR/http-rollback.sh"

finish
