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

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

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

PREVIOUS_REPORT="${PUBLIC_BASE}/20260711-141947_step050i9_full_audit_minimal_enable_commit_dryrun/"
PREVIOUS_ARCHITECTURE_PLAN="${PUBLIC_BASE}/20260711-122923_local_architecture_plan_hmn_egress_recovery_evidence_bound_salvage/"
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}/state"
PUBROOT="${ROOT}/public/r/${TOKEN}"

PREVIOUS_DIR="${PUBROOT}/20260711-141947_step050i9_full_audit_minimal_enable_commit_dryrun"
PREVIOUS_PLAN_DIR="${PUBROOT}/20260711-122923_local_architecture_plan_hmn_egress_recovery_evidence_bound_salvage"

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

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

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

# Первым артефактом сохраняем точный copy-paste STEP.
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=READ_ONLY_EXISTING_TOOL_RESEARCH
error_rc=${rc}
error_line=${line}
production_modified=false
vm100_modified=false
vm101_modified=false
vm121_modified=false
refresh_ran=false
rebalance_apply_ran=false
direct_failopen_enabled=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": "READ_ONLY_EXISTING_TOOL_RESEARCH",
    "safety": {
        "production_modified": False,
        "vm100_modified": False,
        "vm101_modified": False,
        "vm121_modified": False,
        "refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_enabled": 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"

  echo
  echo "STOP: $STEP"
  echo "rc=$rc"
  echo "line=$line"

  print_links
  exit "$rc"
}

trap 'on_error "$LINENO"' ERR

# Remote script тоже создаётся и публикуется до любых проверок.
cat > "$REPORT_DIR/vm121.sh" <<'VM121'
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077

trace() {
  printf '__TRACE__ stage=%s\n' "$1"
}

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

block() {
  echo "__BLOCK_BEGIN__ $1"
  printf '%s\n' "$2"
  echo "__BLOCK_END__ $1"
}

die() {
  local code="$1"
  shift

  printf '__ERROR__ code=%s message=%s\n' \
    "$code" \
    "$*"

  exit "$code"
}

trace docker_inventory

command -v docker >/dev/null 2>&1 ||
  die 21 "docker_command_missing"

DOCKER_PS="$(
  docker ps \
    --format '{{.Names}}\t{{.Image}}\t{{.Status}}'
)"

block docker_ps "$DOCKER_PS"

mapfile -t RUNNING_CONTAINERS < <(
  docker ps --format '{{.Names}}'
)

[ "${#RUNNING_CONTAINERS[@]}" -gt 0 ] ||
  die 22 "no_running_containers"

trace postgres_candidate_discovery

declare -a CANDIDATES=()

for container in "${RUNNING_CONTAINERS[@]}"; do
  ENVIRONMENT="$(
    docker inspect \
      --format '{{range .Config.Env}}{{println .}}{{end}}' \
      "$container"
  )"

  IMAGE="$(
    docker inspect \
      --format '{{.Config.Image}}' \
      "$container"
  )"

  HAS_PSQL=false

  if docker exec "$container" \
    sh -lc 'command -v psql >/dev/null 2>&1'
  then
    HAS_PSQL=true
  fi

  HAS_POSTGRES_ENV=false

  if grep -Eq \
    '^(POSTGRES_USER|POSTGRES_DB)=' \
    <<< "$ENVIRONMENT"
  then
    HAS_POSTGRES_ENV=true
  fi

  fact "container.${container}.image" "$IMAGE"
  fact "container.${container}.has_psql" "$HAS_PSQL"
  fact "container.${container}.has_postgres_env" "$HAS_POSTGRES_ENV"

  if [ "$HAS_PSQL" = true ] &&
     [ "$HAS_POSTGRES_ENV" = true ]
  then
    CANDIDATES+=("$container")
  fi
done

fact postgres_candidate_count "${#CANDIDATES[@]}"

[ "${#CANDIDATES[@]}" -ge 1 ] ||
  die 23 "postgres_candidate_not_found"

PG_CONTAINER=""

for candidate in "${CANDIDATES[@]}"; do
  if [ "$candidate" = "wgaccess-postgres" ]; then
    PG_CONTAINER="$candidate"
    break
  fi
done

