#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
BASE="${ROUTER_OPS_BASE:-/opt/router-ops}"
ROOT="${BUNDLE_ROOT:?}"
RUN_DIR="${ROUTER_DETACHED_RUN_DIR:?}"
CHILD_RESULT="${ROUTER_DETACHED_CHILD_RESULT:?}"
INCOMING="${ROUTER_STEP_INCOMING:-/home/ops/incoming}"
SELFTEST="${ROUTER_EXACT_WORKER_SELFTEST:-false}"
STEP=STEP_050M07R20QY_R05_VM100_COMMON_PEER_LIFECYCLE_TRANSITIONAL_STATE_PUBLICATION_CONTINUATION
CURRENT="$RUN_DIR/workflow-current.txt"; EVENTS="$RUN_DIR/workflow-events.tsv"; EVIDENCE="$RUN_DIR/evidence"; REPORT="$RUN_DIR/report"; PAYLOAD="$RUN_DIR/public-payload"; FINAL_ENV="$RUN_DIR/final-publication.env"
mkdir -p "$EVIDENCE" "$REPORT" "$PAYLOAD" "$INCOMING"; chmod 700 "$EVIDENCE" "$REPORT" "$PAYLOAD"
source "$BASE/lib/router-step-workflow.sh"
router_step_workflow_init "$CURRENT" "$EVENTS" preflight source_publish report_publish archive
CORE_CHANGE_COMPLETE=true; CORE_CHANGE_REPEATED=false; ROLLBACK_STATUS=not_required; STOP_DETAIL=unknown; LATEST_ARCHIVE=not_created; LATEST_ARCHIVE_SHA256=unknown; VM100_PUBLIC_URL=unknown; STEP_REPORT_URL=unknown; STOP_HANDLED=false
TARGET_APPLY_REPEATED=false; SYNTHETIC_TEST_REPEATED=false; DIAGNOSTICS_REPEATED=false
TRANSITIONAL_SYNC_STATE=unknown; RUNTIME_REGISTRY_STATE=unknown; RUNTIME_VM121_STATE=unknown; REGISTRY_VM121_STATE=unknown
RUNTIME_PEER_COUNT=unknown; REGISTRY_PROFILE_COUNT=2; VM121_ENABLED_PEER_COUNT=unknown
write_child(){ local result="$1" rc="$2" tmp="${CHILD_RESULT}.tmp.$$"; { echo RESULT="$result"; echo WORKER_RC="$rc"; echo CORE_CHANGE_COMPLETE="$CORE_CHANGE_COMPLETE"; echo CORE_CHANGE_REPEATED="$CORE_CHANGE_REPEATED"; echo ROLLBACK_STATUS="$ROLLBACK_STATUS"; echo STOP_DETAIL="$STOP_DETAIL"; echo TRANSITIONAL_SYNC_STATE="$TRANSITIONAL_SYNC_STATE"; echo RUNTIME_REGISTRY_STATE="$RUNTIME_REGISTRY_STATE"; echo RUNTIME_VM121_STATE="$RUNTIME_VM121_STATE"; echo REGISTRY_VM121_STATE="$REGISTRY_VM121_STATE"; echo RUNTIME_PEER_COUNT="$RUNTIME_PEER_COUNT"; echo REGISTRY_PROFILE_COUNT="$REGISTRY_PROFILE_COUNT"; echo VM121_ENABLED_PEER_COUNT="$VM121_ENABLED_PEER_COUNT"; echo VM100_PUBLIC_URL="$VM100_PUBLIC_URL"; echo STEP_REPORT_URL="$STEP_REPORT_URL"; echo LATEST_ARCHIVE="$LATEST_ARCHIVE"; echo LATEST_ARCHIVE_SHA256="$LATEST_ARCHIVE_SHA256"; } > "$tmp"; chmod 600 "$tmp"; mv "$tmp" "$CHILD_RESULT"; }
archive_latest(){ local log="$EVIDENCE/archive.log"; router_step_archive_latest_python "$BASE" "$INCOMING" "$(date -u +%Y%m%d-%H%M%S)" | tee "$log"; LATEST_ARCHIVE="$(awk -F= '$1=="LATEST_ARCHIVE"{print substr($0,index($0,"=")+1)}' "$log"|tail -n1)"; LATEST_ARCHIVE_SHA256="$(awk -F= '$1=="LATEST_ARCHIVE_SHA256"{print $2}' "$log"|tail -n1)"; }
vm100(){ ssh -T -o BatchMode=yes -o ConnectTimeout=20 pve-mgts "ssh -T -i /root/.ssh/pve_to_openwrt_mgts_ed25519 -o BatchMode=yes -o ConnectTimeout=20 root@10.71.100.1 '$1'"; }
stop_handler(){ local rc="$1" line="$2" cmd="$3"; trap - ERR; [[ "$STOP_HANDLED" == false ]] || exit "$rc"; STOP_HANDLED=true; { echo RESULT=STOP_R20QY_POST_CORE_CONTINUATION_R05; echo STOP_RC="$rc"; echo STOP_LINE="$line"; printf 'STOP_COMMAND=%q\n' "$cmd"; echo CORE_CHANGE_COMPLETE=true; echo CORE_CHANGE_REPEATED=false; echo ROLLBACK_STATUS=not_required; echo STOP_DETAIL="$STOP_DETAIL"; echo TRANSITIONAL_SYNC_STATE="$TRANSITIONAL_SYNC_STATE"; } | tee "$REPORT/stop.txt" "$EVIDENCE/stop.txt"; set +e; archive_latest >/dev/null 2>&1; set -e; write_child STOP_R20QY_POST_CORE_CONTINUATION_R05 "$rc"; exit "$rc"; }
trap 'rc=$?; stop_handler "$rc" "$LINENO" "$BASH_COMMAND"' ERR

