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

STEP_ID="${STEP_ID:?}"
STEP_DIR="${STEP_DIR:?}"
STEP_SHA256="${STEP_SHA256:?}"
BUNDLE_ROOT="${BUNDLE_ROOT:?}"

BASE="/opt/router-ops"
DOMAIN="reports.secret-studio.ru"
PUBLIC_BASE="https://$DOMAIN"
RESOLVE_IP="10.71.100.84"
TOKEN_FILE="$BASE/public/.router-public-token"
PAYLOAD="$BUNDLE_ROOT/payload"
EXPECTED_CURRENT="$BUNDLE_ROOT/expected-current.sha256"
PAYLOAD_MANIFEST="$BUNDLE_ROOT/manifest.sha256"

PUBLISHER="$BASE/bin/router-publish-report"
REINDEX="$BASE/bin/router-public-reindex"
CADDYFILE="/etc/caddy/Caddyfile"
ACCESS_MAP="$BASE/inventory/access-map.md"

REPORT="$STEP_DIR/report.txt"
FACTS="$STEP_DIR/facts.json"
VERIFY="$STEP_DIR/verify.txt"
ROLLBACK="$STEP_DIR/rollback.sh"
LOG="$STEP_DIR/main.log"
PROGRESS="$STEP_DIR/progress.log"
MODEL="$STEP_DIR/system-model.txt"
METHODS="$STEP_DIR/methods.txt"
ACCESS_ROUTES="$STEP_DIR/access-routes.txt"
INSTALLED_SHA="$STEP_DIR/installed-sha256.txt"
BEFORE_STATE="$STEP_DIR/publication-before.txt"
AFTER_STATE="$STEP_DIR/publication-after.txt"
BACKUP="$STEP_DIR/rollback/original"
SANDBOX="$STEP_DIR/sandbox"
SAFE_STEP="$STEP_DIR/publish-step"
SAFE_SOURCE="$STEP_DIR/publish-vm130-source"
SAFE_ACCESS="$STEP_DIR/publish-access-map"

LOCAL_M07_PLAN_URL="$PUBLIC_BASE/latest/20260713-193942_local_architecture_plan_vm101_autonomous_hmn_recovery/"
GLOBAL_PROJECT_PLAN_URL="$PUBLIC_BASE/latest/20260711-123348_global_project_plan_wg_paid/"
VM101_MODEL_URL="$PUBLIC_BASE/latest/20260713-192030_vm101_model_hmn_download_selector_r13a/"
VM101_METHODS_URL="$PUBLIC_BASE/latest/20260713-193942_vm101_methods_machine_git_count_contract_r13b/"
LATEST_ROOT_URL="$PUBLIC_BASE/latest/"
OLD_ROOT_URL="$PUBLIC_BASE/old/"

TRANSACTION_ACTIVE=0
ROLLBACK_RUNNING=0
CADDY_RELOADED=false
ORIG_STEP_FOLDER=""
ORIG_VM130_FOLDER=""
ORIG_ACCESS_FOLDER=""
NEW_STEP_FOLDER=""
NEW_VM130_FOLDER=""
NEW_ACCESS_FOLDER=""
RESULT="STOP_R05D_UNEXPECTED_ERROR"
STOP_REASON="unexpected_error"

mkdir -p "$STEP_DIR" "$BACKUP" "$SANDBOX" "$SAFE_STEP" "$SAFE_SOURCE" "$SAFE_ACCESS"
chmod 700 "$STEP_DIR" "$BACKUP" "$SANDBOX" "$SAFE_STEP" "$SAFE_SOURCE" "$SAFE_ACCESS"
exec > >(tee -a "$LOG") 2>&1

progress() {
    printf '>>> [%s] %s\n' "$(date '+%F %T')" "$*" | tee -a "$PROGRESS"
}

read_field() {
    local key="$1"
    local file="$2"
    awk -F= -v key="$key" '$1 == key {print substr($0,index($0,"=")+1)}' "$file" | tail -n 1
}

one_latest_folder() {
    local kind="$1"
    local -a rows=()

    case "$kind" in
        step)
            mapfile -t rows < <(find "$LATEST" -mindepth 1 -maxdepth 1 -type d -name '*_step*' -printf '%f\n' | LC_ALL=C sort)
            ;;
        vm130)
            mapfile -t rows < <(find "$LATEST" -mindepth 1 -maxdepth 1 -type d -name '*_vm130_router_ops_source_snapshot*' -printf '%f\n' | LC_ALL=C sort)
            ;;
        access)
            mapfile -t rows < <(find "$LATEST" -mindepth 1 -maxdepth 1 -type d \( -name '*_access_map_*' -o -name '*_xs_map_*' \) -printf '%f\n' | LC_ALL=C sort)
            ;;
        *)
            return 2
            ;;
    esac

    [ "${#rows[@]}" -eq 1 ] || return 1
    printf '%s\n' "${rows[0]}"
}

move_new_to_old() {
    local folder="$1"
    local rollback_ts
    [ -n "$folder" ] || return 0
    [ -d "$LATEST/$folder" ] || return 0

    if [ -e "$OLD/$folder" ]; then
        rollback_ts="$(date -u +%Y%m%d-%H%M%S)"
        mv "$LATEST/$folder" "$OLD/${rollback_ts}_rollback_$folder"
    else
        mv "$LATEST/$folder" "$OLD/$folder"
    fi
}

restore_old_to_latest() {
    local folder="$1"
    [ -n "$folder" ] || return 0
    if [ -d "$OLD/$folder" ] && [ ! -e "$LATEST/$folder" ]; then
        mv "$OLD/$folder" "$LATEST/$folder"
    fi
}

