#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
Usage:
  ./scripts/cir/run_cir_replay_routinator.sh \
    --cir <path> \
    --repo-bytes-db <path> \
    --out-dir <path> \
    --reference-ccr <path> \
    [--keep-db] \
    [--routinator-root <path>] \
    [--routinator-bin <path>] \
    [--real-rsync-bin <path>]
EOF
}

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
RPKI_DEV_ROOT="${RPKI_DEV_ROOT:-$ROOT_DIR}"

CIR=""
REPO_BYTES_DB=""
OUT_DIR=""
REFERENCE_CCR=""
KEEP_DB=0
ROUTINATOR_ROOT="${ROUTINATOR_ROOT:-/home/yuyr/dev/rust_playground/routinator}"
ROUTINATOR_BIN="${ROUTINATOR_BIN:-$ROUTINATOR_ROOT/target/debug/routinator}"
REAL_RSYNC_BIN="${REAL_RSYNC_BIN:-/usr/bin/rsync}"
CIR_MATERIALIZE_BIN="${CIR_MATERIALIZE_BIN:-$ROOT_DIR/target/release/cir_materialize}"
CIR_EXTRACT_INPUTS_BIN="${CIR_EXTRACT_INPUTS_BIN:-$ROOT_DIR/target/release/cir_extract_inputs}"
CCR_TO_COMPARE_VIEWS_BIN="${CCR_TO_COMPARE_VIEWS_BIN:-$ROOT_DIR/target/release/ccr_to_compare_views}"
WRAPPER="$ROOT_DIR/scripts/cir/cir-rsync-wrapper"
JSON_TO_VAPS="$ROOT_DIR/scripts/cir/json_to_vaps_csv.py"
FAKETIME_LIB="${FAKETIME_LIB:-$ROOT_DIR/target/tools/faketime_pkg/extracted/libfaketime/usr/lib/x86_64-linux-gnu/faketime/libfaketime.so.1}"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --cir) CIR="$2"; shift 2 ;;
    --repo-bytes-db) REPO_BYTES_DB="$2"; shift 2 ;;
    --out-dir) OUT_DIR="$2"; shift 2 ;;
    --reference-ccr) REFERENCE_CCR="$2"; shift 2 ;;
    --keep-db) KEEP_DB=1; shift ;;
    --routinator-root) ROUTINATOR_ROOT="$2"; shift 2 ;;
    --routinator-bin) ROUTINATOR_BIN="$2"; shift 2 ;;
    --real-rsync-bin) REAL_RSYNC_BIN="$2"; shift 2 ;;
    -h|--help) usage; exit 0 ;;
    *) echo "unknown argument: $1" >&2; usage; exit 2 ;;
  esac
done

[[ -n "$CIR" && -n "$REPO_BYTES_DB" && -n "$OUT_DIR" && -n "$REFERENCE_CCR" ]] || {
  usage >&2
  exit 2
}
if [[ ! -x "$ROUTINATOR_BIN" ]]; then
  echo "routinator binary not executable: $ROUTINATOR_BIN" >&2
  exit 2
fi

mkdir -p "$OUT_DIR"
if [[ ! -x "$CIR_MATERIALIZE_BIN" || ! -x "$CIR_EXTRACT_INPUTS_BIN" || ! -x "$CCR_TO_COMPARE_VIEWS_BIN" ]]; then
  (
    cd "$ROOT_DIR"
    cargo build --release --bin cir_materialize --bin cir_extract_inputs --bin ccr_to_compare_views
  )
fi

TMP_ROOT="$OUT_DIR/.tmp"
TALS_DIR="$TMP_ROOT/tals"
META_JSON="$TMP_ROOT/meta.json"
MIRROR_ROOT="$TMP_ROOT/mirror"
WORK_REPO="$TMP_ROOT/repository"
RUN_LOG="$OUT_DIR/routinator.log"
ACTUAL_VRPS="$OUT_DIR/actual-vrps.csv"
ACTUAL_VAPS_JSON="$OUT_DIR/actual-vaps.json"
ACTUAL_VAPS="$OUT_DIR/actual-vaps.csv"
REF_VRPS="$OUT_DIR/reference-vrps.csv"
REF_VAPS="$OUT_DIR/reference-vaps.csv"
SUMMARY_JSON="$OUT_DIR/compare-summary.json"

rm -rf "$TMP_ROOT"
mkdir -p "$TMP_ROOT"

"$CIR_EXTRACT_INPUTS_BIN" --cir "$CIR" --tals-dir "$TALS_DIR" --meta-json "$META_JSON"
python3 - <<'PY' "$TALS_DIR"
from pathlib import Path
import sys
for tal in Path(sys.argv[1]).glob("*.tal"):
    lines = tal.read_text(encoding="utf-8").splitlines()
    rsync_uris = [line for line in lines if line.startswith("rsync://")]
    base64_lines = []
    seen_sep = False
    for line in lines:
        if seen_sep:
            if line.strip():
                base64_lines.append(line)
        elif line.strip() == "":
            seen_sep = True
    tal.write_text("\n".join(rsync_uris) + "\n\n" + "\n".join(base64_lines) + "\n", encoding="utf-8")
PY
materialize_cmd=("$CIR_MATERIALIZE_BIN" --cir "$CIR" --repo-bytes-db "$REPO_BYTES_DB" --mirror-root "$MIRROR_ROOT")
if [[ "$KEEP_DB" -eq 1 ]]; then
  materialize_cmd+=(--keep-db)
fi
"${materialize_cmd[@]}"

