// =============================================================================
// REAL Hardhat coverage tests for contracts/AereFeeBurnVault.sol.
//
// AereFeeBurnVault is an IN-USE contract: live on chain 2800 at
// 0x696afDF4f814e6Fd6aa45CE14C498ed9375fB2c6 (deployments/fee-burn-vault.json),
// referenced in aerenew/sdk-js/src/addresses.ts, and the destination of 37.5%
// of protocol fees routed by AereCoinbaseSplitter. The scoped coverage run in
// aerenew/docs/AERE-TEST-COVERAGE-REPORT.md measured it at 0/0/0/0 because none
// of the six economic-core tests call its functions.
//
// These tests exercise EVERY function and EVERY reachable branch, prioritising
// branch coverage (revert paths + boundary conditions), as required by the task.
//
// The one genuinely unreachable branch is the `require(ok, "zero-send failed")`
// FALSE path in sweepToZero(): a low-level call to address(0) with sufficient
// balance always succeeds on the EVM (it is a value transfer to a bare account),
// so that failure branch cannot be driven from a test. It is documented here
// rather than faked.
//
// No em-dashes anywhere in this file.
// =============================================================================

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

const ONE = 10n ** 18n;
const ZERO = ethers.ZeroAddress;
const ADDR_ZERO_BURN = "0x0000000000000000000000000000000000000000";

