#!/usr/bin/env python3
"""
Python benchmark matching src/test/benchmark.zig for all supported Aegis algorithms.

It performs two benchmarks with the same parameters as the Zig version:
- AEGIS encrypt (attached tag, maclen = MACBYTES)
- AEGIS MAC (clone state pattern)

Output format and throughput units mirror the Zig benchmark (Mb/s).
"""

import secrets
import time

from aeg import aegis128l, aegis128x2, aegis128x4, aegis256, aegis256x2, aegis256x4

MSG_LEN = 16384000  # 16 000 KiB
ITERATIONS = 100


def bench_encrypt(ciph) -> None:
    key = ciph.random_key()
    nonce = ciph.random_nonce()

    # Single buffer, as in Zig: c_out == m buffer, with tag appended
    maclen = ciph.MACBYTES
    buf = bytearray(MSG_LEN + maclen)
    # Initialize buffer with random data
    buf[:] = secrets.token_bytes(len(buf))

    mview = memoryview(buf)[:MSG_LEN]

    t0 = time.perf_counter()
    for _ in range(ITERATIONS):
        ciph.encrypt(key, nonce, mview, None, maclen=maclen, into=buf)
    t1 = time.perf_counter()

    # Prevent any unrealistic optimization assumptions
    _ = buf[0]

    bits = MSG_LEN * ITERATIONS * 8
    elapsed_s = t1 - t0
    throughput_mbps = (
        (bits / (elapsed_s * 1_000_000)) if elapsed_s > 0 else float("inf")
    )
    print(f"{ciph.NAME}\t{throughput_mbps:10.2f} Mb/s")


def bench_mac(ciph) -> None:
    key = ciph.random_key()
    nonce = ciph.random_nonce()

    buf = bytearray(MSG_LEN)
    buf[:] = secrets.token_bytes(len(buf))

    mac_out = bytearray(ciph.MACBYTES_LONG)

    t0 = time.perf_counter()
    for _ in range(ITERATIONS):
        ciph.mac(key, nonce, buf, maclen=ciph.MACBYTES_LONG, into=mac_out)
    t1 = time.perf_counter()

    _ = mac_out[0]

    bits = MSG_LEN * ITERATIONS * 8
    elapsed_s = t1 - t0
    throughput_mbps = (
        (bits / (elapsed_s * 1_000_000)) if elapsed_s > 0 else float("inf")
    )
    print(f"{ciph.NAME} MAC\t{throughput_mbps:10.2f} Mb/s")


if __name__ == "__main__":
    # aegis_init() is called in the loader at import time already
    # Run encrypt benchmarks in order: 256, 256x2, 256x4, 128l, 128x2, 128x4
    bench_encrypt(aegis256)
    bench_encrypt(aegis256x2)
    bench_encrypt(aegis256x4)
    bench_encrypt(aegis128l)
    bench_encrypt(aegis128x2)
    bench_encrypt(aegis128x4)

    # Run MAC benchmarks in order: 128l, 128x2, 128x4, 256, 256x2, 256x4
    bench_mac(aegis128l)
    bench_mac(aegis128x2)
    bench_mac(aegis128x4)
    bench_mac(aegis256)
    bench_mac(aegis256x2)
    bench_mac(aegis256x4)
