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

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

STEP="STEP_050LP2A_CLASSIFY_INVENTORY_STOP_AND_FREEZE_TOOL_MAP"
PLAN_ID="vm101-hmn-autonomous-egress-recovery"

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

PREVIOUS_REPORT="${PUBLIC_BASE}/20260711-151610_step050lp2_readonly_inventory_vm101_recovery_toolchain/"
ARCHITECTURE_PLAN="${PUBLIC_BASE}/20260711-150958_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/"

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

PREVIOUS_DIR="${PUBROOT}/20260711-151610_step050lp2_readonly_inventory_vm101_recovery_toolchain"

TS="$(date -u +%Y%m%d-%H%M%S)"
REPORT_SLUG="${TS}_step050lp2a_classify_inventory_stop_and_freeze_tool_map"
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" \
  "$STATE_ROOT"

# Точный copy-paste script сохраняется первым.
cp -a "$0" "$REPORT_DIR/step.sh"
chmod 600 "$REPORT_DIR/step.sh"

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

stage() {
  echo
  echo ">>> [$1] $2" | tee -a "$PROGRESS_LOG"

  date -u '+%Y-%m-%dT%H:%M:%SZ' |
    sed 's/^/    utc=/' |
    tee -a "$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"
}

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

  cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=STOP_${STEP}_${reason}
all_ok=false
mode=LOCAL_READ_ONLY_SALVAGE
error_rc=${rc}
error_line=${line}
production_modified=false
vm100_modified=false
vm101_modified=false
vm121_modified=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 - \
    "$STEP" \
    "$reason" \
    "$rc" \
    "$line" \
    "$TRYCF_REPORT" \
    "$REPORT_TXT" \
    "$FACTS_JSON" \
    "$ARCHITECTURE_PLAN" \
    "$XS_MAP" \
    "$GLOBAL_PROJECT_PLAN" \
    > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

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

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),
    },
    "mode": "LOCAL_READ_ONLY_SALVAGE",
    "safety": {
        "production_modified": False,
        "vm100_modified": False,
        "vm101_modified": False,
        "vm121_modified": 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
}

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

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

trap 'on_error "$LINENO"' ERR

stage "01/06" "Проверяю локальные артефакты STEP_050LP2"

for required in \
  "$PREVIOUS_DIR/report.txt" \
  "$PREVIOUS_DIR/facts.json" \
  "$PREVIOUS_DIR/assessment.json" \
  "$PREVIOUS_DIR/step.sh" \
  "$PREVIOUS_DIR/vm101.sh" \
  "$PREVIOUS_DIR/vm101-archive.sh" \
  "$PREVIOUS_DIR/vm101-inventory.txt" \
  "$PREVIOUS_DIR/vm101-inventory.stderr" \
  "$PREVIOUS_DIR/vm101-archive-manifest.txt" \
  "$PREVIOUS_DIR/source-publication-audit.json" \
  "$PREVIOUS_DIR/current-pointer-audit.txt" \
  "$STATE_ROOT/current-local-architecture-plan-url.txt" \
  "$STATE_ROOT/current-vm101-local-plan.env"
do
  [ -e "$required" ] || {
    echo "MISSING_REQUIRED=$required"
    exit 1
  }
done

cp -a \
  "$PREVIOUS_DIR/report.txt" \
  "$REPORT_DIR/sources/step050lp2-report.txt"

cp -a \
  "$PREVIOUS_DIR/facts.json" \
  "$REPORT_DIR/sources/step050lp2-facts.json"

cp -a \
  "$PREVIOUS_DIR/assessment.json" \
  "$REPORT_DIR/sources/step050lp2-assessment.json"

cp -a \
  "$PREVIOUS_DIR/step.sh" \
  "$REPORT_DIR/sources/step050lp2-step.sh"

cp -a \
  "$PREVIOUS_DIR/vm101.sh" \
  "$REPORT_DIR/sources/step050lp2-vm101.sh"

cp -a \
  "$PREVIOUS_DIR/vm101-archive.sh" \
  "$REPORT_DIR/sources/step050lp2-vm101-archive.sh"

stage "02/06" "Проверяю план, pointers и инструменты публикации"

CURRENT_PLAN="$(
  tr -d '\r\n' \
    < "$STATE_ROOT/current-local-architecture-plan-url.txt"
)"