router_step_phase_begin preflight
if [[ "$SELFTEST" == true ]]; then
cat > "$EVIDENCE/core-verify.txt" <<'EOF'
schema=router-wgpay-peer-lifecycle-status-v1
initialized=true
registry_valid=true
profile_count=2
wireguard_count=2
amneziawg_count=0
selector_registry_consistent=true
runtime_peer_count=1
runtime_ip=10.253.1.14/32
registry_ip=10.253.1.13
registry_ip=10.253.1.14
selector_count=2
canonical_count=2
synthetic_profile_present=false
synthetic_runtime_present=false
RESULT=PASS_R20QY_R05_CORE_VERIFY
EOF
cat > "$EVIDENCE/vm121-enabled-ips.txt" <<'EOF'
10.253.1.14
EOF
else
  vm100 '/usr/local/sbin/router-wgpay-peer-lifecycle.sh --status; runtime_count=$(wg show wg_paid peers 2>/dev/null | sed "/^[[:space:]]*$/d" | wc -l | tr -d " "); echo runtime_peer_count=$runtime_count; wg show wg_paid allowed-ips 2>/dev/null | while read -r peer ips; do oldifs=$IFS; IFS=,; for ip in $ips; do echo runtime_ip=$ip; done; IFS=$oldifs; done; while IFS="$(printf "\\t")" read -r profile protocol iface key ip normal active generation created updated; do case "$profile" in ""|\#*) continue;; esac; echo registry_ip=$ip; done < /etc/router-wgpay-peer-state/registry.tsv; selector_count=$(grep -Ev "^[[:space:]]*(#|$)" /etc/router-wgpay-peer-state/peers.conf | wc -l | tr -d " "); canonical_count=$(grep -Ev "^[[:space:]]*(#|$)" /etc/router-wgpay-peer-state/canonical.conf | wc -l | tr -d " "); synthetic=false; grep -F "r20qy-synthetic-profile" /etc/router-wgpay-peer-state/registry.tsv >/dev/null 2>&1 && synthetic=true; synthetic_runtime=false; wg show wg_paid allowed-ips 2>/dev/null | grep -F "10.253.254.250/32" >/dev/null 2>&1 && synthetic_runtime=true; echo selector_count=$selector_count; echo canonical_count=$canonical_count; echo synthetic_profile_present=$synthetic; echo synthetic_runtime_present=$synthetic_runtime; nft list table inet wgpay_dscp_canary >/dev/null; nft list table inet router_egress_activity >/dev/null; echo RESULT=PASS_R20QY_R05_CORE_VERIFY' > "$EVIDENCE/core-verify.txt" 2>&1
  set +e
  ssh -T -o BatchMode=yes -o ConnectTimeout=20 vm121 'docker exec -i wgaccess-postgres sh -lc '\''exec psql -v ON_ERROR_STOP=1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" -At'\''' > "$EVIDENCE/vm121-enabled-ips.txt" 2> "$EVIDENCE/vm121-enabled-ips.stderr.txt" <<'SQL'
