#!/usr/bin/env bash
# quote-render-pdf-conformance — PostToolUse gate on browser-pdf-save.
#
# The companion to the PreToolUse quote-render-gate: that gate checks the
# rendered HTML on the deterministic path (via the render receipt) before the
# PDF is printed; the HTML the tool gated cannot yet be a PDF at PreToolUse, so
# the two PDF-only conformance checks run here, once the PDF exists.
#
# For a gated client-quote PDF (memory/users/<phone>/documents/quote-<jobId>.pdf
# or ...-breakdown.pdf) whose render receipt names the source HTML, it asserts:
#   1. the PDF's /URI annotation count >= the source HTML's absolute-link count
#      (a print that dropped links — a rasterised or flattened copy — carries
#      fewer /URI than the HTML had anchors);
#   2. every embedded https:// anchor in the source HTML answers 200 (a pending
#      redeploy that would 404 the Sign-Online link is caught before the PDF is
#      sent).
# The /URI-count check is offline and runs first; the network fetch runs only
# when the count check passes, so a link-loss regression never depends on the
# network being up.
#
# Exit codes: 0 = allow, 2 = block (stderr shown to the agent). Fail OPEN on any
# uninspectable call (tty / empty stdin / no python3 / no receipt / PDF not
# written): the no-receipt case is already the PreToolUse gate's to block, and
# browser-pdf-save is general-purpose, so failing open here bricks no unrelated
# PDF. No task numbers or internal refs in any operator-visible string.

set -uo pipefail

if [ -t 0 ]; then exit 0; fi
INPUT=$(cat)
[ -z "$INPUT" ] && exit 0

RESULT=$(INPUT="$INPUT" BASE="$PWD" python3 - <<'PY' 2>/dev/null || true
import os, json, re, sys, urllib.request

try:
    d = json.loads(os.environ["INPUT"])
except Exception:
    sys.exit(0)  # uninspectable envelope -> fail open

tool = d.get("tool_name", "") or ""
if not tool.endswith("browser-pdf-save"):
    sys.exit(0)
path = (d.get("tool_input", {}) or {}).get("path", "") or ""
if not path:
    sys.exit(0)
norm = os.path.normpath(path)
m = re.search(r'(?:^|/)memory/users/[^/]+/documents/quote-(.+?)(-breakdown)?\.pdf$', norm)
if not m:
    sys.exit(0)  # not a gated client-quote artefact
jobid = m.group(1)
if "/" in jobid or ".." in jobid:
    sys.exit(0)  # the PreToolUse gate owns bad-jobid; here fail open
variant = m.group(2)  # "-breakdown" or None

base = os.environ["BASE"]
receipt_path = os.path.join(base, "quoting", "jobs", jobid, "render-receipt.json")
try:
    receipt = json.load(open(receipt_path))
except Exception:
    sys.exit(0)  # no receipt -> PreToolUse gate already blocks; fail open

filed = receipt.get("filed", {}) or {}
html_path = filed.get("breakdown" if variant else "quote", "")
if not html_path or not os.path.isfile(html_path) or not os.path.isfile(path):
    sys.exit(0)  # nothing to compare against -> fail open

html = open(html_path, encoding="utf-8", errors="replace").read()
# Anchors that become a /URI annotation in the printed PDF: absolute http(s) or
# mailto hrefs. Fragment and relative anchors do not, so they are not counted.
uri_anchors = re.findall(r'href="((?:https?|mailto):[^"]*)"', html, re.I)
https_anchors = [u for u in uri_anchors if u.lower().startswith("https:")]

# /URI occurrences in the printed PDF. This is the same primitive the e-sign
# stamp gate uses (grep -a -c '/URI'); on real browser-pdf-save output it was
# measured to carry one greppable /URI per surviving link (base PDFs counted 4
# to 6, one per anchor), so the count is a faithful per-link tally, not stream-
# compressed away. A rasterised or flattened reprint drops to a lower count.
uri_count = open(path, "rb").read().count(b"/URI")
if uri_count < len(uri_anchors):
    print("BLOCK|%s|uri-count pdf=%d html_links=%d" % (jobid, uri_count, len(uri_anchors)))
    sys.exit(0)

for u in https_anchors:
    try:
        with urllib.request.urlopen(u, timeout=6) as r:
            code = r.status
    except Exception as e:
        code = getattr(e, "code", 0) or 0
    if code != 200:
        print("BLOCK|%s|link %s -> %s" % (jobid, u, code))
        sys.exit(0)

sys.exit(0)
PY
)

case "$RESULT" in
  BLOCK\|*)
    rest=${RESULT#BLOCK|}
    jobid=${rest%%|*}
    detail=${rest#*|}
    echo "[quote-render] op=bypass jobId=$jobid reason=pdf-link-conformance detail=$detail" >&2
    echo "Blocked: the printed quote PDF is missing links the document declared, or an embedded link is not live ($detail). Do not send it. If a Sign-Online link is not live yet, deploy the signing page first; then re-render and re-print so the PDF carries every link the document declares." >&2
    exit 2
    ;;
esac
exit 0