[ "$CURRENT_PLAN" = "$ARCHITECTURE_PLAN" ] || {
  echo "CURRENT_PLAN=$CURRENT_PLAN"
  echo "EXPECTED_PLAN=$ARCHITECTURE_PLAN"
  exit 1
}

grep -Fq \
  "PLAN_ID=${PLAN_ID}" \
  "$STATE_ROOT/current-vm101-local-plan.env"

grep -Fq \
  "CURRENT_MILESTONE=M06" \
  "$STATE_ROOT/current-vm101-local-plan.env"

grep -Fq \
  "ARCHITECTURE_PLAN=${ARCHITECTURE_PLAN}" \
  "$STATE_ROOT/current-vm101-local-plan.env"

for tool in \
  "$ROOT/bin/vm101-local-plan-status" \
  "$ROOT/bin/vm101-local-plan-republish" \
  "$STATE_ROOT/local-plans/${PLAN_ID}/canonical-plan.md" \
  "$STATE_ROOT/local-plans/${PLAN_ID}/milestones.json" \
  "$STATE_ROOT/local-plans/${PLAN_ID}/render_plan.py" \
  "$STATE_ROOT/local-plans/${PLAN_ID}/set_status.py" \
  "$STATE_ROOT/local-plans/${PLAN_ID}/republish.sh"
do
  [ -s "$tool" ] || {
    echo "MISSING_PLAN_TOOL=$tool"
    exit 1
  }
done

stage "03/06" "Проверяю опубликованную карту исходников VM101"

SOURCE_ROOT="$PREVIOUS_DIR/sources/vm101-root"

declare -a REQUIRED_VM101_SOURCES=(
  "usr/local/sbin/router-egress-health-repair-watch.sh"
  "usr/local/sbin/router-egress-emergency-decision-hook.sh"
  "usr/local/sbin/router-egress-emergency-refresh.sh"
  "usr/local/sbin/router-egress-hmn-plan-top5.sh"
  "usr/local/sbin/router-egress-hmn-rebalance-top5-apply.sh"
  "usr/local/lib/router-egress-recovery-state.sh"
  "root/hmn/hmn-refresh-pool-safe.sh"
  "etc/init.d/router-egress-health-repair"
  "etc/init.d/router-egress-emergency-decision"
  "etc/init.d/router-egress-slots"
)

: > "$REPORT_DIR/source-map.txt"

SOURCE_MAP_COMPLETE=true

for relative in "${REQUIRED_VM101_SOURCES[@]}"; do
  absolute="$SOURCE_ROOT/$relative"

  if [ -s "$absolute" ]; then
    hash="$(
      sha256sum "$absolute" |
        cut -d' ' -f1
    )"

    echo "present=true sha256=$hash path=$relative" \
      >> "$REPORT_DIR/source-map.txt"
  else
    echo "present=false path=$relative" \
      >> "$REPORT_DIR/source-map.txt"

    SOURCE_MAP_COMPLETE=false
  fi
done

stage "04/06" "Классифицирую точные failed checks"

python3 - \
  "$PREVIOUS_DIR/assessment.json" \
  "$PREVIOUS_DIR/vm101-inventory.stderr" \
  "$PREVIOUS_DIR/source-publication-audit.json" \
  "$SOURCE_MAP_COMPLETE" \
  "$CURRENT_PLAN" \
  "$ARCHITECTURE_PLAN" \
  "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import re
import sys
from pathlib import Path

(
    assessment_path,
    stderr_path,
    publication_audit_path,
    source_map_complete,
    current_plan,
    expected_plan,
    output_path,
) = sys.argv[1:]

assessment = json.loads(
    Path(assessment_path).read_text(
        encoding="utf-8",
        errors="replace",
    )
)

publication_audit = json.loads(
    Path(publication_audit_path).read_text(
        encoding="utf-8",
        errors="replace",
    )
)

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

checks = assessment.get("checks", {})
reported_failed = assessment.get("failed_checks", [])

calculated_failed = [
    name
    for name, result in checks.items()
    if result is not True
]

core_check_names = [
    "vm101_inventory_completed",
    "m06_toolchain_complete",
    "m07_base_tools_present",
    "runner_dryrun_available",
    "planner_readonly_available",
    "watcher_dryrun_available",
    "five_runtime_slots_present",
    "five_runtime_routes_present",
]

core_checks = {
    name: checks.get(name)
    for name in core_check_names
}

core_failed = [
    name
    for name, result in core_checks.items()
    if result is not True
]