SELECT tunnel_ip
FROM peers
WHERE enabled IS TRUE
ORDER BY tunnel_ip;
SQL
  vm121_rc=$?
  set -e
  if [[ "$vm121_rc" -ne 0 ]]; then STOP_DETAIL=vm121_enabled_peer_query_failed; stop_handler "$vm121_rc" "$LINENO" 'VM121 enabled peer read-only query'; fi
fi

grep -Fqx 'initialized=true' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=core_not_initialized; false; }
grep -Fqx 'registry_valid=true' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=registry_invalid; false; }
grep -Fqx 'profile_count=2' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=profile_count; false; }
grep -Fqx 'wireguard_count=2' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=wireguard_count; false; }
grep -Fqx 'amneziawg_count=0' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=amneziawg_count; false; }
grep -Fqx 'selector_registry_consistent=true' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=selector_registry_inconsistent; false; }
grep -Fqx 'selector_count=2' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=selector_count; false; }
grep -Fqx 'canonical_count=2' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=canonical_count; false; }
grep -Fqx 'synthetic_profile_present=false' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=synthetic_profile_residue; false; }
grep -Fqx 'synthetic_runtime_present=false' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=synthetic_runtime_residue; false; }
grep -Fqx 'RESULT=PASS_R20QY_R05_CORE_VERIFY' "$EVIDENCE/core-verify.txt" || { STOP_DETAIL=core_verify_result; false; }

if grep -Ev '^([0-9]{1,3}\.){3}[0-9]{1,3}$|^[[:space:]]*$' "$EVIDENCE/vm121-enabled-ips.txt" | grep -q .; then STOP_DETAIL=vm121_enabled_peer_output_invalid; false; fi
grep '^runtime_ip=' "$EVIDENCE/core-verify.txt" | cut -d= -f2- | sed 's#/32$##' | LC_ALL=C sort -u > "$EVIDENCE/runtime.set"
grep '^registry_ip=' "$EVIDENCE/core-verify.txt" | cut -d= -f2- | LC_ALL=C sort -u > "$EVIDENCE/registry.set"
sed '/^[[:space:]]*$/d' "$EVIDENCE/vm121-enabled-ips.txt" | LC_ALL=C sort -u > "$EVIDENCE/vm121.set"
RUNTIME_PEER_COUNT="$(wc -l < "$EVIDENCE/runtime.set" | tr -d ' ')"
REGISTRY_PROFILE_COUNT="$(wc -l < "$EVIDENCE/registry.set" | tr -d ' ')"
VM121_ENABLED_PEER_COUNT="$(wc -l < "$EVIDENCE/vm121.set" | tr -d ' ')"
[[ "$REGISTRY_PROFILE_COUNT" == 2 ]] || { STOP_DETAIL=registry_ip_set_count; false; }
if cmp -s "$EVIDENCE/runtime.set" "$EVIDENCE/registry.set"; then RUNTIME_REGISTRY_STATE=aligned; else RUNTIME_REGISTRY_STATE=transitional_drift; fi
if cmp -s "$EVIDENCE/runtime.set" "$EVIDENCE/vm121.set"; then RUNTIME_VM121_STATE=aligned; else RUNTIME_VM121_STATE=transitional_drift; fi
if cmp -s "$EVIDENCE/registry.set" "$EVIDENCE/vm121.set"; then REGISTRY_VM121_STATE=aligned; else REGISTRY_VM121_STATE=transitional_drift; fi
if [[ "$RUNTIME_REGISTRY_STATE" == aligned && "$RUNTIME_VM121_STATE" == aligned ]]; then
  TRANSITIONAL_SYNC_STATE=fully_aligned