describe("AereFeeBurnVault (in-use fee burn endpoint) — full branch battery", function () {
  let deployer, alice, bob, vault, vaultAddr, waere, waereAddr;

  beforeEach(async function () {
    [deployer, alice, bob] = await ethers.getSigners();

    vault = await (await ethers.getContractFactory("AereFeeBurnVault")).deploy();
    await vault.waitForDeployment();
    vaultAddr = await vault.getAddress();

    // A real, compliant ERC20 whose transferFrom returns true: WAERE.
    waere = await (await ethers.getContractFactory("contracts/WAERE.sol:WAERE")).deploy();
    await waere.waitForDeployment();
    waereAddr = await waere.getAddress();
  });

  /* ------------------------------- native burn ------------------------------ */

  describe("receive() — plain native AERE transfer is a burn", function () {
    it("credits totalBurnedAERE and emits AereBurned", async function () {
      const amt = 3n * ONE;
      await expect(alice.sendTransaction({ to: vaultAddr, value: amt }))
        .to.emit(vault, "AereBurned")
        .withArgs(alice.address, amt, amt);

      expect(await vault.totalBurnedAERE()).to.equal(amt);
      expect(await ethers.provider.getBalance(vaultAddr)).to.equal(amt);
      expect(await vault.currentAEREBalance()).to.equal(amt);
    });

    it("accumulates across multiple senders (running total in the event)", async function () {
      await alice.sendTransaction({ to: vaultAddr, value: ONE });
      await bob.sendTransaction({ to: vaultAddr, value: 2n * ONE });
      expect(await vault.totalBurnedAERE()).to.equal(3n * ONE);
      expect(await vault.currentAEREBalance()).to.equal(3n * ONE);
    });
  });

  describe("burn() — explicit native burn entrypoint", function () {
    it("credits totalBurnedAERE and emits AereBurned with the running total", async function () {
      const amt = 5n * ONE;
      await expect(vault.connect(alice).burn({ value: amt }))
        .to.emit(vault, "AereBurned")
        .withArgs(alice.address, amt, amt);
      expect(await vault.totalBurnedAERE()).to.equal(amt);
    });

    it("burn() and receive() share the same counter", async function () {
      await vault.connect(alice).burn({ value: ONE });
      await alice.sendTransaction({ to: vaultAddr, value: ONE });
      expect(await vault.totalBurnedAERE()).to.equal(2n * ONE);
    });

    it("burn() with zero value is a no-op success that still emits (amount 0)", async function () {
      await expect(vault.connect(alice).burn({ value: 0 }))
        .to.emit(vault, "AereBurned")
        .withArgs(alice.address, 0, 0);
      expect(await vault.totalBurnedAERE()).to.equal(0);
    });
  });

  /* -------------------------------- burnToken ------------------------------- */

  describe("burnToken() — ERC20 burn pull path (both sides of all 3 requires)", function () {
    it("SUCCESS: pulls the token, bumps per-token counter, emits TokenBurned", async function () {
      const amt = 7n * ONE;
      // Fund alice with WAERE and approve the vault.
      await alice.sendTransaction({ to: waereAddr, value: amt });
      await waere.connect(alice).approve(vaultAddr, amt);

      await expect(vault.connect(alice).burnToken(waereAddr, amt))
        .to.emit(vault, "TokenBurned")
        .withArgs(waereAddr, alice.address, amt, amt);

      expect(await vault.totalBurnedToken(waereAddr)).to.equal(amt);
      expect(await waere.balanceOf(vaultAddr)).to.equal(amt);
      // Native counter untouched by an ERC20 burn.
      expect(await vault.totalBurnedAERE()).to.equal(0);
    });

    it("SUCCESS: per-token counter accumulates across calls", async function () {
      await alice.sendTransaction({ to: waereAddr, value: 10n * ONE });
      await waere.connect(alice).approve(vaultAddr, 10n * ONE);
      await vault.connect(alice).burnToken(waereAddr, 4n * ONE);
      await vault.connect(alice).burnToken(waereAddr, 6n * ONE);
      expect(await vault.totalBurnedToken(waereAddr)).to.equal(10n * ONE);
    });

    it("REVERT: zero token address (require token != address(0))", async function () {
      await expect(vault.connect(alice).burnToken(ZERO, ONE)).to.be.revertedWith(
        "BurnVault: zero token"
      );
    });

    it("REVERT: zero amount (require amount > 0)", async function () {
      await expect(vault.connect(alice).burnToken(waereAddr, 0)).to.be.revertedWith(
        "BurnVault: zero amount"
      );
    });

    it("REVERT: transferFrom returns false (require ok) via a non-compliant token", async function () {
      const bad = await (await ethers.getContractFactory("MockReturnFalseERC20")).deploy();
      await bad.waitForDeployment();
      const badAddr = await bad.getAddress();
      await bad.mint(alice.address, ONE);
      await bad.connect(alice).approve(vaultAddr, ONE);

      await expect(vault.connect(alice).burnToken(badAddr, ONE)).to.be.revertedWith(
        "BurnVault: transferFrom failed"
      );
      // Counter never moved on the failed pull.
      expect(await vault.totalBurnedToken(badAddr)).to.equal(0);
    });

    it("REVERT: transferFrom reverts (no approval) surfaces the token's own revert", async function () {
      await alice.sendTransaction({ to: waereAddr, value: ONE });
      // No approve() -> WAERE.transferFrom reverts on insufficient allowance.
      await expect(vault.connect(alice).burnToken(waereAddr, ONE)).to.be.reverted;
    });
  });

  /* ------------------------------- sweepToZero ------------------------------ */

  describe("sweepToZero() — forward accumulated native AERE to address(0)", function () {
    it("SUCCESS: sends the whole balance to 0x0, bumps totalSentToZero, emits", async function () {
      const amt = 8n * ONE;
      await vault.connect(alice).burn({ value: amt });
      expect(await vault.currentAEREBalance()).to.equal(amt);

      const zeroBefore = await ethers.provider.getBalance(ADDR_ZERO_BURN);

      await expect(vault.connect(bob).sweepToZero())
        .to.emit(vault, "AereSentToZero")
        .withArgs(amt, amt);

      // Vault emptied, sent-to-zero counter reflects the sweep.
      expect(await vault.currentAEREBalance()).to.equal(0);
      expect(await vault.totalSentToZero()).to.equal(amt);
      // address(0) actually received the value.
      expect(await ethers.provider.getBalance(ADDR_ZERO_BURN)).to.equal(zeroBefore + amt);
      // The gross-burned counter is a lifetime figure and is NOT reset by a sweep.
      expect(await vault.totalBurnedAERE()).to.equal(amt);
    });

    it("SUCCESS: two sweeps accumulate totalSentToZero", async function () {
      await vault.connect(alice).burn({ value: 2n * ONE });
      await vault.connect(bob).sweepToZero();
      await vault.connect(alice).burn({ value: 3n * ONE });
      await vault.connect(bob).sweepToZero();
      expect(await vault.totalSentToZero()).to.equal(5n * ONE);
      expect(await vault.currentAEREBalance()).to.equal(0);
    });

    it("REVERT: nothing to sweep when balance is zero (require bal > 0)", async function () {
      await expect(vault.connect(alice).sweepToZero()).to.be.revertedWith(
        "BurnVault: nothing to sweep"
      );
    });

    it("REVERT: nothing to sweep AFTER a full sweep empties the vault", async function () {
      await vault.connect(alice).burn({ value: ONE });
      await vault.connect(bob).sweepToZero();
      // Second sweep with a zero balance reverts.
      await expect(vault.connect(bob).sweepToZero()).to.be.revertedWith(
        "BurnVault: nothing to sweep"
      );
    });
  });

  /* -------------------------------- read views ------------------------------ */

  describe("currentAEREBalance() view", function () {
    it("tracks the live native balance across burn and sweep", async function () {
      expect(await vault.currentAEREBalance()).to.equal(0);
      await vault.connect(alice).burn({ value: 4n * ONE });
      expect(await vault.currentAEREBalance()).to.equal(4n * ONE);
      await vault.connect(bob).sweepToZero();
      expect(await vault.currentAEREBalance()).to.equal(0);
    });
  });
});
