const { expect } = require("chai");
const { ethers } = require("hardhat");

// ---------------------------------------------------------------------------
// AereCryptoRegistry: additive entries for the two NEW native precompiles
// (ML-KEM-768 @ 0x0AE6 and Falcon HashToPoint @ 0x0AE7).
//
// These precompiles are TESTNET-ONLY today; mainnet 2800 still has exactly the five
// 0x0AE1..0x0AE5 (activated block 9,189,161). The registry change is PURELY ADDITIVE:
//   - SCHEME_FALCON_HASHTOPOINT (id 15) is the new append-only auxiliary-primitive id,
//   - the ML-KEM-768 precompile maps to the pre-existing SCHEME_MLKEM768 (id 13),
//   - PRECOMPILE_MLKEM768_TESTNET / PRECOMPILE_FALCON_HASHTOPOINT_TESTNET are reference
//     address constants only.
// This test asserts every new constant resolves to its canonical value, that id 15 is
// distinct from the existing ids 1..14, and that the additions changed NO behavior:
// seedLiveSchemes still seeds exactly the five live rows and neither new precompile is
// seeded or routed.
// ---------------------------------------------------------------------------

const MLKEM768_ADDR = ethers.getAddress("0x0000000000000000000000000000000000000ae6");
const HASHTOPOINT_ADDR = ethers.getAddress("0x0000000000000000000000000000000000000ae7");

// The full canonical scheme space that pre-existed this change (ids 1..14).
const EXISTING_SCHEMES = [
  "SCHEME_FALCON512",
  "SCHEME_FALCON1024",
  "SCHEME_MLDSA44",
  "SCHEME_SLHDSA128S",
  "SCHEME_SHAKE256",
  "SCHEME_MLDSA65",
  "SCHEME_MLDSA87",
  "SCHEME_FALCON512_PADDED",
  "SCHEME_FALCON1024_PADDED",
  "SCHEME_SLHDSA192S",
  "SCHEME_SLHDSA256S",
  "SCHEME_MLKEM512",
  "SCHEME_MLKEM768",
  "SCHEME_MLKEM1024",
];

describe("AereCryptoRegistry: new-precompile additive entries (0x0AE6 / 0x0AE7)", function () {
  let reg;

  beforeEach(async function () {
    const R = await ethers.getContractFactory("AereCryptoRegistry");
    reg = await R.deploy();
    await reg.waitForDeployment();
  });

  it("SCHEME_FALCON_HASHTOPOINT resolves to the canonical id 15", async function () {
    expect(Number(await reg.SCHEME_FALCON_HASHTOPOINT())).to.equal(15);
  });

  it("the ML-KEM-768 precompile maps to the pre-existing SCHEME_MLKEM768 (id 13)", async function () {
    expect(Number(await reg.SCHEME_MLKEM768())).to.equal(13);
  });

  it("PRECOMPILE_MLKEM768_TESTNET resolves to 0x0AE6", async function () {
    expect(await reg.PRECOMPILE_MLKEM768_TESTNET()).to.equal(MLKEM768_ADDR);
  });

  it("PRECOMPILE_FALCON_HASHTOPOINT_TESTNET resolves to 0x0AE7", async function () {
    expect(await reg.PRECOMPILE_FALCON_HASHTOPOINT_TESTNET()).to.equal(HASHTOPOINT_ADDR);
  });

  it("id 15 is distinct from every pre-existing canonical id (1..14)", async function () {
    const htp = Number(await reg.SCHEME_FALCON_HASHTOPOINT());
    const seen = new Set();
    for (const name of EXISTING_SCHEMES) {
      seen.add(Number(await reg[name]()));
    }
    expect(seen.size).to.equal(14); // ids 1..14 all distinct
    expect(seen.has(htp), "HashToPoint id collides with an existing id").to.equal(false);
    // The whole space (existing + new) is 15 distinct ids: exactly 1..15.
    seen.add(htp);
    expect(seen.size).to.equal(15);
    expect([...seen].sort((a, b) => a - b)).to.deep.equal(
      Array.from({ length: 15 }, (_, i) => i + 1)
    );
  });

  it("is purely additive: seedLiveSchemes still seeds exactly the five live rows", async function () {
    await reg.seedLiveSchemes();
    expect(Number(await reg.algorithmCount())).to.equal(5);
  });

  it("neither new precompile is seeded or routed (no behavior change)", async function () {
    await reg.seedLiveSchemes();

    // The ML-KEM-768 scheme (13) has no seeded row, so precompileFor must revert UnknownScheme.
    await expect(reg.precompileFor(13)).to.be.revertedWithCustomError(reg, "UnknownScheme");
    // The Falcon HashToPoint scheme (15) likewise has no seeded row.
    await expect(reg.precompileFor(15)).to.be.revertedWithCustomError(reg, "UnknownScheme");

    // The reference address constants are NOT wired into any routable row: the five seeded
    // rows point only at 0x0AE1..0x0AE5, never at 0x0AE6 / 0x0AE7.
    const seededVerifiers = new Set();
    for (let id = 1; id <= 5; id++) {
      const a = await reg.getAlgorithm(id);
      seededVerifiers.add(a.verifier.toLowerCase());
    }
    expect(seededVerifiers.has(MLKEM768_ADDR.toLowerCase())).to.equal(false);
    expect(seededVerifiers.has(HASHTOPOINT_ADDR.toLowerCase())).to.equal(false);
  });
});