if [ -z "$PG_CONTAINER" ]; then
  for candidate in "${CANDIDATES[@]}"; do
    case "$candidate" in
      *postgres*)
        PG_CONTAINER="$candidate"
        break
        ;;
    esac
  done
fi

if [ -z "$PG_CONTAINER" ]; then
  PG_CONTAINER="${CANDIDATES[0]}"
fi

fact postgres_container "$PG_CONTAINER"

trace postgres_environment

PG_ENV="$(
  docker inspect \
    --format '{{range .Config.Env}}{{println .}}{{end}}' \
    "$PG_CONTAINER"
)"

PGUSER_VALUE="$(
  sed -n 's/^POSTGRES_USER=//p' <<< "$PG_ENV" |
    head -n1
)"

PGDATABASE_VALUE="$(
  sed -n 's/^POSTGRES_DB=//p' <<< "$PG_ENV" |
    head -n1
)"

[ -n "$PGUSER_VALUE" ] ||
  PGUSER_VALUE="postgres"

[ -n "$PGDATABASE_VALUE" ] ||
  PGDATABASE_VALUE="$PGUSER_VALUE"

fact postgres_user "$PGUSER_VALUE"
fact postgres_database "$PGDATABASE_VALUE"

psql_query() {
  docker exec "$PG_CONTAINER" \
    psql \
      -X \
      -v ON_ERROR_STOP=1 \
      -U "$PGUSER_VALUE" \
      -d "$PGDATABASE_VALUE" \
      -Atqc "$1"
}

trace database_ping

DB_PING="$(psql_query 'SELECT 1;')"

[ "$DB_PING" = "1" ] ||
  die 24 "database_ping_failed_result=$DB_PING"

fact database_ping "$DB_PING"

trace schema_inventory

PUBLIC_TABLES="$(
  psql_query "
    SELECT tablename
    FROM pg_tables
    WHERE schemaname='public'
    ORDER BY tablename;
  "
)"

block public_tables "$PUBLIC_TABLES"

table_exists() {
  local table="$1"

  psql_query "
    SELECT CASE
      WHEN to_regclass('public.${table}') IS NULL
      THEN 'false'
      ELSE 'true'
    END;
  "
}

PEERS_EXISTS="$(table_exists peers)"
LEASES_EXISTS="$(table_exists peer_egress_leases)"
JOBS_EXISTS="$(table_exists jobs)"

fact table.peers.exists "$PEERS_EXISTS"
fact table.peer_egress_leases.exists "$LEASES_EXISTS"
fact table.jobs.exists "$JOBS_EXISTS"

[ "$PEERS_EXISTS" = true ] ||
  die 25 "peers_table_missing"

[ "$LEASES_EXISTS" = true ] ||
  die 26 "peer_egress_leases_table_missing"

[ "$JOBS_EXISTS" = true ] ||
  die 27 "jobs_table_missing"

trace schema_columns

PEERS_COLUMNS="$(
  psql_query "
    SELECT column_name
    FROM information_schema.columns
    WHERE table_schema='public'
      AND table_name='peers'
    ORDER BY ordinal_position;
  "
)"

LEASES_COLUMNS="$(
  psql_query "
    SELECT column_name
    FROM information_schema.columns
    WHERE table_schema='public'
      AND table_name='peer_egress_leases'
    ORDER BY ordinal_position;
  "
)"

JOBS_COLUMNS="$(
  psql_query "
    SELECT column_name
    FROM information_schema.columns
    WHERE table_schema='public'
      AND table_name='jobs'
    ORDER BY ordinal_position;
  "
)"

block peers_columns "$PEERS_COLUMNS"
block peer_egress_leases_columns "$LEASES_COLUMNS"
block jobs_columns "$JOBS_COLUMNS"

grep -Fxq enabled <<< "$PEERS_COLUMNS" ||
  die 28 "peers_enabled_column_missing"

grep -Fxq status <<< "$JOBS_COLUMNS" ||
  die 29 "jobs_status_column_missing"

trace canonical_counts

ENABLED_PEERS="$(
  psql_query "
    SELECT count(*)
    FROM peers
    WHERE enabled IS TRUE;
  "
)"

LEASE_ROWS="$(
  psql_query "
    SELECT count(*)
    FROM peer_egress_leases;
  "
)"

PENDING_JOBS="$(
  psql_query "
    SELECT count(*)
    FROM jobs
    WHERE status='pending';
  "
)"

