#!/usr/bin/env python3
"""Real WASM benchmark for barcode-label-generator.

Uses Node.js (stdlib subprocess) to drive the barcode_wasm.js ES module
which loads barcode_wasm_bg.wasm directly — no browser needed.
Output is real timing data from the actual WASM engine.
"""
import json
import os
import subprocess
import sys

APP_DIR = os.path.dirname(os.path.abspath(__file__))
BENCH_JS = os.path.join(APP_DIR, "_bench.js")

BENCH_SCRIPT = r"""
import { initSync, generate_barcode } from './rust/pkg/barcode_wasm.js';
import { readFileSync } from 'fs';

const wasmBytes = readFileSync(new URL('./rust/pkg/barcode_wasm_bg.wasm', import.meta.url));
initSync(wasmBytes);

const samples = [
  { type: 'CODE128', data: 'Hello123' },
  { type: 'CODE128', data: 'PRODUCT-2026-0001' },
  { type: 'EAN13', data: '123456789012' },
  { type: 'UPCA', data: '12345678901' },
  { type: 'CODE39', data: 'ABC-123' },
];

// Warm-up
for (const s of samples) { generate_barcode(s.type, s.data); }

const N = 1000;
const measured = {};
for (const s of samples) {
  const key = s.type + ':' + s.data;
  const start = performance.now();
  for (let i = 0; i < N; i++) { generate_barcode(s.type, s.data); }
  const ms = performance.now() - start;
  measured[key] = { ms, per_call_us: (ms / N) * 1e3, n: N };
}

const batchStart = performance.now();
for (let i = 0; i < 100; i++) {
  generate_barcode('CODE128', 'Product-' + String(i).padStart(4, '0'));
}
const batchMs = performance.now() - batchStart;

console.log(JSON.stringify({
  measured,
  batch_100_ms: batchMs,
  batch_per_call_us: (batchMs / 100) * 1e3,
}));
"""


def find_node():
    """Find node binary."""
    for path in ["/usr/bin/node", "/run/current-system/sw/bin/node"]:
        if os.path.isfile(path):
            return path
    result = subprocess.run(["which", "node"], capture_output=True, text=True)
    if result.returncode == 0:
        return result.stdout.strip()
    return None


def run_benchmark():
    """Run WASM benchmark via Node.js, return results dict."""
    node = find_node()
    if not node:
        print("ERROR: node not found", file=sys.stderr)
        sys.exit(1)

    with open(BENCH_JS, "w") as f:
        f.write(BENCH_SCRIPT)

    try:
        result = subprocess.run(
            [node, BENCH_JS],
            capture_output=True, text=True, timeout=30,
            cwd=APP_DIR,
        )
        if result.returncode != 0:
            print(f"Node benchmark failed: {result.stderr}", file=sys.stderr)
            sys.exit(1)
        return json.loads(result.stdout.strip())
    finally:
        os.remove(BENCH_JS)


def print_results(results):
    """Print human-readable benchmark results."""
    print("=== Barcode Label Generator — Real WASM Benchmark ===\n")
    print("Single-call benchmarks (1000 iterations each):")
    for key, m in results["measured"].items():
        print(f"  {key:30s}  {m['ms']:8.2f} ms total  ({m['per_call_us']:7.2f} µs/call)")
    print(f"\nBatch: 100 Code 128 barcodes in one loop")
    print(f"  Total: {results['batch_100_ms']:.2f} ms")
    print(f"  Per call: {results['batch_per_call_us']:.2f} µs")
    print(f"\nRaw JSON: {json.dumps(results)}")


def update_index_html(results):
    """Update index.html with real benchmark numbers."""
    idx_path = os.path.join(APP_DIR, "index.html")
    with open(idx_path) as f:
        content = f.read()

    batch_ms = results["batch_100_ms"]
    if batch_ms < 100:
        claim = f"under {int(batch_ms) + 1}ms"
    else:
        claim = f"{batch_ms:.1f}ms"

    old = ("Benchmarked on a standard laptop (2024): generating 100 Code 128 "
           "barcodes through the client-side engine takes under 50ms — that is "
           "roughly 0.5 milliseconds per barcode. The entire engine runs in "
           "your browser after the first load; no server round-trips, no "
           "uploads, no waiting.")
    new = (f"Benchmarked on a standard laptop (2024): generating 100 Code 128 "
           f"barcodes through the client-side engine takes {claim} (real "
           f"measurement via Node.js + WASM — see benchmark.py). The entire "
           f"engine runs in your browser after the first load; no server "
           f"round-trips, no uploads, no waiting.")

    if old not in content:
        print(f"WARNING: could not find old claim text in index.html, skipping update",
              file=sys.stderr)
        return

    content = content.replace(old, new)
    with open(idx_path, "w") as f:
        f.write(content)
    print(f"\nUpdated index.html with real claim: {claim}")


if __name__ == "__main__":
    results = run_benchmark()
    print_results(results)
