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

[ "$(id -un)" = "ops" ] || {
  echo "STOP: запускать на router-ops пользователем ops"
  exit 1
}

STEP="STEP_050M07D4B_FINAL_SELFCONTAINED_PRE_NIGHT_FREEZE_AWG"
PASS_DECISION="PASS_${STEP}"

TOKEN="e94a0859747d7b96f29c7fdafc2d0351ba603bb0a7e9e5a4"
PUBLIC_BASE="https://helena-background-beam-harry.trycloudflare.com/r/${TOKEN}"

ROOT="/opt/router-ops"
PUBROOT="${ROOT}/public/r/${TOKEN}"

M07D_DIR="${PUBROOT}/20260711-210850_step050m07d_classify_false_success_and_egress1_failure"
D2_DIR="${PUBROOT}/20260711-211307_step050m07d2_freeze_pre0420_evidence"
C5_DIR="${PUBROOT}/20260711-205604_step050m07c5_tmpfs_surgical_backup_fresh_refresh_live"

ARCHITECTURE_PLAN="${PUBLIC_BASE}/20260711-181158_local_architecture_plan_vm101_autonomous_hmn_recovery/"
XS_MAP="${PUBLIC_BASE}/20260711-120734_xs_map_audit_repair_publish/"
GLOBAL_PROJECT_PLAN="${PUBLIC_BASE}/20260711-123348_global_project_plan_wg_paid/"

TS="$(date -u +%Y%m%d-%H%M%S)"
REPORT_SLUG="${TS}_step050m07d4b_final_selfcontained_pre_night_freeze_awg"
REPORT_DIR="${PUBROOT}/${REPORT_SLUG}"

TRYCF_REPORT="${PUBLIC_BASE}/${REPORT_SLUG}/"
REPORT_TXT="${TRYCF_REPORT}report.txt"
FACTS_JSON="${TRYCF_REPORT}facts.json"

mkdir -p "$REPORT_DIR" "$REPORT_DIR/sources"

# Сохраняем точный верхнеуровневый STEP до проверок и SSH.
cp -a "$0" "$REPORT_DIR/step.sh"
chmod 600 "$REPORT_DIR/step.sh"

PROGRESS_LOG="$REPORT_DIR/progress.log"
: > "$PROGRESS_LOG"

CURRENT_STAGE="initialization"
LAST_SUCCESS="step_saved"
VM101_RC="NOT_RUN"

stage() {
  CURRENT_STAGE="$1"

  echo
  echo ">>> [$1] $2" | tee -a "$PROGRESS_LOG"

  date -u '+    utc=%Y-%m-%dT%H:%M:%SZ' |
    tee -a "$PROGRESS_LOG"
}

mark_success() {
  LAST_SUCCESS="$1"
  echo "last_success=$LAST_SUCCESS" >> "$PROGRESS_LOG"
}

print_links() {
  echo
  echo "TRYCF_REPORT=$TRYCF_REPORT"
  echo "REPORT_TXT=$REPORT_TXT"
  echo "FACTS_JSON=$FACTS_JSON"
  echo "ARCHITECTURE_PLAN=$ARCHITECTURE_PLAN"
  echo "XS_MAP=$XS_MAP"
  echo "GLOBAL_PROJECT_PLAN=$GLOBAL_PROJECT_PLAN"
}

create_index() {
  cat > "$REPORT_DIR/index.html" <<EOF
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>${STEP}</title>
</head>
<body style="font-family:system-ui;max-width:1100px;margin:40px auto;padding:0 20px">
<h1>${STEP}</h1>

<h2>Финальный checkpoint</h2>
<ul>
<li><a href="report.txt">report.txt</a></li>
<li><a href="facts.json">facts.json</a></li>
<li><a href="assessment.json">assessment.json</a></li>
<li><a href="exact-baseline.json">exact-baseline.json</a></li>
<li><a href="comparison-contract.json">comparison-contract.json</a></li>
</ul>

<h2>Raw VM101 snapshot</h2>
<ul>
<li><a href="vm101.txt">vm101.txt</a></li>
<li><a href="vm101.stderr">vm101.stderr</a></li>
<li><a href="vm101.sh">vm101.sh</a></li>
<li><a href="step.sh">step.sh</a></li>
</ul>

<h2>Сохранённый контекст</h2>
<ul>
<li><a href="sources/m07d-source-contract.json">M07D source contract</a></li>
<li><a href="sources/m07d-failure-artifacts.txt">M07D failure artifacts</a></li>
<li><a href="sources/m07d-assessment.json">M07D assessment</a></li>
<li><a href="sources/d2-scheduler-contract.txt">D2 scheduler contract</a></li>
<li><a href="sources/d2-evidence-index.json">D2 evidence index</a></li>
<li><a href="sources/c5-report.txt">C5 report</a></li>
<li><a href="sources/c5-live-stream.txt">C5 live stream</a></li>
</ul>
</body>
</html>
EOF
}