fact enabled_peers "$ENABLED_PEERS"
fact lease_rows "$LEASE_ROWS"
fact pending_jobs "$PENDING_JOBS"

trace related_existing_tools

RELATED_TOOLS="$(
  {
    find /opt/wg-access \
      -maxdepth 4 \
      -type f \
      \( \
        -name '*.sh' \
        -o -name 'docker-compose*.yml' \
        -o -name 'docker-compose*.yaml' \
        -o -name '.env' \
      \) \
      -print 2>/dev/null ||
      true

    find /usr/local/sbin \
      -maxdepth 1 \
      -type f \
      -name '*wg-access*' \
      -print 2>/dev/null ||
      true
  } |
    sort
)"

block related_tools "$RELATED_TOOLS"

fact related_tool_count "$(
  sed '/^$/d' <<< "$RELATED_TOOLS" |
    wc -l |
    tr -d ' '
)"

trace complete
VM121

chmod 600 "$REPORT_DIR/vm121.sh"

stage "01/07" "Исследую точные артефакты STEP_050I9"

for required in \
  "$PREVIOUS_DIR/report.txt" \
  "$PREVIOUS_DIR/facts.json" \
  "$PREVIOUS_DIR/assessment.json" \
  "$PREVIOUS_DIR/outer-source.sh" \
  "$PREVIOUS_DIR/vm101-source.sh" \
  "$PREVIOUS_DIR/vm101.txt" \
  "$PREVIOUS_DIR/vm121.txt" \
  "$PREVIOUS_DIR/vm121.stderr" \
  "$PREVIOUS_DIR/vm101-validation-rollback.txt" \
  "$PREVIOUS_PLAN_DIR/plan.md"
do
  [ -e "$required" ] || {
    echo "MISSING_REQUIRED=$required"
    exit 1
  }
done

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

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

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

cp -a \
  "$PREVIOUS_DIR/outer-source.sh" \
  "$REPORT_DIR/sources/step050i9-step.sh"

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

cp -a \
  "$PREVIOUS_DIR/vm121.txt" \
  "$REPORT_DIR/sources/step050i9-vm121.stdout"

cp -a \
  "$PREVIOUS_DIR/vm121.stderr" \
  "$REPORT_DIR/sources/step050i9-vm121.stderr"

cp -a \
  "$PREVIOUS_DIR/vm101-validation-rollback.txt" \
  "$REPORT_DIR/sources/step050i9-rollback.txt"

python3 - \
  "$PREVIOUS_DIR/assessment.json" \
  "$PREVIOUS_DIR/report.txt" \
  "$PREVIOUS_DIR/vm121.stderr" \
  "$REPORT_DIR/previous-step-analysis.json" <<'PY'
import json
import sys
from pathlib import Path

assessment_path, report_path, stderr_path, output_path = sys.argv[1:]

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

report = Path(report_path).read_text(
    encoding="utf-8",
    errors="replace",
)

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

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

non_vm121_failures = [
    key
    for key, value in checks.items()
    if key != "vm121" and not value
]

if not stderr:
    classification = "EMPTY_STDERR_OR_TRANSPORT_FAILURE"
elif "readonly variable" in stderr:
    classification = "BASH_RESERVED_OR_READONLY_VARIABLE"
elif "Could not resolve hostname" in stderr:
    classification = "SSH_DNS_RESOLUTION_FAILURE"
elif "Connection timed out" in stderr:
    classification = "SSH_CONNECTION_TIMEOUT"
elif "Connection refused" in stderr:
    classification = "SSH_CONNECTION_REFUSED"
elif "No such container" in stderr:
    classification = "POSTGRES_CONTAINER_DISCOVERY_FAILURE"
elif "does not exist" in stderr:
    classification = "DATABASE_SCHEMA_OR_OBJECT_FAILURE"
elif "unbound variable" in stderr:
    classification = "BASH_UNBOUND_VARIABLE"
elif "permission denied" in stderr.lower():
    classification = "PERMISSION_FAILURE"
else:
    classification = "OTHER_RECORDED_VM121_FAILURE"

