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

usage() {
  cat <<'USAGE'
Usage:
  ./scripts/periodic/compare_ccr_cir_round.sh \
    --ours-ccr <path> \
    --rpki-client-ccr <path> \
    --out-dir <path> \
    [--ours-cir <path>] \
    [--rpki-client-cir <path>] \
    [--trust-anchor <name>] \
    [--sample-limit <n>] \
    [--always-compare-cir]
USAGE
}

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
OURS_CCR=""
CLIENT_CCR=""
OURS_CIR=""
CLIENT_CIR=""
OUT_DIR=""
TRUST_ANCHOR="unknown"
SAMPLE_LIMIT="20"
ALWAYS_COMPARE_CIR=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    --ours-ccr) OURS_CCR="$2"; shift 2 ;;
    --rpki-client-ccr) CLIENT_CCR="$2"; shift 2 ;;
    --ours-cir) OURS_CIR="$2"; shift 2 ;;
    --rpki-client-cir) CLIENT_CIR="$2"; shift 2 ;;
    --out-dir) OUT_DIR="$2"; shift 2 ;;
    --trust-anchor) TRUST_ANCHOR="$2"; shift 2 ;;
    --sample-limit) SAMPLE_LIMIT="$2"; shift 2 ;;
    --always-compare-cir) ALWAYS_COMPARE_CIR=1; shift ;;
    -h|--help) usage; exit 0 ;;
    *) echo "unknown argument: $1" >&2; usage; exit 2 ;;
  esac
done

[[ -n "$OURS_CCR" && -n "$CLIENT_CCR" && -n "$OUT_DIR" ]] || { usage >&2; exit 2; }
if [[ -n "$OURS_CIR" || -n "$CLIENT_CIR" ]]; then
  [[ -n "$OURS_CIR" && -n "$CLIENT_CIR" ]] || { echo "--ours-cir and --rpki-client-cir must be provided together" >&2; exit 2; }
fi

mkdir -p "$OUT_DIR/ccr"

"$ROOT_DIR/scripts/periodic/compare_ccr_round.sh" \
  --ours-ccr "$OURS_CCR" \
  --rpki-client-ccr "$CLIENT_CCR" \
  --out-dir "$OUT_DIR/ccr" \
  --trust-anchor "$TRUST_ANCHOR" >/dev/null

RUN_CIR="$(python3 - <<'PY' "$OUT_DIR/ccr/compare-summary.json" "$ALWAYS_COMPARE_CIR" "$OURS_CIR" "$CLIENT_CIR"
import json
import sys
summary = json.load(open(sys.argv[1], "r", encoding="utf-8"))
always = sys.argv[2] == "1"
has_cir = bool(sys.argv[3]) and bool(sys.argv[4])
ccr_match = summary.get("stateDigestMatch") is True or summary.get("allMatch") is True
print("1" if has_cir and (always or not ccr_match) else "0")
PY
)"

if [[ "$RUN_CIR" == "1" ]]; then
  mkdir -p "$OUT_DIR/cir"
  "$ROOT_DIR/scripts/periodic/compare_cir_round.sh" \
    --ours-cir "$OURS_CIR" \
    --rpki-client-cir "$CLIENT_CIR" \
    --out-dir "$OUT_DIR/cir" \
    --sample-limit "$SAMPLE_LIMIT" >/dev/null
fi

python3 - <<'PY' \
  "$OUT_DIR/ccr/compare-summary.json" \
  "$OUT_DIR/cir/cir-compare-summary.json" \
  "$OUT_DIR/summary.json" \
  "$OUT_DIR/summary.md" \
  "$ALWAYS_COMPARE_CIR" \
  "$OURS_CIR" \
  "$CLIENT_CIR" \
  "$TRUST_ANCHOR" \
  "$SAMPLE_LIMIT"
import json
import sys
from pathlib import Path

ccr_path = Path(sys.argv[1])
cir_path = Path(sys.argv[2])
summary_json = Path(sys.argv[3])
summary_md = Path(sys.argv[4])
always_compare_cir = sys.argv[5] == "1"
ours_cir = sys.argv[6]
client_cir = sys.argv[7]
trust_anchor = sys.argv[8]
sample_limit = int(sys.argv[9])

ccr = json.load(open(ccr_path, "r", encoding="utf-8"))
ccr_match = ccr.get("stateDigestMatch") is True or ccr.get("allMatch") is True
cir_available = bool(ours_cir) and bool(client_cir)
cir_compared = cir_path.exists()
cir = json.load(open(cir_path, "r", encoding="utf-8")) if cir_compared else None

if ccr_match:
    if cir_compared and cir and cir.get("allMatch") is not True:
        diagnosis = "ccr_state_same_but_cir_process_diff"
    else:
        diagnosis = "ccr_state_digest_match"