VALIDATION_TIME="$(python3 - <<'PY' "$META_JSON"
import json,sys
print(json.load(open(sys.argv[1]))["validationTime"])
PY
)"
mapfile -t TAL_PATHS < <(python3 - <<'PY' "$META_JSON"
import json, sys
for item in json.load(open(sys.argv[1], encoding="utf-8"))["talFiles"]:
    print(item["path"])
PY
)
COMPARE_TRUST_ANCHOR="unknown"
FAKE_EPOCH="$(python3 - <<'PY' "$VALIDATION_TIME"
from datetime import datetime, timezone
import sys
dt = datetime.fromisoformat(sys.argv[1].replace("Z", "+00:00")).astimezone(timezone.utc)
print(int(dt.timestamp()))
PY
)"

export CIR_MIRROR_ROOT="$(python3 - <<'PY' "$MIRROR_ROOT"
from pathlib import Path
import sys
print(Path(sys.argv[1]).resolve())
PY
)"
export REAL_RSYNC_BIN="$REAL_RSYNC_BIN"
export CIR_LOCAL_LINK_MODE=1
env \
  LD_PRELOAD="$FAKETIME_LIB" \
  FAKETIME_FMT=%s \
  FAKETIME="$FAKE_EPOCH" \
  FAKETIME_DONT_FAKE_MONOTONIC=1 \
  "$ROUTINATOR_BIN" \
  --repository-dir "$WORK_REPO" \
  --disable-rrdp \
  --rsync-command "$WRAPPER" \
  --no-rir-tals \
  --extra-tals-dir "$TALS_DIR" \
  --enable-aspa \
  update --complete >"$RUN_LOG" 2>&1 || true

env \
  LD_PRELOAD="$FAKETIME_LIB" \
  FAKETIME_FMT=%s \
  FAKETIME="$FAKE_EPOCH" \
  FAKETIME_DONT_FAKE_MONOTONIC=1 \
  "$ROUTINATOR_BIN" \
  --repository-dir "$WORK_REPO" \
  --disable-rrdp \
  --rsync-command "$WRAPPER" \
  --no-rir-tals \
  --extra-tals-dir "$TALS_DIR" \
  --enable-aspa \
  vrps --noupdate -o "$ACTUAL_VRPS" >>"$RUN_LOG" 2>&1

env \
  LD_PRELOAD="$FAKETIME_LIB" \
  FAKETIME_FMT=%s \
  FAKETIME="$FAKE_EPOCH" \
  FAKETIME_DONT_FAKE_MONOTONIC=1 \
  "$ROUTINATOR_BIN" \
  --repository-dir "$WORK_REPO" \
  --disable-rrdp \
  --rsync-command "$WRAPPER" \
  --no-rir-tals \
  --extra-tals-dir "$TALS_DIR" \
  --enable-aspa \
  vrps --noupdate --format json -o "$ACTUAL_VAPS_JSON" >>"$RUN_LOG" 2>&1

python3 "$JSON_TO_VAPS" --input "$ACTUAL_VAPS_JSON" --csv-out "$ACTUAL_VAPS"

normalize_trust_anchor_csv() {
  python3 - <<'PY' "$1" "$2"
import csv
import sys
from pathlib import Path

path = Path(sys.argv[1])
trust_anchor = sys.argv[2]
rows = list(csv.reader(path.open(newline="", encoding="utf-8")))
if rows:
    for row in rows[1:]:
        if row:
            row[-1] = trust_anchor
with path.open("w", newline="", encoding="utf-8") as fh:
    csv.writer(fh).writerows(rows)
PY
}

normalize_trust_anchor_csv "$ACTUAL_VRPS" "$COMPARE_TRUST_ANCHOR"
normalize_trust_anchor_csv "$ACTUAL_VAPS" "$COMPARE_TRUST_ANCHOR"
"$CCR_TO_COMPARE_VIEWS_BIN" --ccr "$REFERENCE_CCR" --vrps-out "$REF_VRPS" --vaps-out "$REF_VAPS" --trust-anchor "$COMPARE_TRUST_ANCHOR"

python3 - <<'PY' "$ACTUAL_VRPS" "$REF_VRPS" "$ACTUAL_VAPS" "$REF_VAPS" "$SUMMARY_JSON" "$META_JSON"
import csv, json, sys
def rows(path):
    with open(path, newline="") as f:
        return list(csv.reader(f))[1:]
actual_vrps = {tuple(r) for r in rows(sys.argv[1])}
ref_vrps = {tuple(r) for r in rows(sys.argv[2])}
actual_vaps = {tuple(r) for r in rows(sys.argv[3])}
ref_vaps = {tuple(r) for r in rows(sys.argv[4])}
meta = json.load(open(sys.argv[6], encoding="utf-8"))
summary = {
  "compareMode": "trust-anchor-agnostic",
  "talCount": len(meta["talFiles"]),
  "talPaths": [item["path"] for item in meta["talFiles"]],
  "vrps": {
    "actual": len(actual_vrps),
    "reference": len(ref_vrps),
    "match": actual_vrps == ref_vrps,
    "only_in_actual": sorted(actual_vrps - ref_vrps)[:20],
    "only_in_reference": sorted(ref_vrps - actual_vrps)[:20],
  },
  "vaps": {
    "actual": len(actual_vaps),
    "reference": len(ref_vaps),
    "match": actual_vaps == ref_vaps,
    "only_in_actual": sorted(actual_vaps - ref_vaps)[:20],
    "only_in_reference": sorted(ref_vaps - actual_vaps)[:20],
  }
}
with open(sys.argv[5], "w") as f:
    json.dump(summary, f, indent=2)
PY

if [[ "$KEEP_DB" -ne 1 ]]; then
  rm -rf "$TMP_ROOT"
fi

echo "done: $OUT_DIR"