result = {
    "previous_decision": assessment.get("decision"),
    "previous_failed_checks": failed,
    "previous_non_vm121_failures": non_vm121_failures,
    "previous_vm101_core_passed": not non_vm121_failures,
    "previous_vm121_failed_only": failed == ["vm121"],
    "previous_rollback_proven":
        "rollback_rc=0" in report
        and "production_modified=false" in report,
    "previous_vm121_stderr_empty": not bool(stderr),
    "previous_vm121_failure_classification": classification,
    "previous_vm121_stderr": stderr,
}

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

stage "02/07" "Ищу ранее успешные способы проверки VM121"

python3 - \
  "$PUBROOT" \
  "$REPORT_DIR/prior-vm121-methods.txt" <<'PY'
import sys
from pathlib import Path

root = Path(sys.argv[1])
output = Path(sys.argv[2])

matches = []

for path in root.glob("*/*"):
    if not path.is_file():
        continue

    name = path.name.lower()

    if "vm121" not in name and path.suffix != ".sh":
        continue

    try:
        text = path.read_text(
            encoding="utf-8",
            errors="replace",
        )
    except OSError:
        continue

    score = 0

    if "vm121_enabled_peers=5" in text:
        score += 5
    if "vm121_lease_rows=5" in text:
        score += 5
    if "vm121_pending_jobs=0" in text:
        score += 5
    if "peer_egress_leases" in text:
        score += 2
    if "POSTGRES_USER" in text:
        score += 1
    if "psql" in text:
        score += 1

    if score:
        matches.append((score, str(path)))

matches.sort(
    key=lambda item: (item[0], item[1]),
    reverse=True,
)

lines = [
    f"score={score} path={path}"
    for score, path in matches[:40]
]

output.write_text(
    "\n".join(lines) + ("\n" if lines else ""),
    encoding="utf-8",
)
PY

stage "03/07" "Запускаю read-only исследование VM121"

if ssh vm121 \
  'bash -s' \
  < "$REPORT_DIR/vm121.sh" \
  > "$REPORT_DIR/vm121-research.txt" \
  2> "$REPORT_DIR/vm121-research.stderr"
then
  VM121_RC=0
else
  VM121_RC=$?
fi

echo "vm121_rc=$VM121_RC" |
  tee -a "$PROGRESS_LOG"

stage "04/07" "Формирую assessment и точную классификацию"

python3 - \
  "$REPORT_DIR/previous-step-analysis.json" \
  "$REPORT_DIR/vm121-research.txt" \
  "$REPORT_DIR/vm121-research.stderr" \
  "$VM121_RC" \
  "$REPORT_DIR/assessment.json" <<'PY'
import json
import sys
from pathlib import Path

(
    previous_path,
    stdout_path,
    stderr_path,
    vm121_rc,
    output_path,
) = sys.argv[1:]

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

stdout = Path(stdout_path).read_text(
    encoding="utf-8",
    errors="replace",
)

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

facts = {}
errors = []
traces = []

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

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

    elif line.startswith("__ERROR__ "):
        errors.append(line)

    elif line.startswith("__TRACE__ "):
        traces.append(line)

checks = {
    "previous_vm121_was_only_failed_check":
        previous.get("previous_vm121_failed_only") is True,

    "previous_vm101_core_passed":
        previous.get("previous_vm101_core_passed") is True,

    "previous_rollback_proven":
        previous.get("previous_rollback_proven") is True,

    "vm121_ssh_and_script_ok":
        int(vm121_rc) == 0,

    "vm121_remote_completed":
        "__TRACE__ stage=complete" in traces,

    "vm121_remote_no_errors":
        not errors,

    "postgres_candidate_found":
        int(facts.get("postgres_candidate_count", "0")) >= 1,

    "postgres_container_identified":
        bool(facts.get("postgres_container")),

    "database_ping":
        facts.get("database_ping") == "1",

    "required_tables_exist":
        facts.get("table.peers.exists") == "true"
        and facts.get(
            "table.peer_egress_leases.exists"
        ) == "true"
        and facts.get("table.jobs.exists") == "true",

    "enabled_peers_five":
        facts.get("enabled_peers") == "5",

    "lease_rows_five":
        facts.get("lease_rows") == "5",

    "pending_jobs_zero":
        facts.get("pending_jobs") == "0",
}

all_ok = all(checks.values())