benign_stderr_patterns = [
    r"^Warning: Permanently added ",
    r"^Pseudo-terminal will not be allocated",
]

stderr_lines = [
    line
    for line in stderr.splitlines()
    if line.strip()
]

non_benign_stderr = [
    line
    for line in stderr_lines
    if not any(
        re.search(pattern, line)
        for pattern in benign_stderr_patterns
    )
]

sources_published = (
    int(publication_audit.get("published_count", 0)) >= 1
)

source_map_ok = source_map_complete.lower() == "true"
pointer_ok = current_plan == expected_plan

salvage_pass = (
    not core_failed
    and source_map_ok
    and pointer_ok
    and sources_published
)

if salvage_pass:
    classification = "CORE_INVENTORY_PASS_NONBLOCKING_CHECK_STOP"
    next_step = (
        "M06_ENABLE_EMERGENCY_COMMIT_"
        "REUSING_PUBLISHED_VM101_TOOLCHAIN"
    )
else:
    classification = "BLOCKING_INVENTORY_GAP"
    next_step = "REPAIR_INVENTORY_GAP_BEFORE_M06"

result = {
    "raw_decision": assessment.get("decision"),
    "raw_all_ok": assessment.get("all_ok"),
    "reported_failed_checks": reported_failed,
    "calculated_failed_checks": calculated_failed,
    "checks": checks,
    "core_checks": core_checks,
    "core_failed_checks": core_failed,
    "stderr": {
        "empty": not bool(stderr),
        "lines": stderr_lines,
        "non_benign_lines": non_benign_stderr,
    },
    "source_publication": publication_audit,
    "source_map_complete": source_map_ok,
    "current_plan_pointer_ok": pointer_ok,
    "classification": classification,
    "salvage_pass": salvage_pass,
    "m06_toolchain_complete":
        assessment.get(
            "m06_toolchain",
            {},
        ).get("complete"),
    "m07_base_toolchain_complete":
        assessment.get(
            "m07_base_toolchain",
            {},
        ).get("complete"),
    "runtime": assessment.get("runtime", {}),
    "future_capability_evidence":
        assessment.get(
            "future_capability_evidence",
            {},
        ),
    "future_work_interpretation":
        assessment.get(
            "future_work_interpretation",
            {},
        ),
    "next_step": next_step,
}

Path(output_path).write_text(
    json.dumps(
        result,
        ensure_ascii=False,
        indent=2,
    ) + "\n",
    encoding="utf-8",
)
PY

SALVAGE_PASS="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

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

print("true" if data["salvage_pass"] else "false")
PY
)"

CLASSIFICATION="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

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

print(data["classification"])
PY
)"

FAILED_CHECKS="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

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

failed = data.get("calculated_failed_checks", [])

print(",".join(failed) if failed else "NONE")
PY
)"

CORE_FAILED="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

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

failed = data.get("core_failed_checks", [])

print(",".join(failed) if failed else "NONE")
PY
)"

M06_COMPLETE="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

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

print(
    "true"
    if data.get("m06_toolchain_complete") is True
    else "false"
)
PY
)"

M07_COMPLETE="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

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

print(
    "true"
    if data.get("m07_base_toolchain_complete") is True
    else "false"
)
PY
)"

CURRENT_COMMIT="$(
  python3 - "$REPORT_DIR/diagnosis.json" <<'PY'
import json
import sys

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

print(
    data.get("runtime", {}).get(
        "emergency_commit_enabled_raw",
        "UNKNOWN",
    )
)
PY
)"

stage "05/06" "Публикую salvage report"

if [ "$SALVAGE_PASS" = true ]; then
  DECISION="PASS_${STEP}"
else
  DECISION="STOP_${STEP}_BLOCKING_INVENTORY_GAP"
fi

cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=${DECISION}
all_ok=${SALVAGE_PASS}
mode=LOCAL_READ_ONLY_SALVAGE
production_modified=false
vm100_modified=false
vm101_modified=false
vm121_modified=false

classification:
  raw_step050lp2=STOP
  classification=${CLASSIFICATION}
  calculated_failed_checks=${FAILED_CHECKS}
  core_failed_checks=${CORE_FAILED}
  source_map_complete=${SOURCE_MAP_COMPLETE}
  current_plan_pointer_ok=true

implementation_readiness:
  current_milestone=M06
  m06_toolchain_complete=${M06_COMPLETE}
  m07_base_toolchain_complete=${M07_COMPLETE}
  emergency_commit_enabled_raw=${CURRENT_COMMIT}