elif [[ "$RUNTIME_VM121_STATE" == aligned ]]; then
  TRANSITIONAL_SYNC_STATE=legacy_vm121_agent_matches_runtime_registry_pending_p1_3
elif [[ "$REGISTRY_VM121_STATE" == aligned ]]; then
  TRANSITIONAL_SYNC_STATE=registry_matches_vm121_runtime_pending_legacy_agent_reconcile
else
  TRANSITIONAL_SYNC_STATE=three_way_transitional_drift_pending_p1_3
fi
{
  echo RUNTIME_PEER_COUNT="$RUNTIME_PEER_COUNT"
  echo REGISTRY_PROFILE_COUNT="$REGISTRY_PROFILE_COUNT"
  echo VM121_ENABLED_PEER_COUNT="$VM121_ENABLED_PEER_COUNT"
  echo RUNTIME_REGISTRY_STATE="$RUNTIME_REGISTRY_STATE"
  echo RUNTIME_VM121_STATE="$RUNTIME_VM121_STATE"
  echo REGISTRY_VM121_STATE="$REGISTRY_VM121_STATE"
  echo TRANSITIONAL_SYNC_STATE="$TRANSITIONAL_SYNC_STATE"
  echo RUNTIME_IPS="$(paste -sd, "$EVIDENCE/runtime.set" 2>/dev/null || tr '\n' ',' < "$EVIDENCE/runtime.set" | sed 's/,$//')"
  echo REGISTRY_IPS="$(paste -sd, "$EVIDENCE/registry.set" 2>/dev/null || tr '\n' ',' < "$EVIDENCE/registry.set" | sed 's/,$//')"
  echo VM121_ENABLED_IPS="$(paste -sd, "$EVIDENCE/vm121.set" 2>/dev/null || tr '\n' ',' < "$EVIDENCE/vm121.set" | sed 's/,$//')"
  echo RESULT=PASS_R20QY_R05_TRANSITION_CLASSIFICATION
} > "$EVIDENCE/transition-classification.txt"
router_step_phase_complete preflight retained_r03_core_validated_and_transition_classified_without_core_repeat