assessment = {
    "all_ok": all_ok,
    "decision": (
        "PASS_STEP_050I10_RESEARCH_EXISTING_VM121_DB_CHECK_CONTRACT"
        if all_ok
        else
        "STOP_STEP_050I10_RESEARCH_EXISTING_VM121_DB_CHECK_CONTRACT"
    ),
    "checks": checks,
    "failed_checks": [
        key
        for key, value in checks.items()
        if not value
    ],
    "classification": {
        "previous_vm121_failure":
            previous.get(
                "previous_vm121_failure_classification"
            ),
        "previous_vm121_stderr":
            previous.get("previous_vm121_stderr"),
        "current_vm121_stderr": stderr,
    },
    "discovered_contract": {
        "postgres_container":
            facts.get("postgres_container"),
        "postgres_user":
            facts.get("postgres_user"),
        "postgres_database":
            facts.get("postgres_database"),
        "enabled_peers":
            facts.get("enabled_peers"),
        "lease_rows":
            facts.get("lease_rows"),
        "pending_jobs":
            facts.get("pending_jobs"),
        "related_tool_count":
            facts.get("related_tool_count"),
    },
    "next_step": (
        "STEP_050I11_REUSE_STEP050I9_VM101_"
        "ENABLE_COMMIT_WITH_PROVEN_VM121_CHECK"
    ),
}

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

ALL_OK="$(
  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("true" if data["all_ok"] else "false")
PY
)"

DECISION="$(
  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(data["decision"])
PY
)"

ROOT_CAUSE="$(
  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(
    data["classification"].get(
        "previous_vm121_failure",
        "UNKNOWN",
    )
)
PY
)"

POSTGRES_CONTAINER="$(
  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(
    data["discovered_contract"].get(
        "postgres_container",
        "",
    )
)
PY
)"

stage "05/07" "Переиздаю локальный архитектурный план"

PLAN_SLUG="${TS}_local_architecture_plan_hmn_egress_recovery_step050i10"
PLAN_DIR="${PUBROOT}/${PLAN_SLUG}"

ARCHITECTURE_PLAN="${PUBLIC_BASE}/${PLAN_SLUG}/"

mkdir -p "$PLAN_DIR/sources"

cp -a \
  "$PREVIOUS_PLAN_DIR/plan.md" \
  "$PLAN_DIR/previous-plan.md"

cp -a \
  "$REPORT_DIR/assessment.json" \
  "$PLAN_DIR/sources/step050i10-assessment.json"

cat > "$PLAN_DIR/plan.md" <<EOF
# Локальный архитектурный план — после STEP_050I10

## Зафиксировано

- [x] STEP_050I9 VM101 core checks прошли.
- [x] STEP_050I9 завершился STOP только из-за VM121 check.
- [x] STEP_050I9 rollback восстановил VM101 config.
- [x] STEP_050I10 выполнен в read-only режиме.
- [x] Изучены точные STEP_050I9 step.sh и vm101.sh.
- [x] Изучены предыдущие успешные способы VM121 DB crosscheck.
- [x] PostgreSQL container обнаружен динамически:
      ${POSTGRES_CONTAINER}
- [x] Проверены таблицы peers, peer_egress_leases и jobs.
- [x] Проверены counts: enabled peers=5, leases=5, pending=0.
- [x] Классификация предыдущего VM121 failure:
      ${ROOT_CAUSE}
- [x] VM100, VM101 и VM121 не изменялись.

## Следующий шаг

- [ ] STEP_050I11_REUSE_STEP050I9_VM101_ENABLE_COMMIT_WITH_PROVEN_VM121_CHECK

В STEP_050I11 необходимо:

1. Переиспользовать опубликованный STEP_050I9 vm101-source.sh.
2. Не вводить новый endpoint parser.
3. Использовать найденный STEP_050I10 VM121 DB contract.
4. Сохранить step.sh и все remote scripts до выполнения.
5. Оставить refresh, rebalance apply и direct failopen выключенными.

CURRENT REPORT:
${TRYCF_REPORT}

LOCAL ARCHITECTURE PLAN:
${ARCHITECTURE_PLAN}

XS MAP:
${XS_MAP}

GLOBAL PROJECT PLAN:
${GLOBAL_PROJECT_PLAN}
EOF

