#!/usr/bin/env python3
"""tau-cli — thin client for the AuriGlyph Tau compression demo API.

This script does NOT contain the Tau compression engine. It sends public
blockchain transactions (raw JSON-RPC form) to the public demo API at
https://auriglyph.com and prints aggregate metrics only.

USAGE
  chmod +x tau-cli

  tau-cli batch <file.json>
      Send a batch of RPC-JSON transactions and get aggregate lossless ratio
      plus a byte-perfect round-trip verdict. Ratio grows with batch volume.

      Accepted JSON shapes:
        • an array of transaction objects:        [ {..tx..}, {..tx..} ]
        • a full block response:                  {"result": {"transactions": [ {..}, .. ]}}
        • the bare result object:                 {"transactions": [ {..}, .. ]}

EXAMPLES
  curl -sS https://ethereum.publicnode.com -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["latest",true]}' \
    > my_txs.json
  ./tau-cli batch my_txs.json
  ./tau-cli batch my_txs.json --endpoint https://auriglyph.com

PRIVACY NOTICE
  The batch command sends transaction data to the public demo server. The
  server returns only aggregate numbers (ratio, tx_count, round_trip_ok).
  It does not return compressed bytes, recovered bytes, logs, or engine internals.

  Use public blockchain data or synthetic test samples only — do not upload
  private or confidential transactions to the public demo endpoint.

Dependencies: none (Python 3 stdlib only).
"""
import argparse
import json
import os
import sys
import urllib.request
import urllib.error

DEFAULT_ENDPOINT = "https://auriglyph.com"
MAX_FILE_BYTES = 50 * 1024 * 1024   # 50 MB
MAX_TX_COUNT   = 200_000


def _die(msg):
    sys.stderr.write(f"error: {msg}\n")
    sys.exit(1)


def post(url, payload, timeout=300):
    data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        url,
        data=data,
        headers={
            "Content-Type": "application/json",
            "User-Agent": "tau-cli/2.1 (+https://auriglyph.com)",
            "Origin": "https://auriglyph.com",
            "Referer": "https://auriglyph.com/tau-demo/",
        },
        method="POST",
    )
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", "replace").strip()
        _die(f"HTTP {e.code}: {body}")
    except urllib.error.URLError as e:
        _die(f"request failed: {e.reason}")


def load_txs(path):
    """Load transactions from a JSON file in any common JSON-RPC shape."""
    ext = os.path.splitext(path)[1].lower()
    if ext != ".json":
        _die(
            "unsupported file type '{0}'. Use a .json file of transactions "
            "(an array of tx objects, or a full eth_getBlockByNumber response).".format(ext)
        )
    if os.path.getsize(path) > MAX_FILE_BYTES:
        _die("file too large (max {0} MB)".format(MAX_FILE_BYTES // 1024 // 1024))
    with open(path, "r", encoding="utf-8") as f:
        try:
            doc = json.load(f)
        except json.JSONDecodeError as e:
            _die("invalid JSON: {0}".format(e))

    txs = doc
    if isinstance(doc, dict):
        if "result" in doc and isinstance(doc["result"], dict):
            txs = doc["result"].get("transactions", [])
        elif "transactions" in doc:
            txs = doc["transactions"]
        else:
            txs = [doc]
    if not isinstance(txs, list) or not txs:
        _die("no transactions found. Expected an array of tx objects or a block response.")
    if len(txs) > MAX_TX_COUNT:
        _die("too many transactions ({0:,}, max {1:,})".format(len(txs), MAX_TX_COUNT))
    return txs


def cmd_batch(args):
    txs = load_txs(args.file)
    print("sending {0:,} transactions ...".format(len(txs)))
    out = post(args.endpoint.rstrip("/") + "/api/compress-batch", {"json": txs})
    if out.get("error"):
        _die("server: {0}".format(out["error"]))
    tx_count = out.get("tx_count")
    ratio = out.get("ratio")
    round_trip_ok = out.get("round_trip_ok")
    print("tx_count       : {0}".format(tx_count))
    if isinstance(ratio, (int, float)):
        print("ratio          : {0:.3f}x  lossless".format(ratio))
    else:
        print("ratio          : {0}".format(ratio))
    print("round_trip_ok  : {0}".format(round_trip_ok))
    if out.get("note"):
        print("note           : {0}".format(out["note"]))


def main():
    p = argparse.ArgumentParser(prog="tau-cli", description="AuriGlyph Tau compression demo client.")
    p.add_argument("--endpoint", default=DEFAULT_ENDPOINT, help="API base URL (default: {0})".format(DEFAULT_ENDPOINT))
    sub = p.add_subparsers(dest="command", required=True)
    b = sub.add_parser("batch", help="compress a batch of transactions from a .json file")
    b.add_argument("file", help="path to a .json file (array of tx objects or a block response)")
    b.set_defaults(func=cmd_batch)
    args = p.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()