restore_core_files() {
    local rel

    for rel in \
        opt/router-ops/bin/router-publish-report \
        opt/router-ops/bin/router-public-reindex \
        opt/router-ops/bin/router-public-url \
        opt/router-ops/bin/router-vm100-git-source-publish \
        opt/router-ops/bin/router-vm101-git-source-publish \
        opt/router-ops/bin/router-vm121-git-source-publish \
        opt/router-ops/bin/router-machine-close \
        opt/router-ops/state/current-vm100-git-source.env \
        opt/router-ops/state/current-vm101-git-source.env \
        opt/router-ops/state/current-vm121-git-source.env \
        opt/router-ops/state/current-machine-close-vm100.env \
        opt/router-ops/state/current-machine-close-vm101.env \
        opt/router-ops/state/current-machine-close-vm121.env \
        opt/router-ops/inventory/access-map.md
    do
        [ -e "$BACKUP/$rel" ] || continue
        cp -a "$BACKUP/$rel" "/$rel"
    done

    if [ -e "$BACKUP/etc/caddy/Caddyfile" ]; then
        sudo -n install -o root -g root -m 0644 "$BACKUP/etc/caddy/Caddyfile" "$CADDYFILE"
        sudo -n caddy validate --config "$CADDYFILE"
        sudo -n systemctl reload caddy
    fi
}

perform_rollback() {
    [ "$ROLLBACK_RUNNING" -eq 0 ] || return 0
    ROLLBACK_RUNNING=1
    set +e

    progress "automatic rollback"
    move_new_to_old "$NEW_STEP_FOLDER"
    move_new_to_old "$NEW_ACCESS_FOLDER"
    move_new_to_old "$NEW_VM130_FOLDER"
    restore_old_to_latest "$ORIG_STEP_FOLDER"
    restore_old_to_latest "$ORIG_ACCESS_FOLDER"
    restore_old_to_latest "$ORIG_VM130_FOLDER"
    restore_core_files
    "$REINDEX" >/dev/null 2>&1 || true

    ROLLBACK_RUNNING=0
}

on_error() {
    local line="$1"
    local rc="$2"
    trap - ERR INT TERM
    set +e

    STOP_REASON="line_${line}_rc_${rc}"
    if [ "$TRANSACTION_ACTIVE" -eq 1 ]; then
        perform_rollback
        RESULT="STOP_R05D_APPLY_FAILED_ROLLED_BACK"
    else
        RESULT="STOP_R05D_PREFLIGHT_OR_BUILD_FAILED"
    fi

    cat > "$REPORT" <<EOF_STOP
RESULT=$RESULT
STEP_ID=$STEP_ID
STOP_REASON=$STOP_REASON
STEP_REPORT_URL=PENDING_NOT_PUBLISHED
COPY_VERIFY=PASS
STEP_SHA256=$STEP_SHA256
STEP_WAS_NOT_STARTED=false
RUNTIME_IMPACT=$CADDY_RELOADED
EOF_STOP

    printf 'RESULT=%s\n' "$RESULT"
    printf 'STOP_REASON=%s\n' "$STOP_REASON"
    printf 'STEP_REPORT_URL=PENDING_NOT_PUBLISHED\n'
    printf 'COPY_VERIFY=PASS\n'
    printf 'STEP_SHA256=%s\n' "$STEP_SHA256"
    printf 'RUNTIME_IMPACT=%s\n' "$CADDY_RELOADED"
    exit "$rc"
}
trap 'on_error "$LINENO" "$?"' ERR
trap 'on_error "$LINENO" 130' INT TERM

progress "preflight and exact bundle verification"

for cmd in \
    awk base64 basename bash chmod cmp cp curl date dirname find flock grep gzip id install \
    mkdir mv python3 sed sha256sum sort stat sudo systemctl tar tee tr wc xargs
 do
    command -v "$cmd" >/dev/null 2>&1
 done

[ "$(id -un)" = "ops" ]
sudo -n true
[ -f "$PAYLOAD_MANIFEST" ]
[ -f "$EXPECTED_CURRENT" ]
[ -f "$TOKEN_FILE" ]
[ -f "$ACCESS_MAP" ]

(
    cd "$BUNDLE_ROOT"
    sha256sum -c manifest.sha256
) > "$STEP_DIR/payload-manifest-verify.txt"

sha256sum -c "$EXPECTED_CURRENT" > "$STEP_DIR/current-state-verify.txt"

sh -n "$PAYLOAD/opt/router-ops/bin/router-publish-report"
sh -n "$PAYLOAD/opt/router-ops/bin/router-public-reindex"
sh -n "$PAYLOAD/opt/router-ops/bin/router-public-url"
bash -n "$PAYLOAD/opt/router-ops/bin/router-vm100-git-source-publish"
bash -n "$PAYLOAD/opt/router-ops/bin/router-vm101-git-source-publish"
bash -n "$PAYLOAD/opt/router-ops/bin/router-vm121-git-source-publish"
bash -n "$PAYLOAD/opt/router-ops/bin/router-machine-close"

if grep -RInE 'trycloudflare|TRYCF|QUICK_TUNNEL|TUNNEL_ENV|CLOUDFLARE_PUBLISHED|cloudflared-quick' \
    "$PAYLOAD/opt/router-ops/bin" "$PAYLOAD/opt/router-ops/state" "$PAYLOAD/etc/caddy/Caddyfile"
then
    echo "ERROR: legacy publication reference in final payload" >&2
    false
fi

sudo -n caddy validate --config "$PAYLOAD/etc/caddy/Caddyfile" > "$STEP_DIR/caddy-candidate-validate.txt" 2>&1

TOKEN="$(cat "$TOKEN_FILE")"
ROOT="$BASE/public/r/$TOKEN"
LATEST="$ROOT/latest"
OLD="$ROOT/old"
[ -d "$LATEST" ]
[ -d "$OLD" ]

ORIG_STEP_FOLDER="$(one_latest_folder step)"
ORIG_VM130_FOLDER="$(one_latest_folder vm130)"
ORIG_ACCESS_FOLDER="$(one_latest_folder access)"

cat > "$BEFORE_STATE" <<EOF_BEFORE
ORIG_STEP_FOLDER=$ORIG_STEP_FOLDER
ORIG_VM130_FOLDER=$ORIG_VM130_FOLDER
ORIG_ACCESS_FOLDER=$ORIG_ACCESS_FOLDER
EOF_BEFORE

progress "local sandbox tests of complete final files"

SB_BASE="$SANDBOX/base"
SB_TOKEN="0123456789abcdef0123456789abcdef"
mkdir -p "$SB_BASE/bin" "$SB_BASE/public" "$SB_BASE/state"
cp -a "$PAYLOAD/opt/router-ops/bin/router-public-reindex" "$SB_BASE/bin/router-public-reindex"
chmod 755 "$SB_BASE/bin/router-public-reindex"
printf '%s\n' "$SB_TOKEN" > "$SB_BASE/public/.router-public-token"
printf 'alpha\n' > "$SANDBOX/a.txt"
printf 'beta\n' > "$SANDBOX/b.txt"