write_stop() {
  local reason="$1"
  local rc="$2"
  local line="$3"

  python3 - \
    "$REPORT_DIR" \
    "$STEP" \
    "$reason" \
    "$rc" \
    "$line" \
    "$CURRENT_STAGE" \
    "$LAST_SUCCESS" \
    "$VM101_RC" \
    > "$REPORT_DIR/diagnostic.json" <<'PY'
import json
import sys
from pathlib import Path

(
    report_dir,
    step,
    reason,
    rc,
    line,
    stage,
    last_success,
    vm101_rc,
) = sys.argv[1:]

root = Path(report_dir)


def tail(path: Path, limit: int = 100000):
    if not path.exists() or not path.is_file():
        return None

    return path.read_text(
        encoding="utf-8",
        errors="replace",
    )[-limit:]


streams = {}

for pattern in ("*.txt", "*.stderr", "*.log"):
    for path in sorted(root.glob(pattern)):
        if path.name in {"report.txt", "progress.log"}:
            continue

        streams[path.name] = {
            "size_bytes": path.stat().st_size,
            "tail": tail(path),
        }

combined = "\n".join(
    item["tail"] or ""
    for item in streams.values()
)

if "awg_cli_missing" in combined:
    classification = "AMNEZIAWG_CLI_MISSING"
elif "awg_endpoint_unresolved=" in combined:
    classification = "AMNEZIAWG_ENDPOINT_UNRESOLVED"
elif "strict_all=false" in combined:
    classification = "PRE_NIGHT_VPN_BASELINE_DEGRADED"
elif "routes_all=false" in combined:
    classification = "PRE_NIGHT_ROUTE_BASELINE_DEGRADED"
elif "schedule_missing=" in combined:
    classification = "SCHEDULE_CONTRACT_MISSING"
elif vm101_rc not in {"NOT_RUN", "0"}:
    classification = "VM101_READONLY_SNAPSHOT_FAILED"
else:
    classification = "LOCAL_PARSER_OR_PUBLICATION_FAILURE"

print(json.dumps({
    "schema": "router-step-inline-diagnostic-v13",
    "step": step,
    "failure": {
        "reason": reason,
        "rc": int(rc),
        "line": int(line),
        "stage": stage,
        "last_success": last_success,
    },
    "command_results": {
        "vm101_rc": vm101_rc,
    },
    "automatic_classification": classification,
    "safety": {
        "read_only": True,
        "refresh_ran": False,
        "rebalance_ran": False,
        "network_changed": False,
        "services_changed": False,
        "state_changed": False,
        "timer_changed": False,
        "plan_changed": False,
        "direct_failopen_changed": False,
    },
    "captured_streams": streams,
    "recommended_next_step": (
        "После scheduled refresh выполнить read-only comparison "
        "с exact-baseline.json."
    ),
}, ensure_ascii=False, indent=2))
PY

  cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=STOP_${STEP}_${reason}
all_ok=false
mode=M07_FINAL_SELFCONTAINED_PRE_NIGHT_FREEZE_AWG
error_rc=${rc}
error_line=${line}
failed_stage=${CURRENT_STAGE}
last_success=${LAST_SUCCESS}

command_results:
  vm101_rc=${VM101_RC}

safety:
  read_only=true
  refresh_ran=false
  rebalance_ran=false
  network_changed=false
  services_changed=false
  state_changed=false
  timer_changed=false
  plan_changed=false
  direct_failopen_changed=false

plan:
  current_milestone=M07
  milestone_completed=false
  milestone_changed=false

TRYCF_REPORT=${TRYCF_REPORT}
REPORT_TXT=${REPORT_TXT}
FACTS_JSON=${FACTS_JSON}
ARCHITECTURE_PLAN=${ARCHITECTURE_PLAN}
XS_MAP=${XS_MAP}
GLOBAL_PROJECT_PLAN=${GLOBAL_PROJECT_PLAN}
EOF

  python3 - \
    "$REPORT_DIR/diagnostic.json" \
    "$STEP" \
    "$reason" \
    "$rc" \
    "$line" \
    "$CURRENT_STAGE" \
    "$LAST_SUCCESS" \
    "$TRYCF_REPORT" \
    "$REPORT_TXT" \
    "$FACTS_JSON" \
    "$ARCHITECTURE_PLAN" \
    "$XS_MAP" \
    "$GLOBAL_PROJECT_PLAN" \
    > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    diagnostic_path,
    step,
    reason,
    rc,
    line,
    stage,
    last_success,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
) = sys.argv[1:]

with open(diagnostic_path, encoding="utf-8") as source:
    diagnostic = json.load(source)

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "assessment": {
        "decision": f"STOP_{step}_{reason}",
        "all_ok": False,
        "error_rc": int(rc),
        "error_line": int(line),
        "failed_stage": stage,
        "last_success": last_success,
    },
    "inline_diagnostic": diagnostic,
    "safety": diagnostic["safety"],
    "plan": {
        "current_milestone": "M07",
        "milestone_completed": False,
        "milestone_changed": False,
    },
    "publish": {
        "trycf_report": report,
        "report_txt": report_txt,
        "facts_json": facts_json,
        "architecture_plan": architecture,
        "xs_map": xs_map,
        "global_project_plan": global_plan,
    },
}, ensure_ascii=False, indent=2))
PY

  create_index

  find "$REPORT_DIR" \
    -type f \
    ! -name SHA256SUMS \
    -print0 |
    sort -z |
    xargs -0 sha256sum \
    > "$REPORT_DIR/SHA256SUMS"
}