router_step_phase_begin source_publish
if [[ "$SELFTEST" == true ]]; then
cat > "$EVIDENCE/vm100-source-precheck.txt" <<'EOF'
LOCAL_HEAD=a8b7337674b931c2e73f9b4bd7ebe76866e6a81f
REMOTE_HEAD=a8b7337674b931c2e73f9b4bd7ebe76866e6a81f
SOURCE_PUSH_RECOVERY=not_required
EOF
cat > "$EVIDENCE/vm100-machine-close.txt" <<'EOF'
RESULT=PASS_ROUTER_MACHINE_CLOSE
COMMIT=1111111111111111111111111111111111111111
TREE=2222222222222222222222222222222222222222
GIT_FILE_COUNT=33
VM100_PUBLIC_URL=selftest://vm100-source/
EOF
VM100_PUBLIC_URL=selftest://vm100-source/
else
  set +e
  vm100 'G=/root/.vm100-source.git; L=$(git --git-dir="$G" rev-parse HEAD); R=$(git -c protocol.version=0 --git-dir="$G" ls-remote origin refs/heads/main | while read -r sha ref; do echo $sha; break; done); echo LOCAL_HEAD=$L; echo REMOTE_HEAD=$R; if [ "$L" = "$R" ]; then echo SOURCE_PUSH_RECOVERY=not_required; exit 0; fi; P=$(git --git-dir="$G" rev-parse "$L^" 2>/dev/null || true); S=$(git --git-dir="$G" log -1 --format=%s "$L" 2>/dev/null || true); echo LOCAL_PARENT=$P; echo LOCAL_SUBJECT=$S; [ "$P" = "$R" ] || { echo SOURCE_PUSH_RECOVERY=blocked_non_fast_forward; exit 81; }; case "$S" in STEP_050M07R20QY_R03:*) ;; *) echo SOURCE_PUSH_RECOVERY=blocked_unexpected_commit; exit 82;; esac; git -c protocol.version=0 --git-dir="$G" --work-tree=/ push origin main; R2=$(git -c protocol.version=0 --git-dir="$G" ls-remote origin refs/heads/main | while read -r sha ref; do echo $sha; break; done); [ "$R2" = "$L" ] || exit 83; echo SOURCE_PUSH_RECOVERY=performed' > "$EVIDENCE/vm100-source-precheck.txt" 2>&1
  src_rc=$?
  set -e
  if [[ "$src_rc" -ne 0 ]]; then STOP_DETAIL=vm100_source_head_divergence; stop_handler "$src_rc" "$LINENO" 'VM100 source-head recovery'; fi
  set +e
  "$BASE/bin/router-machine-close-safe" vm100 'STEP_050M07R20QY_R05: lifecycle foundation source publication after transitional-state classification' > "$EVIDENCE/vm100-machine-close.txt" 2>&1
  close_rc=$?
  set -e
  if [[ "$close_rc" -ne 0 ]]; then STOP_DETAIL="$(awk -F= '$1=="STOP_REASON"{print substr($0,index($0,"=")+1)} $1=="RESULT" && $2 ~ /^STOP_/{v=$2} END{if(v!="")print v}' "$EVIDENCE/vm100-machine-close.txt" | tail -n1)"; [[ -n "$STOP_DETAIL" ]] || STOP_DETAIL=vm100_machine_close_failed; stop_handler "$close_rc" "$LINENO" 'router-machine-close-safe vm100'; fi
  grep -Fqx 'RESULT=PASS_ROUTER_MACHINE_CLOSE' "$EVIDENCE/vm100-machine-close.txt" || { STOP_DETAIL=vm100_machine_close_result; false; }
  grep -Fqx 'GIT_FILE_COUNT=33' "$EVIDENCE/vm100-machine-close.txt" || { STOP_DETAIL=vm100_git_file_count; false; }
  VM100_PUBLIC_URL="$(awk -F= '$1=="VM100_PUBLIC_URL"{print substr($0,index($0,"=")+1)}' "$EVIDENCE/vm100-machine-close.txt" | tail -n1)"
  [[ -n "$VM100_PUBLIC_URL" ]] || { STOP_DETAIL=vm100_public_url_missing; false; }
fi
router_step_phase_complete source_publish vm100_machine_git_and_source_closed_once_without_core_repeat