ROUTER_OPS_BASE="$SB_BASE" ROUTER_PUBLIC_HTTP_CHECK=0 \
ROUTER_PUBLIC_TIMESTAMP=20260715-220001 \
"$PAYLOAD/opt/router-ops/bin/router-publish-report" step_r05d_one "$SANDBOX/a.txt" \
    > "$SANDBOX/publish-one.txt"

ROUTER_OPS_BASE="$SB_BASE" ROUTER_PUBLIC_HTTP_CHECK=0 \
ROUTER_PUBLIC_TIMESTAMP=20260715-220002 \
"$PAYLOAD/opt/router-ops/bin/router-publish-report" step_r05d_two "$SANDBOX/b.txt" \
    > "$SANDBOX/publish-two.txt"

SB_LATEST="$SB_BASE/public/r/$SB_TOKEN/latest"
SB_OLD="$SB_BASE/public/r/$SB_TOKEN/old"
[ "$(find "$SB_LATEST" -mindepth 1 -maxdepth 1 -type d -name '*_step*' | wc -l)" -eq 1 ]
[ "$(find "$SB_OLD" -mindepth 1 -maxdepth 1 -type d -name '*_step*' | wc -l)" -eq 1 ]
grep -Fqx 'CURRENT_KIND_COUNT=1' "$SANDBOX/publish-two.txt"
grep -Fqx 'LATEST_INDEX_CONTAINS_FOLDER=true' "$SANDBOX/publish-two.txt"
grep -Fqx 'HTTP_POSTCHECK=SKIPPED' "$SANDBOX/publish-two.txt"

ROUTER_OPS_BASE="$SANDBOX/url-base" mkdir -p "$SANDBOX/url-base/public"
printf 'testtoken\n' > "$SANDBOX/url-base/public/.router-public-token"
ROUTER_OPS_BASE="$SANDBOX/url-base" "$PAYLOAD/opt/router-ops/bin/router-public-url" \
    > "$SANDBOX/public-url.txt"
grep -Fqx 'PUBLIC_ROOT_URL=https://reports.secret-studio.ru/' "$SANDBOX/public-url.txt"
grep -Fqx 'LATEST_ROOT_URL=https://reports.secret-studio.ru/latest/' "$SANDBOX/public-url.txt"

progress "backup current partial R05C2 state"

for rel in \
    opt/router-ops/bin/router-publish-report \
    opt/router-ops/bin/router-public-reindex \
    opt/router-ops/bin/router-public-url \
    opt/router-ops/bin/router-vm100-git-source-publish \
    opt/router-ops/bin/router-vm101-git-source-publish \
    opt/router-ops/bin/router-vm121-git-source-publish \
    opt/router-ops/bin/router-machine-close \
    opt/router-ops/state/current-vm100-git-source.env \
    opt/router-ops/state/current-vm101-git-source.env \
    opt/router-ops/state/current-vm121-git-source.env \
    opt/router-ops/state/current-machine-close-vm100.env \
    opt/router-ops/state/current-machine-close-vm101.env \
    opt/router-ops/state/current-machine-close-vm121.env \
    opt/router-ops/inventory/access-map.md \
    etc/caddy/Caddyfile
 do
    mkdir -p "$BACKUP/$(dirname "$rel")"
    cp -a "/$rel" "$BACKUP/$rel"
 done

TRANSACTION_ACTIVE=1

progress "install complete final files, not runtime patches"

for name in \
    router-publish-report \
    router-public-reindex \
    router-public-url \
    router-vm100-git-source-publish \
    router-vm101-git-source-publish \
    router-vm121-git-source-publish \
    router-machine-close
 do
    install -m 0755 "$PAYLOAD/opt/router-ops/bin/$name" "$BASE/bin/$name"
 done

for name in \
    current-vm100-git-source.env \
    current-vm101-git-source.env \
    current-vm121-git-source.env \
    current-machine-close-vm100.env \
    current-machine-close-vm101.env \
    current-machine-close-vm121.env
 do
    install -m 0600 "$PAYLOAD/opt/router-ops/state/$name" "$BASE/state/$name"
 done

install -m 0644 "$PAYLOAD/opt/router-ops/inventory/access-map.md" "$ACCESS_MAP"
sudo -n install -o root -g root -m 0644 "$PAYLOAD/etc/caddy/Caddyfile" "$CADDYFILE"

progress "validate installed files and reload Caddy"

for name in \
    router-publish-report \
    router-public-reindex \
    router-public-url \
    router-vm100-git-source-publish \
    router-vm101-git-source-publish \
    router-vm121-git-source-publish \
    router-machine-close
 do
    cmp -s "$PAYLOAD/opt/router-ops/bin/$name" "$BASE/bin/$name"
 done

for name in \
    current-vm100-git-source.env \
    current-vm101-git-source.env \
    current-vm121-git-source.env \
    current-machine-close-vm100.env \
    current-machine-close-vm101.env \
    current-machine-close-vm121.env
 do
    cmp -s "$PAYLOAD/opt/router-ops/state/$name" "$BASE/state/$name"
 done

cmp -s "$PAYLOAD/opt/router-ops/inventory/access-map.md" "$ACCESS_MAP"
cmp -s "$PAYLOAD/etc/caddy/Caddyfile" "$CADDYFILE"

sh -n "$BASE/bin/router-publish-report"
sh -n "$BASE/bin/router-public-reindex"
sh -n "$BASE/bin/router-public-url"
bash -n "$BASE/bin/router-vm100-git-source-publish"
bash -n "$BASE/bin/router-vm101-git-source-publish"
bash -n "$BASE/bin/router-vm121-git-source-publish"
bash -n "$BASE/bin/router-machine-close"

sudo -n caddy validate --config "$CADDYFILE" > "$STEP_DIR/caddy-installed-validate.txt" 2>&1
sudo -n systemctl reload caddy
CADDY_RELOADED=true
sudo -n systemctl is-active --quiet caddy
"$REINDEX" > "$STEP_DIR/reindex-after-install.txt"