fatal() {
  local reason="$1"
  local rc="${2:-1}"
  local line="${3:-$LINENO}"

  trap - ERR
  write_stop "$reason" "$rc" "$line"
  print_links
  exit "$rc"
}

on_error() {
  local rc="$?"
  local line="$1"

  fatal "UNEXPECTED_ERROR" "$rc" "$line"
}

trap 'on_error "$LINENO"' ERR

stage "01/05" "Сохраняю устойчивые доказательства M07D, D2 и C5"

for required in \
  "$M07D_DIR/report.txt" \
  "$M07D_DIR/assessment.json" \
  "$M07D_DIR/source-contract.json" \
  "$M07D_DIR/failure-artifacts.txt" \
  "$D2_DIR/report.txt" \
  "$D2_DIR/scheduler-contract.txt" \
  "$D2_DIR/evidence-index.json" \
  "$C5_DIR/report.txt" \
  "$C5_DIR/vm101.txt"
do
  [ -s "$required" ] || {
    echo "MISSING_REQUIRED=$required"
    fatal "REQUIRED_ARTIFACT_MISSING" 2 "$LINENO"
  }
done

grep -Fq \
  "decision=PASS_STEP_050M07D_CLASSIFY_FALSE_SUCCESS_AND_EGRESS1_FAILURE" \
  "$M07D_DIR/report.txt" ||
  fatal "M07D_PASS_NOT_PROVEN" 3 "$LINENO"

grep -Fq \
  "decision=PASS_STEP_050M07D2_FREEZE_PRE0420_EVIDENCE" \
  "$D2_DIR/report.txt" ||
  fatal "D2_PASS_NOT_PROVEN" 4 "$LINENO"

cp -a "$M07D_DIR/source-contract.json" \
  "$REPORT_DIR/sources/m07d-source-contract.json"

cp -a "$M07D_DIR/failure-artifacts.txt" \
  "$REPORT_DIR/sources/m07d-failure-artifacts.txt"

cp -a "$M07D_DIR/assessment.json" \
  "$REPORT_DIR/sources/m07d-assessment.json"

cp -a "$D2_DIR/scheduler-contract.txt" \
  "$REPORT_DIR/sources/d2-scheduler-contract.txt"

cp -a "$D2_DIR/evidence-index.json" \
  "$REPORT_DIR/sources/d2-evidence-index.json"

cp -a "$C5_DIR/report.txt" \
  "$REPORT_DIR/sources/c5-report.txt"

cp -a "$C5_DIR/vm101.txt" \
  "$REPORT_DIR/sources/c5-live-stream.txt"

mark_success "persistent_evidence_preserved"

stage "02/05" "Публикую самостоятельный AWG read-only extractor"

cat > "$REPORT_DIR/vm101.sh" <<'VM101'
#!/bin/sh
set -u
umask 077

RUNNER="/usr/local/sbin/router-egress-emergency-refresh.sh"
PLANNER="/usr/local/sbin/router-egress-hmn-plan-top5.sh"
HELPER="/usr/local/lib/router-egress-recovery-state.sh"
CONF="/etc/router-egress-emergency-refresh.conf"
POOL="/root/hmn/cache/ok-awg1-strict-foreign-latest.tsv"

HOOK_INIT="/etc/init.d/router-egress-emergency-decision"
WATCHER_INIT="/etc/init.d/router-egress-health-repair"

fact() {
  printf '__FACT__ %s=%s\n' "$1" "$2"
}

json_block() {
  echo "__JSON_BEGIN__ $1"
  printf '%s\n' "$2"
  echo "__JSON_END__ $1"
}

block() {
  name="$1"
  shift

  echo "__BLOCK_BEGIN__ $name"
  "$@" 2>&1 || true
  echo "__BLOCK_END__ $name"
}

bool_cmd() {
  if "$@" >/dev/null 2>&1; then
    printf true
  else
    printf false
  fi
}

strict_iface() {
  interface="$1"
  attempt=1

  while [ "$attempt" -le 3 ]; do
    if ping \
      -I "$interface" \
      -c 1 \
      -W 3 \
      1.1.1.1 \
      >/dev/null 2>&1
    then
      return 0
    fi

    attempt=$((attempt + 1))
    sleep 1
  done

  return 1
}

routes_all() {
  for table in 201 202 203 204 205; do
    ip route show table "$table" 2>/dev/null |
      grep -q '^default ' ||
      return 1
  done

  return 0
}

state_value() {
  key="$1"
  fallback="$2"

  (
    unset REG_STATE_DIR
    . "$HELPER"
    reg_get_state "$key" "$fallback"
  )
}

