// Honest gas report for AereThresholdAccount.executeThreshold. The MockPQCPrecompile returns
// almost immediately, so the MEASURED gas here is the account's own Solidity + calldata overhead
// (decode + distinct-bitmap + per-leg envelope assembly + staticcall dispatch). The REAL on-chain
// cost adds the live precompile's verify gas per leg (Falcon-512 ~40k, ML-DSA-44 ~55k, Falcon-1024
// ~75k, SLH-DSA-128s ~350k — from research/aip-draft-pqc-precompiles.md). We measure the marginal
// overhead per leg by differencing two thresholds, then project the real (scheme, t) cost.
const { ethers, network } = require("hardhat");

const PRECOMPILE = {
  1: ethers.getAddress("0x0000000000000000000000000000000000000ae1"),
  2: ethers.getAddress("0x0000000000000000000000000000000000000ae2"),
  3: ethers.getAddress("0x0000000000000000000000000000000000000ae3"),
  4: ethers.getAddress("0x0000000000000000000000000000000000000ae4"),
};
const META = { 1: { pkLen: 897, pkHeader: 0x09, esigHeader: 0x29, falcon: true } };
const REAL_PRECOMPILE_GAS = { 1: 40000, 2: 75000, 3: 55000, 4: 350000 };

function makeRng(seedText) {
  let seed = ethers.keccak256(ethers.toUtf8Bytes(seedText));
  return {
    bytes(n) {
      const out = new Uint8Array(n);
      let i = 0;
      while (i < n) {
        seed = ethers.keccak256(seed);
        const c = ethers.getBytes(seed);
        for (let j = 0; j < c.length && i < n; j++, i++) out[i] = c[j];
      }
      return out;
    },
  };
}
function makePubKey(rng) {
  const raw = rng.bytes(META[1].pkLen);
  raw[0] = META[1].pkHeader;
  return ethers.hexlify(raw);
}
function genuine(pubKey, message, rng) {
  const commitment = ethers.keccak256(ethers.concat([pubKey, message]));
  const nonce = rng.bytes(40);
  const esig = ethers.concat([new Uint8Array([META[1].esigHeader]), commitment]);
  return ethers.hexlify(ethers.concat([nonce, esig]));
}

describe("AereThresholdAccount — gas report (Falcon-512, n=8)", function () {
  let mockCode, ep, echo, pubKeys, rng, factory;
  const N = 8;

  before(async function () {
    mockCode = {};
    const F = await ethers.getContractFactory("MockPQCPrecompile");
    for (const s of [1, 2, 3, 4]) {
      const m = await F.deploy(s);
      await m.waitForDeployment();
      mockCode[s] = await ethers.provider.getCode(await m.getAddress());
    }
  });

  beforeEach(async function () {
    for (const s of [1, 2, 3, 4]) await network.provider.send("hardhat_setCode", [PRECOMPILE[s], mockCode[s]]);
    [ep] = await ethers.getSigners();
    rng = makeRng("gas-report");
    pubKeys = [];
    for (let i = 0; i < N; i++) pubKeys.push(makePubKey(rng));
    factory = await (await ethers.getContractFactory("AereThresholdAccountFactory")).deploy();
    await factory.waitForDeployment();
    echo = await (await ethers.getContractFactory("EchoTarget")).deploy();
    await echo.waitForDeployment();
  });

  async function deploy(t, salt) {
    const addr = await factory.computeAddress(ep.address, 1, t, pubKeys, salt);
    await (await factory.createAccount(ep.address, 1, t, pubKeys, salt)).wait();
    return ethers.getContractAt("AereThresholdAccount", addr);
  }
  async function execGas(account, t) {
    const target = await echo.getAddress();
    const data = echo.interface.encodeFunctionData("bump");
    const challenge = await account.execChallenge(0, target, 0, data);
    const legs = [];
    for (let i = 0; i < t; i++) legs.push([i, genuine(pubKeys[i], challenge, rng)]);
    const tx = await account.executeThreshold(target, 0, data, legs);
    const rc = await tx.wait();
    return Number(rc.gasUsed);
  }

  it("measures marginal overhead per leg and projects the real per-scheme cost", async function () {
    const g2 = await execGas(await deploy(2, ethers.id("g2")), 2);
    const g6 = await execGas(await deploy(6, ethers.id("g6")), 6);
    const perLegOverhead = Math.round((g6 - g2) / 4);
    const baseOverhead = g2 - 2 * perLegOverhead;

    console.log(`\n      executeThreshold measured gas (mock precompile ~free):`);
    console.log(`        t=2: ${g2} gas   t=6: ${g6} gas`);
    console.log(`        marginal Solidity+calldata overhead per leg: ~${perLegOverhead} gas`);
    console.log(`        fixed base overhead: ~${baseOverhead} gas`);
    console.log(`      projected REAL cost = base + t*(overhead + precompile_verify):`);
    for (const s of [1, 2, 3, 4]) {
      const name = { 1: "Falcon-512", 2: "Falcon-1024", 3: "ML-DSA-44", 4: "SLH-DSA-128s" }[s];
      const perLegReal = perLegOverhead + REAL_PRECOMPILE_GAS[s];
      const t3 = baseOverhead + 3 * perLegReal;
      const cap = 16777216; // EIP-7825 per-tx gas cap (2^24)
      const tMax = Math.floor((cap - baseOverhead) / perLegReal);
      console.log(`        ${name.padEnd(13)} real/leg ~${perLegReal} gas | 3-of-n ~${t3} gas | t_max under 2^24 ~ ${tMax}`);
    }
    // sanity: overhead is positive and gas grew with t
    if (!(g6 > g2 && perLegOverhead > 0)) throw new Error("unexpected gas profile");
  });
});