progress "independent active-state and HTTPS checks"

if grep -RInE 'trycloudflare|TRYCF|QUICK_TUNNEL|TUNNEL_ENV|CLOUDFLARE_PUBLISHED|cloudflared-quick' \
    "$BASE/bin/router-publish-report" \
    "$BASE/bin/router-public-url" \
    "$BASE/bin/router-vm100-git-source-publish" \
    "$BASE/bin/router-vm101-git-source-publish" \
    "$BASE/bin/router-vm121-git-source-publish" \
    "$BASE/bin/router-machine-close" \
    "$BASE/state/current-vm100-git-source.env" \
    "$BASE/state/current-vm101-git-source.env" \
    "$BASE/state/current-vm121-git-source.env" \
    "$BASE/state/current-machine-close-vm100.env" \
    "$BASE/state/current-machine-close-vm101.env" \
    "$BASE/state/current-machine-close-vm121.env"
then
    echo "ERROR: legacy publication reference remains active" >&2
    false
fi

"$BASE/bin/router-public-url" > "$STEP_DIR/public-url-output.txt"
if grep -Eiq 'trycloudflare|TRYCF|QUICK_TUNNEL|CLOUDFLARE' "$STEP_DIR/public-url-output.txt"; then
    echo "ERROR: legacy output from router-public-url" >&2
    false
fi

grep -Fqx 'LATEST_ROOT_URL=https://reports.secret-studio.ru/latest/' "$STEP_DIR/public-url-output.txt"
grep -Fqx 'OLD_ROOT_URL=https://reports.secret-studio.ru/old/' "$STEP_DIR/public-url-output.txt"

curl -fsS --connect-timeout 5 --max-time 30 \
    --resolve "$DOMAIN:443:$RESOLVE_IP" \
    -D "$STEP_DIR/latest-headers.txt" \
    "$LATEST_ROOT_URL" > "$STEP_DIR/latest-index.html"

curl -fsS --connect-timeout 5 --max-time 30 \
    --resolve "$DOMAIN:443:$RESOLVE_IP" \
    "$OLD_ROOT_URL" > "$STEP_DIR/old-index.html"

grep -Eiq '^cache-control: .*no-store' "$STEP_DIR/latest-headers.txt"

for machine in vm100 vm101 vm121; do
    upper="$(printf '%s' "$machine" | tr '[:lower:]' '[:upper:]')"
    state="$BASE/state/current-${machine}-git-source.env"
    url="$(read_field "${upper}_GIT_SOURCE_URL" "$state")"
    dir="$(read_field "${upper}_GIT_SOURCE_PUBLIC_DIR" "$state")"
    case "$url" in
        "$PUBLIC_BASE/latest/"*/) ;;
        *) false ;;
    esac
    case "$dir" in
        "$LATEST/"*) ;;
        *) false ;;
    esac
    [ -d "$dir" ]
    curl -fsS --connect-timeout 5 --max-time 30 \
        --resolve "$DOMAIN:443:$RESOLVE_IP" \
        "$url" > "$STEP_DIR/${machine}-public-index.html"
done

find "$BASE/bin/router-publish-report" \
     "$BASE/bin/router-public-reindex" \
     "$BASE/bin/router-public-url" \
     "$BASE/bin/router-vm100-git-source-publish" \
     "$BASE/bin/router-vm101-git-source-publish" \
     "$BASE/bin/router-vm121-git-source-publish" \
     "$BASE/bin/router-machine-close" \
     "$CADDYFILE" \
     "$ACCESS_MAP" \
     "$BASE/state/current-vm100-git-source.env" \
     "$BASE/state/current-vm101-git-source.env" \
     "$BASE/state/current-vm121-git-source.env" \
     "$BASE/state/current-machine-close-vm100.env" \
     "$BASE/state/current-machine-close-vm101.env" \
     "$BASE/state/current-machine-close-vm121.env" \
     -maxdepth 0 -type f -print0 |
    sort -z |
    xargs -0 sha256sum > "$INSTALLED_SHA"

progress "build and publish fresh targeted VM130 source"

VM130_BUILD="$STEP_DIR/vm130-source-build/root"
mkdir -p "$VM130_BUILD"
for rel in \
    opt/router-ops/bin/router-publish-report \
    opt/router-ops/bin/router-public-reindex \
    opt/router-ops/bin/router-public-url \
    opt/router-ops/bin/router-vm100-git-source-publish \
    opt/router-ops/bin/router-vm101-git-source-publish \
    opt/router-ops/bin/router-vm121-git-source-publish \
    opt/router-ops/bin/router-machine-close \
    opt/router-ops/state/current-vm100-git-source.env \
    opt/router-ops/state/current-vm101-git-source.env \
    opt/router-ops/state/current-vm121-git-source.env \
    opt/router-ops/state/current-machine-close-vm100.env \
    opt/router-ops/state/current-machine-close-vm101.env \
    opt/router-ops/state/current-machine-close-vm121.env \
    opt/router-ops/inventory/access-map.md \
    etc/caddy/Caddyfile
 do
    mkdir -p "$VM130_BUILD/$(dirname "$rel")"
    cp -a "/$rel" "$VM130_BUILD/$rel"
 done

(
    cd "$VM130_BUILD"
    find . -type f -print0 | LC_ALL=C sort -z | xargs -0 sha256sum
) > "$STEP_DIR/vm130-source-manifest.sha256"

VM130_ARCHIVE="$STEP_DIR/vm130-router-ops-r05d.tar.gz"
VM130_ARCHIVE_B64="$STEP_DIR/vm130-router-ops-r05d.tar.gz.b64.txt"
(
    cd "$VM130_BUILD"
    tar --sort=name --mtime='UTC 1970-01-01' --owner=0 --group=0 --numeric-owner -cf - .
) | gzip -n -9 > "$VM130_ARCHIVE"
gzip -t "$VM130_ARCHIVE"
base64 "$VM130_ARCHIVE" > "$VM130_ARCHIVE_B64"
VM130_DECODED="$STEP_DIR/vm130-router-ops-r05d.decoded.tar.gz"
base64 -d "$VM130_ARCHIVE_B64" > "$VM130_DECODED"
cmp -s "$VM130_ARCHIVE" "$VM130_DECODED"
gzip -t "$VM130_DECODED"
VM130_EXTRACT="$STEP_DIR/vm130-source-extract-verify"
mkdir -p "$VM130_EXTRACT"
tar -xzf "$VM130_DECODED" -C "$VM130_EXTRACT"
(
    cd "$VM130_EXTRACT"
    sha256sum -c "$STEP_DIR/vm130-source-manifest.sha256"
) > "$STEP_DIR/vm130-source-extract-verify.txt"
VM130_ARCHIVE_SHA256="$(sha256sum "$VM130_ARCHIVE" | awk '{print $1}')"
printf '%s  %s\n' "$VM130_ARCHIVE_SHA256" "$(basename "$VM130_ARCHIVE")" \
    > "$STEP_DIR/vm130-router-ops-r05d.tar.gz.sha256"