repair_counter() {
  (
    unset REG_STATE_DIR
    . "$HELPER"
    reg_daily_repair_get
  )
}

AWG_BIN="$(
  command -v awg 2>/dev/null ||
  true
)"

[ -n "$AWG_BIN" ] || {
  echo "__ERROR__ awg_cli_missing"
  exit 20
}

for required in \
  "$RUNNER" \
  "$PLANNER" \
  "$HELPER" \
  "$CONF" \
  "$POOL"
do
  [ -f "$required" ] || {
    echo "__ERROR__ source_missing=$required"
    exit 21
  }
done

fact awg_binary "$AWG_BIN"
fact standard_wg_cli_used false

echo "__TRACE__ stage=clock_and_schedule"

SNAPSHOT_EPOCH="$(date +%s)"

fact snapshot_epoch "$SNAPSHOT_EPOCH"
fact vm101_local_time "$(date '+%Y-%m-%dT%H:%M:%S%z')"
fact vm101_utc_time "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"

fact etc_tz "$(
  tr '\r\n ' '_' < /etc/TZ 2>/dev/null ||
  echo UNAVAILABLE
)"

fact uci_timezone "$(
  uci -q get system.@system[0].timezone 2>/dev/null ||
  echo UNAVAILABLE
)"

fact uci_zonename "$(
  uci -q get system.@system[0].zonename 2>/dev/null ||
  echo UNAVAILABLE
)"

CRON_LINE="$(
  grep -E \
    '^[[:space:]]*20[[:space:]]+4[[:space:]]+\*[[:space:]]+\*[[:space:]]+\*[[:space:]]+' \
    /etc/crontabs/root \
    2>/dev/null |
  grep -F '/root/hmn/hmn-refresh-pool-safe.sh' |
  head -n1 ||
  true
)"

[ -n "$CRON_LINE" ] || {
  echo "__ERROR__ schedule_missing=20_4_daily_hmn_refresh"
  exit 22
}

fact cron_0420_found true

echo "__BLOCK_BEGIN__ exact_cron_line"
printf '%s\n' "$CRON_LINE"
echo "__BLOCK_END__ exact_cron_line"

echo "__TRACE__ stage=amneziawg_runtime"

HEALTHY_COUNT=0
ENDPOINT_COUNT=0

for interface in vpn1 vpn2 vpn3 vpn4 vpn5; do
  if strict_iface "$interface"; then
    STRICT_VALUE=true
    HEALTHY_COUNT=$((HEALTHY_COUNT + 1))
  else
    STRICT_VALUE=false
  fi

  fact "strict.${interface}" "$STRICT_VALUE"

  DUMP_LINE="$(
    "$AWG_BIN" show "$interface" dump 2>/dev/null |
      sed -n '2p'
  )"

  [ -n "$DUMP_LINE" ] || {
    echo "__ERROR__ awg_endpoint_unresolved=$interface"
    exit 23
  }

  PUBLIC_KEY="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f1
  )"

  ENDPOINT="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f3 |
      tr -d '\r\n'
  )"

  HANDSHAKE="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f5
  )"

  RX_BYTES="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f6
  )"

  TX_BYTES="$(
    printf '%s\n' "$DUMP_LINE" |
      cut -f7
  )"

  if [ -z "$ENDPOINT" ] ||
     [ "$ENDPOINT" = "(none)" ]
  then
    echo "__ERROR__ awg_endpoint_unresolved=$interface"
    exit 24
  fi

  ENDPOINT_COUNT=$((ENDPOINT_COUNT + 1))

  fact "endpoint.${interface}" "$ENDPOINT"

  fact "public_key_sha256.${interface}" "$(
    printf '%s' "$PUBLIC_KEY" |
      sha256sum |
      sed 's/[[:space:]].*$//'
  )"

  fact "latest_handshake.${interface}" "${HANDSHAKE:-0}"
  fact "rx_bytes.${interface}" "${RX_BYTES:-0}"
  fact "tx_bytes.${interface}" "${TX_BYTES:-0}"
done

fact endpoint_count "$ENDPOINT_COUNT"
fact healthy_vpn_slots "$HEALTHY_COUNT"

fact strict_all "$(
  if [ "$HEALTHY_COUNT" -eq 5 ]; then
    echo true
  else
    echo false
  fi
)"

fact routes_all "$(bool_cmd routes_all)"

block awg_show_all_dump "$AWG_BIN" show all dump

echo "__BLOCK_BEGIN__ awg_show_interfaces"

for interface in vpn1 vpn2 vpn3 vpn4 vpn5; do
  echo "===== $interface ====="
  "$AWG_BIN" show "$interface" 2>/dev/null || true
done

echo "__BLOCK_END__ awg_show_interfaces"

echo "__TRACE__ stage=services_state_and_pool"

fact hook_running "$(bool_cmd "$HOOK_INIT" running)"
fact hook_enabled "$(bool_cmd "$HOOK_INIT" enabled)"
fact watcher_running "$(bool_cmd "$WATCHER_INIT" running)"
fact watcher_enabled "$(bool_cmd "$WATCHER_INIT" enabled)"