else:
    if not cir_available:
        diagnosis = "ccr_mismatch_cir_not_available"
    elif not cir_compared:
        diagnosis = "ccr_mismatch_cir_compare_skipped"
    elif cir.get("trustAnchors", {}).get("match") is not True:
        diagnosis = "trust_anchor_input_difference"
    elif cir.get("objects", {}).get("match") is not True:
        diagnosis = "input_object_or_manifest_accepted_set_difference"
    elif (
        cir.get("rejects", {}).get("match") is not True
        or cir.get("rejectListSha256Match") is not True
    ):
        diagnosis = "validation_reject_policy_difference"
    else:
        diagnosis = "ccr_projection_sorting_encoding_or_non_cir_state_difference"

combined = {
    "allMatch": bool(ccr_match and (not cir_compared or (cir and cir.get("allMatch") is True))),
    "diagnosis": diagnosis,
    "comparePath": ccr.get("comparePath"),
    "stateDigestMatch": ccr_match,
    "mismatchedStates": ccr.get("mismatchedStates", []),
    "mismatchedComponents": ccr.get("mismatchedComponents", []),
    "vrps": ccr.get("vrps"),
    "vaps": ccr.get("vaps"),
    "trustAnchor": trust_anchor,
    "sampleLimit": sample_limit,
    "ccr": {
        "summaryPath": str(ccr_path),
        "stateDigestMatch": ccr_match,
        "comparePath": ccr.get("comparePath"),
        "mismatchedStates": ccr.get("mismatchedStates", []),
        "mismatchedComponents": ccr.get("mismatchedComponents", []),
        "vrpMatch": ccr.get("vrps", {}).get("match"),
        "vapMatch": ccr.get("vaps", {}).get("match"),
    },
    "cir": {
        "available": cir_available,
        "compared": cir_compared,
        "summaryPath": str(cir_path) if cir_compared else None,
        "comparePolicy": "always" if always_compare_cir else "on_ccr_mismatch",
        "allMatch": cir.get("allMatch") if cir else None,
        "objectsMatch": cir.get("objects", {}).get("match") if cir else None,
        "rejectsMatch": cir.get("rejects", {}).get("match") if cir else None,
        "trustAnchorsMatch": cir.get("trustAnchors", {}).get("match") if cir else None,
        "talsMatch": cir.get("trustAnchors", {}).get("match") if cir else None,
        "rejectListSha256Match": cir.get("rejectListSha256Match") if cir else None,
        "oursObjectCount": cir.get("ours", {}).get("objectCount") if cir else None,
        "rpkiClientObjectCount": cir.get("rpkiClient", {}).get("objectCount") if cir else None,
        "oursTrustAnchorCount": cir.get("ours", {}).get("trustAnchorCount") if cir else None,
        "rpkiClientTrustAnchorCount": cir.get("rpkiClient", {}).get("trustAnchorCount") if cir else None,
        "oursRejectCount": cir.get("ours", {}).get("rejectCount") if cir else None,
        "rpkiClientRejectCount": cir.get("rpkiClient", {}).get("rejectCount") if cir else None,
    },
}

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

lines = [
    "# CCR/CIR Compare Summary",
    "",
    f"- `allMatch`: `{str(combined['allMatch']).lower()}`",
    f"- `diagnosis`: `{combined['diagnosis']}`",
    f"- `ccrStateDigestMatch`: `{str(ccr_match).lower()}`",
    f"- `ccrMismatchedStates`: `{','.join(combined['ccr']['mismatchedStates'])}`",
    f"- `cirCompared`: `{str(cir_compared).lower()}`",
]
if cir:
    lines.extend([
        f"- `cirObjectsMatch`: `{str(combined['cir']['objectsMatch']).lower()}`",
        f"- `cirRejectsMatch`: `{str(combined['cir']['rejectsMatch']).lower()}`",
        f"- `cirTrustAnchorsMatch`: `{str(combined['cir']['trustAnchorsMatch']).lower()}`",
        f"- `cirCounts`: ours objects `{combined['cir']['oursObjectCount']}`, rpki-client objects `{combined['cir']['rpkiClientObjectCount']}`, ours trustAnchors `{combined['cir']['oursTrustAnchorCount']}`, rpki-client trustAnchors `{combined['cir']['rpkiClientTrustAnchorCount']}`, ours rejects `{combined['cir']['oursRejectCount']}`, rpki-client rejects `{combined['cir']['rpkiClientRejectCount']}`",
    ])
else:
    lines.append(f"- `cirSkippedReason`: `{'ccr matched' if ccr_match and not always_compare_cir else 'CIR inputs unavailable'}`")
summary_md.write_text("\n".join(lines) + "\n", encoding="utf-8")
print(summary_json)
PY