cat > "$SAFE_SOURCE/report.txt" <<EOF_VM130_REPORT
RESULT=PASS_VM130_TARGETED_SOURCE_BUILT
STEP_ID=$STEP_ID
SOURCE_SCOPE=publication pipeline, Caddyfile, current machine publication states, Access Map
ARCHIVE=$(basename "$VM130_ARCHIVE")
ARCHIVE_SHA256=$VM130_ARCHIVE_SHA256
RUNTIME_IMPACT=false
EOF_VM130_REPORT

python3 - "$SAFE_SOURCE/facts.json" "$VM130_ARCHIVE_SHA256" <<'PY_FACTS_VM130'
import json, sys
from pathlib import Path
Path(sys.argv[1]).write_text(json.dumps({
    "result": "PASS_VM130_TARGETED_SOURCE_BUILT",
    "scope": "publication pipeline, Caddyfile, current machine publication states, Access Map",
    "archive": "vm130-router-ops-r05d.tar.gz",
    "archive_sha256": sys.argv[2],
    "runtime_impact": False
}, indent=2) + "\n", encoding="utf-8")
PY_FACTS_VM130

cat > "$SAFE_SOURCE/verify.txt" <<EOF_VM130_VERIFY
ARCHIVE_SHA256=$VM130_ARCHIVE_SHA256
GZIP_TEST=PASS
BASE64_DECODE_COMPARE=PASS
EXTRACT_VERIFY=PASS
SOURCE_MANIFEST=PASS
SYNTAX_CHECK=PASS
ACTIVE_LEGACY_REFERENCE_CHECK=PASS
EOF_VM130_VERIFY

cp -a "$VM130_ARCHIVE" "$VM130_ARCHIVE_B64" \
    "$STEP_DIR/vm130-router-ops-r05d.tar.gz.sha256" \
    "$STEP_DIR/vm130-source-manifest.sha256" \
    "$SAFE_SOURCE/"
