#!/usr/bin/env python3
"""Standalone evidence-chain verifier for the Regulatory Change Radar.

Re-checks a client's public evidence chain over HTTP, without trusting the vendor:
  - fetches the advertised Ed25519 public key,
  - fetches the chain records,
  - recomputes the canonical SHA-256 hash of every record,
  - recomputes every link hash (chain-linkage integrity),
  - verifies every Ed25519 signature against the advertised public key,
  - checks chain continuity (sequence + previous-hash).

It states facts only (yes/no per check). It does NOT certify legal compliance and
is NOT legal advice. No account and no secrets are required.

Usage:
    python verifier.py https://radar.rupestelis.com KZ-FINTECH-001

Requires: Python 3.9+ and the 'cryptography' package (pip install cryptography).

Canonicalization (must match the server byte-for-byte):
    deterministic JSON = json.dumps(obj, sort_keys=True, ensure_ascii=False,
                                    separators=(",", ":")) encoded as UTF-8.
"""

import hashlib
import json
import sys
import urllib.request


def canonical_bytes(obj):
    return json.dumps(
        obj, sort_keys=True, ensure_ascii=False, separators=(",", ":")
    ).encode("utf-8")


def compute_link_hash(client_id, sequence, alert_id, alert_content_sha256, prev_link_hash):
    return hashlib.sha256(canonical_bytes({
        "client_id": client_id,
        "sequence": sequence,
        "alert_id": alert_id,
        "alert_content_sha256": alert_content_sha256,
        "prev_link_hash": prev_link_hash,
    })).hexdigest()


def _get(url):
    with urllib.request.urlopen(url, timeout=20) as resp:
        return json.loads(resp.read().decode("utf-8"))


def verify_records(public_key, records):
    """Pure verification of already-fetched records against a loaded Ed25519
    public key. Returns a dict of per-check booleans + overall ``valid``.
    Fail-closed: an empty / None chain or any bad record yields valid=False."""
    records = records or []
    n = len(records)
    linkage = hashes = sigs = (n > 0)
    prev = None
    expected_seq = 1
    tip = None
    for rec in sorted(records, key=lambda r: r.get("index", 0)):
        payload = rec.get("canonical_payload", {})

        if hashlib.sha256(canonical_bytes(payload)).hexdigest() != rec.get("record_hash"):
            hashes = False
        lh = compute_link_hash(
            payload.get("client_id"), payload.get("sequence"),
            payload.get("alert_id"), payload.get("alert_content_sha256"),
            payload.get("prev_link_hash"),
        )
        if lh != payload.get("link_hash"):
            hashes = False
        if payload.get("sequence") != expected_seq or payload.get("prev_link_hash") != prev:
            linkage = False
        try:
            public_key.verify(bytes.fromhex(rec.get("signature", "")),
                              canonical_bytes(payload))
        except Exception:
            sigs = False

        prev = payload.get("link_hash")
        tip = prev
        expected_seq += 1

    return {
        "records": n,
        "chain_linkage": linkage,
        "canonical_hashes": hashes,
        "ed25519_signatures": sigs,
        "chain_tip": tip,
        "valid": bool(n) and linkage and hashes and sigs,
    }


def main(argv):
    if len(argv) < 2:
        print("usage: python verifier.py <base_url> <client_id>")
        return 2
    base = argv[0].rstrip("/")
    client = argv[1]
    try:
        from cryptography.hazmat.primitives.serialization import load_pem_public_key
    except ImportError:
        print("ERROR: this verifier needs the 'cryptography' package "
              "(pip install cryptography)")
        return 2

    pk = _get(base + "/api/v1/public-key")
    public_key = load_pem_public_key(pk["public_key_pem"].encode("ascii"))
    chain = _get(base + "/api/v1/chain/" + client)
    res = verify_records(public_key, chain.get("records", []))

    print("Client: " + client)
    print("Records: " + str(res["records"]))
    print("Chain linkage: " + ("OK" if res["chain_linkage"] else "FAIL"))
    print("Canonical hashes: " + ("OK" if res["canonical_hashes"] else "FAIL"))
    print("Ed25519 signatures: " + ("OK" if res["ed25519_signatures"] else "FAIL"))
    print("Chain tip (link_hash): " + str(res.get("chain_tip")))
    print("Result: " + ("VALID" if res["valid"] else "INVALID"))
    print("Note: verifies evidence-chain integrity only; "
          "not legal compliance, not legal advice. To detect a silently "
          "truncated tail, pin the chain tip/length out-of-band.")
    return 0 if res["valid"] else 1


if __name__ == "__main__":
    raise SystemExit(main(sys.argv[1:]))