fact emergency_lock_present "$(
  bool_cmd test -e /var/lock/router-egress-emergency-refresh.lock
)"

fact refresh_lock_present "$(
  bool_cmd test -e /tmp/hmn-refresh-pool-safe.lock
)"

fact state_mode "$(state_value mode UNKNOWN)"
fact state_status "$(
  state_value last_emergency_refresh_status UNKNOWN
)"
fact state_epoch "$(
  state_value last_emergency_refresh_epoch 0
)"
fact repair_counter "$(repair_counter)"

fact commit_raw "$(
  (
    . "$CONF"
    printf '%s' "${EMERGENCY_COMMIT_ENABLED:-UNSET}"
  )
)"

fact pool_sha256 "$(
  sha256sum "$POOL" |
    sed 's/[[:space:]].*$//'
)"

fact pool_rows "$(
  sed '1d' "$POOL" |
    grep -c . ||
  true
)"

fact pool_mtime_epoch "$(date -r "$POOL" +%s)"

LATEST_GENERATION="$(
  readlink -f /root/hmn/configs/awg1/latest 2>/dev/null ||
  true
)"

[ -n "$LATEST_GENERATION" ] ||
  LATEST_GENERATION="UNRESOLVED"

fact latest_generation "$LATEST_GENERATION"

PLANNER_JSON="$("$PLANNER")"
RUNNER_JSON="$("$RUNNER" --dry-run)"

json_block planner "$PLANNER_JSON"
json_block runner "$RUNNER_JSON"

block routes_200_205 sh -c '
  for table in 200 201 202 203 204 205; do
    echo "===== table $table ====="
    ip route show table "$table" 2>/dev/null || true
  done
'

block safe_uci_endpoints sh -c '
  uci -q show network 2>/dev/null |
    grep -E \
      "wireguard_vpn[1-5].*endpoint_(host|port)=" ||
  true
'

block cron_log_tail sh -c '
  tail -n 400 \
    /root/hmn/logs/hmn-refresh-pool-cron.log \
    2>/dev/null ||
  true
'

block emergency_log_tail sh -c '
  tail -n 400 \
    /var/log/router-egress-emergency-refresh.log \
    2>/dev/null ||
  true
'

block filesystem_usage df -Pk

fact read_only true
fact refresh_ran false
fact rebalance_ran false
fact network_changed false
fact services_changed false
fact state_changed false
fact timer_changed false
fact plan_changed false
fact direct_failopen_changed false

echo "__TRACE__ stage=complete"

[ "$ENDPOINT_COUNT" -eq 5 ] || exit 31
[ "$HEALTHY_COUNT" -eq 5 ] || exit 32
[ "$(bool_cmd routes_all)" = true ] || exit 33

exit 0
VM101

chmod 600 "$REPORT_DIR/vm101.sh"
sh -n "$REPORT_DIR/vm101.sh"

mark_success "selfcontained_awg_extractor_published"

stage "03/05" "Снимаю окончательный read-only snapshot VM101"

if ssh pve-mgts \
  "ssh \
    -o BatchMode=yes \
    -o ConnectTimeout=8 \
    -o ServerAliveInterval=20 \
    -o ServerAliveCountMax=6 \
    -o StrictHostKeyChecking=no \
    -o UserKnownHostsFile=/dev/null \
    -i /root/.ssh/pve_to_openwrt_mgts_ed25519 \
    root@10.71.100.2 \
    'sh -s'" \
  < "$REPORT_DIR/vm101.sh" \
  > >(tee "$REPORT_DIR/vm101.txt") \
  2> >(tee "$REPORT_DIR/vm101.stderr" >&2)
then
  VM101_RC=0
else
  VM101_RC=$?
fi

echo "vm101_rc=$VM101_RC" |
  tee -a "$PROGRESS_LOG"

[ "$VM101_RC" -eq 0 ] ||
  fatal "VM101_READONLY_SNAPSHOT_FAILED" "$VM101_RC" "$LINENO"

grep -Fq \
  "__TRACE__ stage=complete" \
  "$REPORT_DIR/vm101.txt" ||
  fatal "REMOTE_COMPLETION_MARKER_MISSING" 5 "$LINENO"

mark_success "vm101_awg_snapshot_complete"

stage "04/05" "Строю exact baseline и ночной comparison contract"

python3 - \
  "$REPORT_DIR/vm101.txt" \
  "$REPORT_DIR/vm101.stderr" \
  "$REPORT_DIR" <<'PY'
import json
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
from zoneinfo import ZoneInfo

runtime_path, stderr_path, report_dir = sys.argv[1:]
root = Path(report_dir)

runtime = Path(runtime_path).read_text(
    encoding="utf-8",
    errors="replace",
)

stderr = Path(stderr_path).read_text(
    encoding="utf-8",
    errors="replace",
)

facts = {}
blocks = {}
current = None
lines = []