chmod 644 "$SAFE_SOURCE"/*

VM130_PUBLISH_OUT="$STEP_DIR/vm130-source-publisher-output.txt"
ROUTER_PUBLIC_BASE_URL="$PUBLIC_BASE" \
ROUTER_PUBLIC_RESOLVE_IP="$RESOLVE_IP" \
"$PUBLISHER" vm130_router_ops_source_snapshot_r05d "$SAFE_SOURCE"/* \
    > "$VM130_PUBLISH_OUT"

VM130_SOURCE_URL="$(read_field VM130_SOURCE_URL "$VM130_PUBLISH_OUT")"
VM130_PUBLISHED_DIR="$(read_field PUBLISHED_DIR "$VM130_PUBLISH_OUT")"
[ "$(read_field PUBLIC_OBJECT_KIND "$VM130_PUBLISH_OUT")" = "vm130-source" ]
[ "$(read_field CURRENT_KIND_COUNT "$VM130_PUBLISH_OUT")" = "1" ]
[ "$(read_field HTTP_POSTCHECK "$VM130_PUBLISH_OUT")" = "PASS" ]
[ -d "$VM130_PUBLISHED_DIR" ]
NEW_VM130_FOLDER="$(basename "$VM130_PUBLISHED_DIR")"

progress "publish refreshed canonical Access Map"

cat > "$SAFE_ACCESS/report.txt" <<EOF_ACCESS_REPORT
RESULT=PASS_ACCESS_MAP_REFRESHED
STEP_ID=$STEP_ID
CHANGE=permanent report publication route and corrected VM130/VM101 access notes
RUNTIME_IMPACT=false
EOF_ACCESS_REPORT

python3 - "$SAFE_ACCESS/facts.json" <<'PY_FACTS_ACCESS'
import json, sys
from pathlib import Path
Path(sys.argv[1]).write_text(json.dumps({
    "result": "PASS_ACCESS_MAP_REFRESHED",
    "change": "permanent report publication route and corrected VM130/VM101 access notes",
    "runtime_impact": False
}, indent=2) + "\n", encoding="utf-8")
PY_FACTS_ACCESS

cat > "$SAFE_ACCESS/verify.txt" <<EOF_ACCESS_VERIFY
ACCESS_MAP_FILE=/opt/router-ops/inventory/access-map.md
PERMANENT_ROOT=https://reports.secret-studio.ru/
LATEST_ROOT=https://reports.secret-studio.ru/latest/
OLD_ROOT=https://reports.secret-studio.ru/old/
VM121_DIRECT_ADMIN_WG=confirmed
VM130_TRANSIT_FOR_VM121=false
EOF_ACCESS_VERIFY

cp -a "$ACCESS_MAP" "$SAFE_ACCESS/access-map.md"
chmod 644 "$SAFE_ACCESS"/*

ACCESS_PUBLISH_OUT="$STEP_DIR/access-map-publisher-output.txt"
ROUTER_PUBLIC_BASE_URL="$PUBLIC_BASE" \
ROUTER_PUBLIC_RESOLVE_IP="$RESOLVE_IP" \
"$PUBLISHER" access_map_canonical_current "$SAFE_ACCESS"/* \
    > "$ACCESS_PUBLISH_OUT"

ACCESS_MAP_URL="$(read_field ACCESS_MAP_URL "$ACCESS_PUBLISH_OUT")"
ACCESS_PUBLISHED_DIR="$(read_field PUBLISHED_DIR "$ACCESS_PUBLISH_OUT")"
[ "$(read_field PUBLIC_OBJECT_KIND "$ACCESS_PUBLISH_OUT")" = "access-map" ]
[ "$(read_field CURRENT_KIND_COUNT "$ACCESS_PUBLISH_OUT")" = "1" ]
[ "$(read_field HTTP_POSTCHECK "$ACCESS_PUBLISH_OUT")" = "PASS" ]
[ -d "$ACCESS_PUBLISHED_DIR" ]
NEW_ACCESS_FOLDER="$(basename "$ACCESS_PUBLISHED_DIR")"

VM100_PUBLIC_URL="$(read_field VM100_GIT_SOURCE_URL "$BASE/state/current-vm100-git-source.env")"
VM101_PUBLIC_URL="$(read_field VM101_GIT_SOURCE_URL "$BASE/state/current-vm101-git-source.env")"
VM121_PUBLIC_URL="$(read_field VM121_GIT_SOURCE_URL "$BASE/state/current-vm121-git-source.env")"

progress "prepare complete final STEP report"

STEP_LABEL="step_direct_https_publisher_pipeline_fix_r05d"
STEP_PUBLISH_TS="$(date -u +%Y%m%d-%H%M%S)"
NEW_STEP_FOLDER="${STEP_PUBLISH_TS}_${STEP_LABEL}"
STEP_REPORT_URL="$PUBLIC_BASE/latest/$NEW_STEP_FOLDER/"
FACTS_URL="${STEP_REPORT_URL}facts.json"
VERIFY_URL="${STEP_REPORT_URL}verify.txt"
ROLLBACK_URL="${STEP_REPORT_URL}rollback.sh"

cat > "$MODEL" <<EOF_MODEL
SYSTEM=VM130 publication pipeline
BEFORE=partial R05C2 scripts active; Caddyfile and current URL states still legacy
AFTER=complete permanent-HTTPS files installed; Caddy indexes no-store; current states permanent; canonical VM130 source and Access Map refreshed
VM100_RUNTIME_CHANGE=false
VM101_RUNTIME_CHANGE=false
VM121_RUNTIME_CHANGE=false
EOF_MODEL

cat > "$METHODS" <<EOF_METHODS
SOURCE_INPUT=uploaded exact VM130 handoff archive sha256 fae2c4306e31ee335c0fbb21c2df77fc400c5c243c508d6d7c400ad8cfafbfcc
DELIVERY=complete prebuilt files in verified bundle; no runtime regex patching
CHECKS=exact-current SHA, payload manifest, syntax, Caddy validate, sandbox publication, byte compare, state URL checks, HTTPS, latest/old counts
EOF_METHODS

cat > "$ACCESS_ROUTES" <<EOF_ROUTES
VM130=192.168.30.84
VM130_MGTS_REPORT_IP=10.71.100.84
PUBLIC_ROOT=$PUBLIC_BASE/
LATEST_ROOT=$LATEST_ROOT_URL
OLD_ROOT=$OLD_ROOT_URL
LOCAL_TLS_RESOLVE=$DOMAIN:443:$RESOLVE_IP
VM121=192.168.30.83 direct admin WG 10.250.100.121/32
VM130_IS_NOT_VM121_TRANSIT=true
EOF_ROUTES

cat > "$ROLLBACK" <<EOF_ROLLBACK
#!/usr/bin/env bash
set -Eeuo pipefail
BASE="$BASE"
BACKUP="$BACKUP"
TOKEN="$TOKEN"
LATEST="$LATEST"
OLD="$OLD"
ORIG_STEP_FOLDER="$ORIG_STEP_FOLDER"
ORIG_VM130_FOLDER="$ORIG_VM130_FOLDER"
ORIG_ACCESS_FOLDER="$ORIG_ACCESS_FOLDER"
NEW_STEP_FOLDER="$NEW_STEP_FOLDER"
NEW_VM130_FOLDER="$NEW_VM130_FOLDER"
NEW_ACCESS_FOLDER="$NEW_ACCESS_FOLDER"
move_new_to_old() {
    local folder="\$1" ts
    [ -n "\$folder" ] || return 0
    [ -d "\$LATEST/\$folder" ] || return 0
    if [ -e "\$OLD/\$folder" ]; then
        ts="\$(date -u +%Y%m%d-%H%M%S)"
        mv "\$LATEST/\$folder" "\$OLD/\${ts}_rollback_\$folder"
    else
        mv "\$LATEST/\$folder" "\$OLD/\$folder"
    fi
}
restore_old_to_latest() {
    local folder="\$1"
    [ -n "\$folder" ] || return 0
    if [ -d "\$OLD/\$folder" ] && [ ! -e "\$LATEST/\$folder" ]; then
        mv "\$OLD/\$folder" "\$LATEST/\$folder"
    fi
}
move_new_to_old "\$NEW_STEP_FOLDER"
move_new_to_old "\$NEW_ACCESS_FOLDER"
move_new_to_old "\$NEW_VM130_FOLDER"
restore_old_to_latest "\$ORIG_STEP_FOLDER"
restore_old_to_latest "\$ORIG_ACCESS_FOLDER"
restore_old_to_latest "\$ORIG_VM130_FOLDER"
for rel in \\
    opt/router-ops/bin/router-publish-report \\
    opt/router-ops/bin/router-public-reindex \\
    opt/router-ops/bin/router-public-url \\
    opt/router-ops/bin/router-vm100-git-source-publish \\
    opt/router-ops/bin/router-vm101-git-source-publish \\
    opt/router-ops/bin/router-vm121-git-source-publish \\
    opt/router-ops/bin/router-machine-close \\
    opt/router-ops/state/current-vm100-git-source.env \\
    opt/router-ops/state/current-vm101-git-source.env \\
    opt/router-ops/state/current-vm121-git-source.env \\
    opt/router-ops/state/current-machine-close-vm100.env \\
    opt/router-ops/state/current-machine-close-vm101.env \\
    opt/router-ops/state/current-machine-close-vm121.env \\
    opt/router-ops/inventory/access-map.md
 do
    cp -a "\$BACKUP/\$rel" "/\$rel"
 done
sudo -n install -o root -g root -m 0644 "\$BACKUP/etc/caddy/Caddyfile" /etc/caddy/Caddyfile
sudo -n caddy validate --config /etc/caddy/Caddyfile
sudo -n systemctl reload caddy
/opt/router-ops/bin/router-public-reindex
printf 'RESULT=PASS_R05D_ROLLBACK\n'
printf 'RUNTIME_IMPACT=true\n'
printf 'TRAFFIC_INTERRUPTION=none\n'
EOF_ROLLBACK
chmod 700 "$ROLLBACK"

cat > "$REPORT" <<EOF_REPORT
RESULT=PASS_DIRECT_HTTPS_PUBLICATION_PIPELINE_FIXED
STEP_ID=$STEP_ID
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=step-report
PREVIOUS_MOVED_COUNT=1
COPY_VERIFY=PASS
STEP_SHA256=$STEP_SHA256
ACTIVE_LEGACY_REFERENCE_CHECK=PASS
CURRENT_STATE_MIGRATION=PASS
LATEST_INDEX_POSTCHECK=PASS
CACHE_CONTROL_NO_STORE=PASS
VM130_SOURCE_REFRESH=PASS
ACCESS_MAP_REFRESH=PASS
CADDY_RELOAD=PASS
RUNTIME_IMPACT=true
TRAFFIC_INTERRUPTION=none
EXTERNAL_POSTCHECK=PENDING_ASSISTANT
EOF_REPORT

cat > "$VERIFY" <<EOF_VERIFY
RESULT=PASS_DIRECT_HTTPS_PUBLICATION_PIPELINE_FIXED
COPY_VERIFY=PASS
STEP_SHA256=$STEP_SHA256
PAYLOAD_MANIFEST=PASS
EXACT_CURRENT_SHA=PASS
SYNTAX_CHECK=PASS
CADDY_CANDIDATE_VALIDATE=PASS
SANDBOX_PUBLICATION=PASS
FULL_FILE_INSTALL_COMPARE=PASS
ACTIVE_LEGACY_REFERENCE_CHECK=PASS
CURRENT_STATE_MIGRATION=PASS
CADDY_INSTALLED_VALIDATE=PASS
CADDY_RELOAD=PASS
CADDY_SERVICE_ACTIVE=PASS
CACHE_CONTROL_NO_STORE=PASS
VM100_PUBLIC_HTTP=PASS
VM101_PUBLIC_HTTP=PASS
VM121_PUBLIC_HTTP=PASS
VM130_SOURCE_PUBLICATION=PASS
ACCESS_MAP_PUBLICATION=PASS
STEP_PUBLICATION=PENDING
EXTERNAL_POSTCHECK=PENDING_ASSISTANT
RUNTIME_IMPACT=true
TRAFFIC_INTERRUPTION=none
EOF_VERIFY

cat > "$AFTER_STATE" <<EOF_AFTER
NEW_STEP_FOLDER=$NEW_STEP_FOLDER
NEW_VM130_FOLDER=$NEW_VM130_FOLDER
NEW_ACCESS_FOLDER=$NEW_ACCESS_FOLDER
STEP_REPORT_URL=$STEP_REPORT_URL
VM130_SOURCE_URL=$VM130_SOURCE_URL
ACCESS_MAP_URL=$ACCESS_MAP_URL
EOF_AFTER

python3 - "$FACTS" <<PY_FACTS_STEP
import json
from pathlib import Path
Path("$FACTS").write_text(json.dumps({
    "result": "PASS_DIRECT_HTTPS_PUBLICATION_PIPELINE_FIXED",
    "step_id": "$STEP_ID",
    "step_report_url": "$STEP_REPORT_URL",
    "copy_verify": "PASS",
    "step_sha256": "$STEP_SHA256",
    "active_legacy_reference_check": "PASS",
    "current_state_migration": "PASS",
    "latest_index_postcheck": "PASS",
    "cache_control_no_store": "PASS",
    "vm130_source_url": "$VM130_SOURCE_URL",
    "access_map_url": "$ACCESS_MAP_URL",
    "runtime_impact": True,
    "traffic_interruption": "none",
    "external_postcheck": "PENDING_ASSISTANT"
}, indent=2) + "\n", encoding="utf-8")
PY_FACTS_STEP

cp -a "$STEP_DIR/step-code.sh" "$SAFE_STEP/step-code.sh"
cp -a "$STEP_DIR/transfer-verify.txt" "$SAFE_STEP/transfer-verify.txt"
cp -a "$REPORT" "$FACTS" "$VERIFY" "$ROLLBACK" "$LOG" "$PROGRESS" \
    "$MODEL" "$METHODS" "$ACCESS_ROUTES" "$INSTALLED_SHA" \
    "$STEP_DIR/payload-manifest-verify.txt" "$STEP_DIR/current-state-verify.txt" \
    "$STEP_DIR/vm130-source-extract-verify.txt" \
    "$STEP_DIR/caddy-candidate-validate.txt" "$STEP_DIR/caddy-installed-validate.txt" \
    "$STEP_DIR/reindex-after-install.txt" "$STEP_DIR/public-url-output.txt" \
    "$STEP_DIR/latest-headers.txt" "$VM130_PUBLISH_OUT" "$ACCESS_PUBLISH_OUT" \
    "$BEFORE_STATE" "$AFTER_STATE" "$SAFE_STEP/"

cp -a "$BUNDLE_ROOT/manifest.sha256" "$SAFE_STEP/payload-manifest.sha256"
cp -a "$BUNDLE_ROOT/expected-current.sha256" "$SAFE_STEP/expected-current-before.sha256"
chmod 644 "$SAFE_STEP"/*
chmod 755 "$SAFE_STEP/rollback.sh"

progress "publish final STEP report through corrected publisher"

STEP_PUBLISH_OUT="$STEP_DIR/step-publisher-output.txt"
ROUTER_PUBLIC_BASE_URL="$PUBLIC_BASE" \
ROUTER_PUBLIC_RESOLVE_IP="$RESOLVE_IP" \
ROUTER_PUBLIC_TIMESTAMP="$STEP_PUBLISH_TS" \
"$PUBLISHER" "$STEP_LABEL" "$SAFE_STEP"/* > "$STEP_PUBLISH_OUT"

ACTUAL_STEP_REPORT_URL="$(read_field STEP_REPORT_URL "$STEP_PUBLISH_OUT")"
STEP_PUBLISHED_DIR="$(read_field PUBLISHED_DIR "$STEP_PUBLISH_OUT")"
[ "$ACTUAL_STEP_REPORT_URL" = "$STEP_REPORT_URL" ]
[ "$(read_field PUBLIC_OBJECT_KIND "$STEP_PUBLISH_OUT")" = "step-report" ]
[ "$(read_field PREVIOUS_MOVED_COUNT "$STEP_PUBLISH_OUT")" = "1" ]
[ "$(read_field CURRENT_KIND_COUNT "$STEP_PUBLISH_OUT")" = "1" ]
[ "$(read_field HTTP_POSTCHECK "$STEP_PUBLISH_OUT")" = "PASS" ]
[ -d "$STEP_PUBLISHED_DIR" ]
NEW_STEP_FOLDER="$(basename "$STEP_PUBLISHED_DIR")"

sed -i 's/STEP_PUBLICATION=PENDING/STEP_PUBLICATION=PASS/' "$VERIFY"
cp -a "$VERIFY" "$STEP_PUBLISHED_DIR/verify.txt"
chmod 644 "$STEP_PUBLISHED_DIR/verify.txt"
"$REINDEX" > "$STEP_DIR/final-reindex.txt"

progress "final independent post-check"

[ "$(one_latest_folder step)" = "$NEW_STEP_FOLDER" ]
[ "$(one_latest_folder vm130)" = "$NEW_VM130_FOLDER" ]
[ "$(one_latest_folder access)" = "$NEW_ACCESS_FOLDER" ]
grep -Fq "$NEW_STEP_FOLDER" "$LATEST/index.html"
grep -Fq "$NEW_VM130_FOLDER" "$LATEST/index.html"
grep -Fq "$NEW_ACCESS_FOLDER" "$LATEST/index.html"

for url in \
    "$STEP_REPORT_URL" \
    "${STEP_REPORT_URL}report.txt" \
    "${STEP_REPORT_URL}facts.json" \
    "${STEP_REPORT_URL}verify.txt" \
    "${STEP_REPORT_URL}rollback.sh" \
    "$VM130_SOURCE_URL" \
    "$ACCESS_MAP_URL"
 do
    curl -fsS --connect-timeout 5 --max-time 30 \
        --resolve "$DOMAIN:443:$RESOLVE_IP" \
        "$url" > /dev/null
 done

if grep -Eiq 'trycloudflare|TRYCF|QUICK_TUNNEL|CLOUDFLARE_PUBLISHED' \
    "$REPORT" "$VERIFY" "$STEP_PUBLISH_OUT" "$VM130_PUBLISH_OUT" "$ACCESS_PUBLISH_OUT"
then
    echo "ERROR: legacy reference in current outputs" >&2
    false
fi

cp -a "$STEP_PUBLISH_OUT" "$STEP_PUBLISHED_DIR/step-publisher-output.txt"
cp -a "$STEP_DIR/final-reindex.txt" "$STEP_PUBLISHED_DIR/final-reindex.txt"
chmod 644 "$STEP_PUBLISHED_DIR/step-publisher-output.txt" "$STEP_PUBLISHED_DIR/final-reindex.txt"
FOLDER_INDEX_TMP="$STEP_PUBLISHED_DIR/index.html.tmp.$$"
{
    echo '<!doctype html><html><head><meta charset="utf-8"><title>router report</title></head><body>'
    printf '<h1>%s</h1>\n' "$NEW_STEP_FOLDER"
    echo '<ul>'
    find "$STEP_PUBLISHED_DIR" -maxdepth 1 -type f ! -name index.html ! -name 'index.html.tmp.*' -printf '%f\n' |
        LC_ALL=C sort |
        while IFS= read -r name; do
            [ -n "$name" ] || continue
            printf '<li><a href="%s">%s</a></li>\n' "$name" "$name"
        done
    echo '</ul>'
    echo '<p><a href="../">Back to latest</a></p>'
    echo '</body></html>'
} > "$FOLDER_INDEX_TMP"
chmod 644 "$FOLDER_INDEX_TMP"
mv -f "$FOLDER_INDEX_TMP" "$STEP_PUBLISHED_DIR/index.html"

TRANSACTION_ACTIVE=0
trap - ERR INT TERM

progress "complete files installed, canonical publications refreshed"

printf 'RESULT=PASS_DIRECT_HTTPS_PUBLICATION_PIPELINE_FIXED\n'
printf 'STEP_REPORT_URL=%s\n' "$STEP_REPORT_URL"
printf 'LOCAL_M07_PLAN_URL=%s\n' "$LOCAL_M07_PLAN_URL"
printf 'GLOBAL_PROJECT_PLAN_URL=%s\n' "$GLOBAL_PROJECT_PLAN_URL"
printf 'ACCESS_MAP_URL=%s\n' "$ACCESS_MAP_URL"
printf 'VM100_PUBLIC_URL=%s\n' "$VM100_PUBLIC_URL"
printf 'VM101_PUBLIC_URL=%s\n' "$VM101_PUBLIC_URL"
printf 'VM121_PUBLIC_URL=%s\n' "$VM121_PUBLIC_URL"
printf 'VM101_MODEL_URL=%s\n' "$VM101_MODEL_URL"
printf 'VM101_METHODS_URL=%s\n' "$VM101_METHODS_URL"
printf 'VM130_SOURCE_URL=%s\n' "$VM130_SOURCE_URL"
printf 'LATEST_ROOT_URL=%s\n' "$LATEST_ROOT_URL"
printf 'OLD_ROOT_URL=%s\n' "$OLD_ROOT_URL"
printf 'FACTS_URL=%s\n' "$FACTS_URL"
printf 'VERIFY_URL=%s\n' "$VERIFY_URL"
printf 'ROLLBACK_URL=%s\n' "$ROLLBACK_URL"
printf 'PUBLIC_OBJECT_KIND=step-report\n'
printf 'PREVIOUS_MOVED_COUNT=1\n'
printf 'COPY_VERIFY=PASS\n'
printf 'STEP_SHA256=%s\n' "$STEP_SHA256"
printf 'RUNTIME_IMPACT=true\n'
printf 'TRAFFIC_INTERRUPTION=none\n'
printf 'EXTERNAL_POSTCHECK=PENDING_ASSISTANT\n'