cat > "$REPORT/report.txt" <<EOF
RESULT=PASS_R20QY_VM100_COMMON_PEER_LIFECYCLE_WIREGUARD_FOUNDATION_POST_CORE_CONTINUATION_R05
REVISION=R05
CORE_CHANGE_COMPLETE=true
CORE_CHANGE_REPEATED=false
TARGET_APPLY_REPEATED=false
SYNTHETIC_TEST_REPEATED=false
DIAGNOSTICS_REPEATED=false
ROLLBACK_STATUS=not_required
REGISTRY_PROFILE_COUNT=$REGISTRY_PROFILE_COUNT
RUNTIME_PEER_COUNT=$RUNTIME_PEER_COUNT
VM121_ENABLED_PEER_COUNT=$VM121_ENABLED_PEER_COUNT
SELECTOR_COUNT=2
CANONICAL_COUNT=2
STALE_SELECTOR_ROWS_PRUNED=3
SYNTHETIC_ENABLE_DISABLE=PASS_RETAINED_R03
RUNTIME_REGISTRY_STATE=$RUNTIME_REGISTRY_STATE
RUNTIME_VM121_STATE=$RUNTIME_VM121_STATE
REGISTRY_VM121_STATE=$REGISTRY_VM121_STATE
TRANSITIONAL_SYNC_STATE=$TRANSITIONAL_SYNC_STATE
TRANSITIONAL_CAVEAT=legacy_vm121_agent_still_directly_owns_wg_runtime_until_p1_3
P1_2_FOUNDATION_STATUS=PASS
P1_3_REQUIRED=true
VM121_CHANGED=false
VM101_CHANGED=false
REBOOT_PERFORMED=false
LONG_RECOVERY_TEST_REPEATED=false
WORKFLOW_CURRENT_ARTIFACT=pre_completion_snapshot_by_publisher_contract
VM100_PUBLIC_URL=$VM100_PUBLIC_URL
VM101_PUBLIC_URL=https://reports.secret-studio.ru/latest/20260801-095331_vm101_git_source_ac678d1b3164/
VM121_PUBLIC_URL=https://reports.secret-studio.ru/latest/20260713-182700_vm121_git_source_93e0e5a31f09/
GLOBAL_PLAN_URL=https://reports.secret-studio.ru/latest/20260802-105248_global_project_plan_wg_paid_after_r20qw/
ACCESS_MAP_URL=https://reports.secret-studio.ru/latest/20260715-210236_access_map_canonical_current/
NEXT_ACTION=P1.3 VM121 agent integration without selector ownership
EOF
cp "$REPORT/report.txt" "$REPORT/verify.txt"; cp "$ROOT/scripts/run-step.sh" "$REPORT/executed-step.sh"; cp "$ROOT/release-info.json" "$REPORT/release-info.json"; cp "$ROOT/manifest.sha256" "$REPORT/bundle-manifest.sha256"; cp "$ROOT/rollback/README.md" "$REPORT/rollback.txt"; cp "$EVIDENCE"/*.txt "$REPORT/" 2>/dev/null || true

router_step_phase_begin report_publish
if [[ "$SELFTEST" == true ]]; then
  DEST="${ROUTER_PUBLIC_ROOT:?}/latest/r20qy-r05-selftest"; rm -rf "$DEST"; mkdir -p "$DEST"; cp -a "$REPORT"/. "$DEST"/; STEP_REPORT_URL=selftest://r20qy-r05-selftest/
else
  args=(); while IFS= read -r file; do args+=("$file" "$(basename "$file")"); done < <(find "$REPORT" -maxdepth 1 -type f -print | LC_ALL=C sort)
  router_step_publish_payload step050m07r20qy_r05_vm100_common_peer_lifecycle_transitional_state_publication_continuation "$REPORT" "$PAYLOAD" "$CURRENT" "$EVENTS" "$FINAL_ENV" "${args[@]}"
  STEP_REPORT_URL="$(awk -F= '$1=="PUBLIC_URL"{print substr($0,index($0,"=")+1)}' "$FINAL_ENV"|tail -n1)"
fi
router_step_phase_complete report_publish step_report_published
router_step_phase_begin archive; archive_latest; router_step_phase_complete archive latest_created; router_step_workflow_mark_complete
trap - ERR; STOP_DETAIL=none; write_child PASS_R20QY_VM100_COMMON_PEER_LIFECYCLE_WIREGUARD_FOUNDATION_POST_CORE_CONTINUATION_R05 0
cat "$REPORT/report.txt"; echo STEP_REPORT_URL="$STEP_REPORT_URL"; echo LATEST_ARCHIVE="$LATEST_ARCHIVE"; echo LATEST_ARCHIVE_SHA256="$LATEST_ARCHIVE_SHA256"