next_step:
  M06_ENABLE_EMERGENCY_COMMIT_REUSING_PUBLISHED_VM101_TOOLCHAIN

artifacts:
  step_script=step.sh
  diagnosis=diagnosis.json
  source_map=source-map.txt
  previous_assessment=sources/step050lp2-assessment.json
  previous_step=sources/step050lp2-step.sh
  previous_vm101=sources/step050lp2-vm101.sh

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 - \
  "$STEP" \
  "$TS" \
  "$DECISION" \
  "$SALVAGE_PASS" \
  "$CLASSIFICATION" \
  "$FAILED_CHECKS" \
  "$CORE_FAILED" \
  "$SOURCE_MAP_COMPLETE" \
  "$M06_COMPLETE" \
  "$M07_COMPLETE" \
  "$CURRENT_COMMIT" \
  "$PREVIOUS_REPORT" \
  "$TRYCF_REPORT" \
  "$REPORT_TXT" \
  "$FACTS_JSON" \
  "$ARCHITECTURE_PLAN" \
  "$XS_MAP" \
  "$GLOBAL_PROJECT_PLAN" \
  > "$REPORT_DIR/facts.json" <<'PY'
import json
import sys

(
    step,
    timestamp,
    decision,
    salvage_pass,
    classification,
    failed_checks,
    core_failed,
    source_map,
    m06,
    m07,
    commit,
    previous_report,
    report,
    report_txt,
    facts_json,
    architecture,
    xs_map,
    global_plan,
) = sys.argv[1:]

def split_value(value):
    return [] if value == "NONE" else value.split(",")

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "generated_at_utc": timestamp,
    "assessment": {
        "decision": decision,
        "all_ok": salvage_pass == "true",
        "classification": classification,
        "calculated_failed_checks":
            split_value(failed_checks),
        "core_failed_checks":
            split_value(core_failed),
    },
    "readiness": {
        "current_milestone": "M06",
        "source_map_complete":
            source_map == "true",
        "m06_toolchain_complete":
            m06 == "true",
        "m07_base_toolchain_complete":
            m07 == "true",
        "emergency_commit_enabled_raw":
            commit,
        "next_step":
            "M06_ENABLE_EMERGENCY_COMMIT_"
            "REUSING_PUBLISHED_VM101_TOOLCHAIN",
    },
    "safety": {
        "production_modified": False,
        "vm100_modified": False,
        "vm101_modified": False,
        "vm121_modified": False,
    },
    "source": {
        "step050lp2": previous_report,
    },
    "artifacts": {
        "step_script": "step.sh",
        "diagnosis": "diagnosis.json",
        "source_map": "source-map.txt",
    },
    "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

cat > "$REPORT_DIR/index.html" <<EOF
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>${STEP}</title>
</head>
<body style="font-family:system-ui;max-width:1000px;margin:40px auto;padding:0 20px;line-height:1.5">

<h1>${STEP}</h1>

<h2>Результат</h2>
<ul>
<li><a href="report.txt">report.txt</a></li>
<li><a href="facts.json">facts.json</a></li>
<li><a href="diagnosis.json">diagnosis.json</a></li>
<li><a href="source-map.txt">source-map.txt</a></li>
<li><a href="step.sh">step.sh</a></li>
</ul>

<h2>Исходные материалы STEP_050LP2</h2>
<ul>
<li><a href="sources/step050lp2-assessment.json">assessment.json</a></li>
<li><a href="sources/step050lp2-step.sh">step.sh</a></li>
<li><a href="sources/step050lp2-vm101.sh">vm101.sh</a></li>
<li><a href="sources/step050lp2-vm101-archive.sh">vm101-archive.sh</a></li>
</ul>

<h2>Планы</h2>
<ul>
<li><a href="${ARCHITECTURE_PLAN}">Local architecture plan</a></li>
<li><a href="${XS_MAP}">XS Map</a></li>
<li><a href="${GLOBAL_PROJECT_PLAN}">Global project plan</a></li>
</ul>

</body>
</html>
EOF

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

stage "06/06" "Завершаю классификацию"

trap - ERR

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

echo "classification=$CLASSIFICATION" |
  tee -a "$PROGRESS_LOG"

echo "core_failed_checks=$CORE_FAILED" |
  tee -a "$PROGRESS_LOG"

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

print_links

[ "$SALVAGE_PASS" = true ]