cat > "$PLAN_DIR/index.html" <<EOF
<!doctype html>
<meta charset="utf-8">
<title>Local architecture plan after STEP_050I10</title>
<h1>Local architecture plan after STEP_050I10</h1>
<p><a href="plan.md">plan.md</a></p>
<p><a href="${TRYCF_REPORT}">Current report</a></p>
<p><a href="${ARCHITECTURE_PLAN}">Architecture plan</a></p>
<p><a href="${XS_MAP}">XS Map</a></p>
<p><a href="${GLOBAL_PROJECT_PLAN}">Global project plan</a></p>
EOF

stage "06/07" "Публикую итоговый report и facts"

if [ "$ALL_OK" = true ]; then
  cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=${DECISION}
all_ok=true
mode=READ_ONLY_EXISTING_TOOL_RESEARCH
production_modified=false
vm100_modified=false
vm101_modified=false
vm121_modified=false
previous_step050i9_vm101_core_passed=true
previous_step050i9_vm121_failed_only=true
previous_step050i9_rollback_proven=true
previous_vm121_failure_classification=${ROOT_CAUSE}
postgres_container=${POSTGRES_CONTAINER}
vm121_enabled_peers=5
vm121_lease_rows=5
vm121_pending_jobs=0
next_step=STEP_050I11_REUSE_STEP050I9_VM101_ENABLE_COMMIT_WITH_PROVEN_VM121_CHECK

artifacts:
  step_script=step.sh
  vm121_script=vm121.sh
  assessment=assessment.json
  previous_analysis=previous-step-analysis.json
  prior_methods=prior-vm121-methods.txt
  vm121_research=vm121-research.txt
  sources=sources/

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
else
  cat > "$REPORT_DIR/report.txt" <<EOF
=== ${STEP} RESULT ===
step=${STEP}
decision=${DECISION}
all_ok=false
mode=READ_ONLY_EXISTING_TOOL_RESEARCH
production_modified=false
vm100_modified=false
vm101_modified=false
vm121_modified=false
previous_vm121_failure_classification=${ROOT_CAUSE}
assessment=assessment.json

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
fi

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

(
    assessment_path,
    step,
    timestamp,
    previous_report,
    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)

print(json.dumps({
    "schema": "router-step-facts-v1",
    "step": step,
    "generated_at_utc": timestamp,
    "assessment": assessment,
    "mode": "READ_ONLY_EXISTING_TOOL_RESEARCH",
    "safety": {
        "production_modified": False,
        "vm100_modified": False,
        "vm101_modified": False,
        "vm121_modified": False,
        "refresh_ran": False,
        "rebalance_apply_ran": False,
        "direct_failopen_enabled": False,
    },
    "source": {
        "previous_step050i9": previous_report,
    },
    "artifacts": {
        "step_script": "step.sh",
        "vm121_script": "vm121.sh",
        "previous_analysis":
            "previous-step-analysis.json",
        "prior_methods":
            "prior-vm121-methods.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>
<meta charset="utf-8">
<title>${STEP}</title>
<h1>${STEP}</h1>

<h2>Исполнявшиеся скрипты</h2>
<ul>
<li><a href="step.sh">step.sh</a></li>
<li><a href="vm121.sh">vm121.sh</a></li>
</ul>

<h2>Результаты</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="previous-step-analysis.json">previous-step-analysis.json</a></li>
<li><a href="prior-vm121-methods.txt">prior-vm121-methods.txt</a></li>
<li><a href="vm121-research.txt">vm121-research.txt</a></li>
<li><a href="vm121-research.stderr">vm121-research.stderr</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>
EOF

sha256sum \
  "$REPORT_DIR/step.sh" \
  "$REPORT_DIR/vm121.sh" \
  "$REPORT_DIR/sources/"* \
  > "$REPORT_DIR/SHA256SUMS"

stage "07/07" "Обновляю current pointers и завершаю"

cat > "$STATE/current-project-links.env" <<EOF
UPDATED_AT_UTC=${TS}
SOURCE_STEP=${STEP}
CURRENT_REPORT=${TRYCF_REPORT}
ARCHITECTURE_PLAN=${ARCHITECTURE_PLAN}
XS_MAP=${XS_MAP}
GLOBAL_PROJECT_PLAN=${GLOBAL_PROJECT_PLAN}
EOF

printf '%s\n' \
  "$ARCHITECTURE_PLAN" \
  > "$STATE/current-local-architecture-plan-url.txt"

trap - ERR

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

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

print_links

[ "$ALL_OK" = true ]