for line in runtime.splitlines():
    if line.startswith("__FACT__ "):
        payload = line[len("__FACT__ "):]

        if "=" in payload:
            key, value = payload.split("=", 1)
            facts[key] = value

    elif line.startswith("__JSON_BEGIN__ "):
        current = line[len("__JSON_BEGIN__ "):]
        lines = []

    elif line.startswith("__JSON_END__ "):
        name = line[len("__JSON_END__ "):]

        if name == current:
            blocks[name] = json.loads(
                "\n".join(lines)
            )

        current = None
        lines = []

    elif current is not None:
        lines.append(line)

interfaces = [
    "vpn1",
    "vpn2",
    "vpn3",
    "vpn4",
    "vpn5",
]

endpoints = {
    interface: facts[f"endpoint.{interface}"]
    for interface in interfaces
}

strict = {
    interface:
        facts[f"strict.{interface}"] == "true"
    for interface in interfaces
}

handshakes = {
    interface:
        int(facts[f"latest_handshake.{interface}"])
    for interface in interfaces
}

traffic = {
    interface: {
        "rx_bytes":
            int(facts[f"rx_bytes.{interface}"]),
        "tx_bytes":
            int(facts[f"tx_bytes.{interface}"]),
    }
    for interface in interfaces
}

planner = blocks["planner"]
runner = blocks["runner"]

planner_current = {
    item["iface"]: item.get("current")
    for item in planner.get("plan", [])
    if item.get("iface") in interfaces
}

planner_matches_awg = {
    interface:
        planner_current.get(interface)
            == endpoints[interface]
    for interface in interfaces
}

snapshot_epoch = int(facts["snapshot_epoch"])

snapshot_utc = datetime.fromtimestamp(
    snapshot_epoch,
    tz=timezone.utc,
)

next_run_utc = snapshot_utc.replace(
    hour=4,
    minute=20,
    second=0,
    microsecond=0,
)

if next_run_utc <= snapshot_utc:
    next_run_utc += timedelta(days=1)

next_run_amsterdam = next_run_utc.astimezone(
    ZoneInfo("Europe/Amsterdam")
)

unexpected_stderr = [
    line
    for line in stderr.splitlines()
    if line.strip()
    and not line.startswith(
        "Warning: Permanently added "
    )
]

checks = {
    "awg_cli_used":
        facts.get("standard_wg_cli_used") == "false"
        and bool(facts.get("awg_binary")),

    "five_awg_endpoints":
        len(endpoints) == 5
        and all(
            endpoint
            and endpoint != "(none)"
            and ":" in endpoint
            for endpoint in endpoints.values()
        ),

    "five_unique_endpoints":
        len(set(endpoints.values())) == 5,

    "five_strict_slots":
        all(strict.values())
        and facts.get("healthy_vpn_slots") == "5"
        and facts.get("strict_all") == "true",

    "routes_201_205":
        facts.get("routes_all") == "true",

    "services_running_enabled":
        facts.get("hook_running") == "true"
        and facts.get("hook_enabled") == "true"
        and facts.get("watcher_running") == "true"
        and facts.get("watcher_enabled") == "true",

    "locks_absent":
        facts.get("emergency_lock_present") == "false"
        and facts.get("refresh_lock_present") == "false",

    "cron_0420_utc":
        facts.get("cron_0420_found") == "true",

    "read_only":
        facts.get("read_only") == "true"
        and facts.get("refresh_ran") == "false"
        and facts.get("rebalance_ran") == "false"
        and facts.get("network_changed") == "false"
        and facts.get("services_changed") == "false"
        and facts.get("state_changed") == "false"
        and facts.get("timer_changed") == "false"
        and facts.get("plan_changed") == "false"
        and facts.get("direct_failopen_changed") == "false",

    "stderr_clean":
        not unexpected_stderr,
}

warnings = []

if not all(planner_matches_awg.values()):
    warnings.append(
        "planner_current_differs_from_awg_runtime"
    )

if planner.get("changes_count", 0) != 0:
    warnings.append(
        f"planner_changes_count={planner.get('changes_count')}"
    )

all_ok = all(checks.values())

baseline = {
    "captured_at": {
        "epoch": snapshot_epoch,
        "vm101_local":
            facts["vm101_local_time"],
        "vm101_utc":
            facts["vm101_utc_time"],
        "timezone": {
            "etc_tz": facts.get("etc_tz"),
            "uci_timezone":
                facts.get("uci_timezone"),
            "uci_zonename":
                facts.get("uci_zonename"),
        },
    },
    "schedule": {
        "cron": "20 4 * * *",
        "timezone": "UTC",
        "next_run_utc":
            next_run_utc.isoformat(),
        "next_run_amsterdam":
            next_run_amsterdam.isoformat(),
    },
    "runtime_source": {
        "protocol": "AmneziaWG",
        "cli": "awg",
        "standard_wg_used": False,
    },
    "endpoints": endpoints,
    "strict": strict,
    "handshakes": handshakes,
    "traffic": traffic,
    "healthy_vpn_slots":
        int(facts["healthy_vpn_slots"]),
    "routes_201_205":
        facts["routes_all"] == "true",
    "services": {
        "hook_running":
            facts["hook_running"] == "true",
        "hook_enabled":
            facts["hook_enabled"] == "true",
        "watcher_running":
            facts["watcher_running"] == "true",
        "watcher_enabled":
            facts["watcher_enabled"] == "true",
    },
    "locks": {
        "emergency":
            facts["emergency_lock_present"] == "true",
        "refresh":
            facts["refresh_lock_present"] == "true",
    },
    "state": {
        "mode": facts["state_mode"],
        "status": facts["state_status"],
        "epoch": int(facts["state_epoch"]),
        "repair_counter":
            int(facts["repair_counter"]),
        "commit_raw":
            facts["commit_raw"],
    },
    "pool": {
        "sha256": facts["pool_sha256"],
        "rows": int(facts["pool_rows"]),
        "mtime_epoch":
            int(facts["pool_mtime_epoch"]),
        "latest_generation":
            facts["latest_generation"],
    },
    "planner": {
        "decision": planner.get("decision"),
        "changes_count":
            planner.get("changes_count"),
        "current_endpoints":
            planner_current,
        "matches_awg_runtime":
            planner_matches_awg,
        "plan": planner.get("plan", []),
    },
    "runner": {
        "decision": runner.get("decision"),
        "reason": runner.get("reason"),
        "daily_fail_count":
            runner.get("daily_fail_count"),
        "cooldown_remaining":
            runner.get("cooldown_remaining"),
        "last_status":
            runner.get(
                "last_emergency_refresh_status"
            ),
        "last_epoch":
            runner.get(
                "last_emergency_refresh_epoch"
            ),
    },
    "checks": checks,
    "warnings": warnings,
}

comparison_contract = {
    "schema":
        "post-scheduled-refresh-comparison-v3",
    "reference_file": "exact-baseline.json",
    "runtime_cli": "awg",
    "compare_after": {
        "utc": next_run_utc.isoformat(),
        "amsterdam":
            next_run_amsterdam.isoformat(),
    },
    "required_comparisons": [
        "AmneziaWG endpoints vpn1..vpn5 via awg",
        "strict status vpn1..vpn5",
        "handshake and RX/TX counters",
        "pool sha256, rows and mtime",
        "latest HMN generation",
        "state mode, status and epoch",
        "repair counter",
        "planner decision and changes_count",
        "cron log delta",
        "emergency log delta",
        "commit_failed occurrences",
        "slot_apply_failed_egress1 occurrences",
        "whether scheduled refresh changed live endpoints",
    ],
    "preserved_failure_context": {
        "runner_false_success": True,
        "underlying_failure":
            "slot_apply_failed_egress1",
        "source_contract":
            "sources/m07d-source-contract.json",
        "failure_artifacts":
            "sources/m07d-failure-artifacts.txt",
        "c5_live_stream":
            "sources/c5-live-stream.txt",
    },
    "do_not_mutate_before_comparison": True,
}

assessment = {
    "all_ok": all_ok,
    "decision": (
        "PASS_STEP_050M07D4B_FINAL_SELFCONTAINED_PRE_NIGHT_FREEZE_AWG"
        if all_ok
        else
        "STOP_STEP_050M07D4B_FINAL_SELFCONTAINED_PRE_NIGHT_FREEZE_AWG"
    ),
    "checks": checks,
    "warnings": warnings,
    "failed_checks": [
        name
        for name, value in checks.items()
        if not value
    ],
    "schedule": baseline["schedule"],
    "exact_awg_endpoints_frozen": all_ok,
    "safety": {
        "read_only": True,
        "refresh_ran": False,
        "rebalance_ran": False,
        "network_changed": False,
        "services_changed": False,
        "state_changed": False,
        "timer_changed": False,
        "plan_changed": False,
        "direct_failopen_changed": False,
    },
    "next_action": (
        "Pause changes. After scheduled refresh, "
        "run read-only comparison."
    ),
}

(root / "exact-baseline.json").write_text(
    json.dumps(
        baseline,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)

(root / "comparison-contract.json").write_text(
    json.dumps(
        comparison_contract,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)

(root / "assessment.json").write_text(
    json.dumps(
        assessment,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)

if not all_ok:
    raise SystemExit(
        "failed checks: "
        + ",".join(assessment["failed_checks"])
    )
PY

mark_success "exact_awg_baseline_built"

stage "05/05" "Публикую окончательный checkpoint"

NEXT_UTC="$(
  python3 - "$REPORT_DIR/exact-baseline.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

print(data["schedule"]["next_run_utc"])
PY
)"

NEXT_AMSTERDAM="$(
  python3 - "$REPORT_DIR/exact-baseline.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

print(data["schedule"]["next_run_amsterdam"])
PY
)"

ENDPOINTS="$(
  python3 - "$REPORT_DIR/exact-baseline.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

print(
    ", ".join(
        f"{iface}={endpoint}"
        for iface, endpoint
        in sorted(data["endpoints"].items())
    )
)
PY
)"

WARNINGS="$(
  python3 - "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

print(
    ",".join(data["warnings"])
    if data["warnings"]
    else "NONE"
)
PY
)"

cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=${PASS_DECISION}
all_ok=true
mode=M07_FINAL_SELFCONTAINED_PRE_NIGHT_FREEZE_AWG

schedule:
  cron=20 4 * * *
  timezone=UTC
  next_run_utc=${NEXT_UTC}
  next_run_amsterdam=${NEXT_AMSTERDAM}

runtime:
  protocol=AmneziaWG
  cli=awg
  standard_wg_cli_used=false
  exact_endpoints_frozen=true
  endpoints=${ENDPOINTS}
  healthy_vpn_slots=5
  five_strict_slots=true
  routes_201_205=true
  services_running_enabled=true
  locks_absent=true

assessment:
  warnings=${WARNINGS}
  warnings_are_preserved_not_hidden=true
  details=exact-baseline.json
  comparison_contract=comparison-contract.json

preserved_context:
  m07d_source_contract=true
  m07d_failure_artifacts=true
  c5_live_stream=true
  d2_scheduler_contract=true

safety:
  read_only=true
  refresh_ran=false
  rebalance_ran=false
  network_changed=false
  services_changed=false
  state_changed=false
  timer_changed=false
  plan_changed=false
  direct_failopen_changed=false

plan:
  current_milestone=M07
  milestone_completed=false
  milestone_changed=false

pause_contract:
  changes_before_scheduled_refresh=false
  next_action=READONLY_POST_SCHEDULED_REFRESH_COMPARISON

TRYCF_REPORT=${TRYCF_REPORT}
REPORT_TXT=${REPORT_TXT}
FACTS_JSON=${FACTS_JSON}
ARCHITECTURE_PLAN=${ARCHITECTURE_PLAN}
XS_MAP=${XS_MAP}
GLOBAL_PROJECT_PLAN=${GLOBAL_PROJECT_PLAN}
EOF

python3 - \
  "$REPORT_DIR/assessment.json" \
  "$REPORT_DIR/exact-baseline.json" \
  "$STEP" \
  "$TS" \
  "$TRYCF_REPORT" \
  "$REPORT_TXT" \
  "$FACTS_JSON" \
  "$ARCHITECTURE_PLAN" \
  "$XS_MAP" \
  "$GLOBAL_PROJECT_PLAN" \
  > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    assessment_path,
    baseline_path,
    step,
    timestamp,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
) = sys.argv[1:]

with open(assessment_path, encoding="utf-8") as source:
    assessment = json.load(source)

with open(baseline_path, encoding="utf-8") as source:
    baseline = json.load(source)

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "generated_at_utc": timestamp,
    "assessment": assessment,
    "exact_baseline": baseline,
    "operation": {
        "final_pre_night_checkpoint": True,
        "runtime_protocol": "AmneziaWG",
        "runtime_cli": "awg",
        "standard_wg_cli_used": False,
        "exact_endpoints_frozen": True,
        "schedule_frozen": True,
        "failure_context_preserved": True,
        "comparison_contract_created": True,
    },
    "safety": assessment["safety"],
    "plan": {
        "current_milestone": "M07",
        "milestone_completed": False,
        "milestone_changed": False,
    },
    "next_step":
        "READONLY_POST_SCHEDULED_REFRESH_COMPARISON",
    "publish": {
        "trycf_report": report,
        "report_txt": report_txt,
        "facts_json": facts_json,
        "architecture_plan": architecture,
        "xs_map": xs_map,
        "global_project_plan": global_plan,
    },
}, ensure_ascii=False, indent=2))
PY

create_index

find "$REPORT_DIR" \
  -type f \
  ! -name SHA256SUMS \
  -print0 |
  sort -z |
  xargs -0 sha256sum \
  > "$REPORT_DIR/SHA256SUMS"

mark_success "final_checkpoint_published"
trap - ERR

echo "decision=$PASS_DECISION" |
  tee -a "$PROGRESS_LOG"

echo "protocol=AmneziaWG" |
  tee -a "$PROGRESS_LOG"

echo "runtime_cli=awg" |
  tee -a "$PROGRESS_LOG"

echo "standard_wg_cli_used=false" |
  tee -a "$PROGRESS_LOG"

echo "exact_endpoints_frozen=true" |
  tee -a "$PROGRESS_LOG"

echo "endpoints=$ENDPOINTS" |
  tee -a "$PROGRESS_LOG"

echo "next_run_utc=$NEXT_UTC" |
  tee -a "$PROGRESS_LOG"

echo "next_run_amsterdam=$NEXT_AMSTERDAM" |
  tee -a "$PROGRESS_LOG"

echo "warnings=$WARNINGS" |
  tee -a "$PROGRESS_LOG"

echo "healthy_vpn_slots=5" |
  tee -a "$PROGRESS_LOG"

echo "current_milestone=M07" |
  tee -a "$PROGRESS_LOG"

echo "next_action=READONLY_POST_SCHEDULED_REFRESH_COMPARISON" |
  tee -a "$PROGRESS_LOG"

echo "read_only=true" |
  tee -a "$PROGRESS_LOG"

print_links
