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

describe("AereDeathSwitch", () => {
  let foundation, alice, bob;
  let bridge, dex, ds;

  const BUCKET_BRIDGE = ethers.keccak256(ethers.toUtf8Bytes("bridge"));
  const BUCKET_DEX    = ethers.keccak256(ethers.toUtf8Bytes("dex"));

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

    const M = await ethers.getContractFactory("MockDeathSwitchCondition");
    bridge = await M.deploy(BUCKET_BRIDGE, "bridge collateral ratio breached");
    await bridge.waitForDeployment();
    dex    = await M.deploy(BUCKET_DEX, "dex pool drained > 50%");
    await dex.waitForDeployment();

    const D = await ethers.getContractFactory("AereDeathSwitch");
    ds = await D.deploy(foundation.address, [await bridge.getAddress(), await dex.getAddress()]);
    await ds.waitForDeployment();
  });

  it("constructor registers conditions + buckets", async () => {
    expect(await ds.conditionsLength()).to.equal(2n);
    const [addr0, b0] = await ds.conditionAt(0);
    expect(addr0).to.equal(await bridge.getAddress());
    expect(b0).to.equal(BUCKET_BRIDGE);
    expect(await ds.isTripped()).to.equal(false);
  });

  it("only Foundation can declare emergency; declares + lock", async () => {
    await expect(
      ds.connect(alice).armForDeclaredEmergency("rogue Foundation key?")
    ).to.be.revertedWithCustomError(ds, "NotFoundation");
    await ds.connect(foundation).armForDeclaredEmergency("test incident");
    expect(await ds.isTripped()).to.equal(true);
    expect(await ds.lastTripLabel()).to.equal("test incident");
    expect(await ds.lastTripReason()).to.equal(ethers.keccak256(ethers.toUtf8Bytes("declared")));
  });

  it("permissionless trip succeeds when condition breached", async () => {
    await bridge.setBreached(true);
    await expect(ds.connect(alice).permissionlessTrip(0))
      .to.emit(ds, "Tripped")
      .withArgs(BUCKET_BRIDGE, alice.address, 0n, "bridge collateral ratio breached");
    expect(await ds.isTripped()).to.equal(true);
    expect(await ds.isBucketTripped(BUCKET_BRIDGE)).to.equal(true);
  });

  it("permissionless trip rejects when condition not breached / unknown id", async () => {
    await expect(ds.connect(alice).permissionlessTrip(0))
      .to.be.revertedWithCustomError(ds, "ConditionNotBreached");
    await expect(ds.connect(alice).permissionlessTrip(99))
      .to.be.revertedWithCustomError(ds, "UnknownCondition");
  });

  it("cannot trip twice; rearm flow gated by 7-day timelock", async () => {
    await ds.connect(foundation).armForDeclaredEmergency("incident");
    await expect(ds.connect(foundation).armForDeclaredEmergency("twice"))
      .to.be.revertedWithCustomError(ds, "AlreadyTripped");

    // Only Foundation can request rearm.
    await expect(ds.connect(alice).requestRearm(ethers.ZeroHash))
      .to.be.revertedWithCustomError(ds, "NotFoundation");

    const reason = ethers.keccak256(ethers.toUtf8Bytes("rotated keys"));
    await ds.connect(foundation).requestRearm(reason);
    // Double request prevented.
    await expect(ds.connect(foundation).requestRearm(reason))
      .to.be.revertedWithCustomError(ds, "RearmAlreadyRequested");

    // Cannot finalize before timelock.
    await expect(ds.connect(foundation).finalizeRearm())
      .to.be.revertedWithCustomError(ds, "RearmTimelockOpen");

    // Fast-forward 7d.
    await ethers.provider.send("evm_increaseTime", [7 * 24 * 3600 + 1]);
    await ethers.provider.send("evm_mine", []);

    await expect(ds.connect(foundation).finalizeRearm()).to.emit(ds, "Rearmed");
    expect(await ds.isTripped()).to.equal(false);
    expect(await ds.rearmReadyAt()).to.equal(0n);
  });

  it("finalizeRearm reverts when no request pending", async () => {
    await ds.connect(foundation).armForDeclaredEmergency("incident");
    await expect(ds.connect(foundation).finalizeRearm())
      .to.be.revertedWithCustomError(ds, "NoRearmRequest");
  });

  it("cancelRearmRequest drops a pending request + emits (audit fix MED #37)", async () => {
    await ds.connect(foundation).armForDeclaredEmergency("incident");
    const reason = ethers.keccak256(ethers.toUtf8Bytes("reason"));
    await ds.connect(foundation).requestRearm(reason);
    await expect(ds.connect(foundation).cancelRearmRequest()).to.emit(ds, "RearmCancelled").withArgs(reason);
    expect(await ds.rearmReadyAt()).to.equal(0n);
    await ds.connect(foundation).requestRearm(ethers.ZeroHash);
    expect(await ds.rearmReadyAt()).to.be.gt(0n);
  });

  it("permissionlessUnarmDeclared after 24h if no condition breached (audit fix HIGH #19)", async () => {
    await ds.connect(foundation).armForDeclaredEmergency("Foundation pager test");
    // Too early.
    await expect(ds.connect(alice).permissionlessUnarmDeclared()).to.be.revertedWithCustomError(ds, "RearmTimelockOpen");
    await ethers.provider.send("evm_increaseTime", [24 * 3600 + 1]);
    await ethers.provider.send("evm_mine", []);
    // If a condition is breached now, unarm still rejected.
    await bridge.setBreached(true);
    await expect(ds.connect(alice).permissionlessUnarmDeclared()).to.be.revertedWithCustomError(ds, "NoConditionsBreached");
    await bridge.setBreached(false);
    // Now succeeds — any address can call.
    await ds.connect(alice).permissionlessUnarmDeclared();
    expect(await ds.isTripped()).to.equal(false);
  });

  it("permissionlessUnarmDeclared not applicable for condition-tripped emergencies", async () => {
    await bridge.setBreached(true);
    await ds.connect(alice).permissionlessTrip(0);
    await ethers.provider.send("evm_increaseTime", [24 * 3600 + 1]);
    await ethers.provider.send("evm_mine", []);
    await expect(ds.connect(alice).permissionlessUnarmDeclared()).to.be.revertedWithCustomError(ds, "ConditionNotBreached");
  });

  it("bucketTripped flags clear after rearm finalize", async () => {
    await bridge.setBreached(true);
    await ds.connect(alice).permissionlessTrip(0);
    expect(await ds.bucketTripped(BUCKET_BRIDGE)).to.equal(true);

    await bridge.setBreached(false);
    await ds.connect(foundation).requestRearm(ethers.ZeroHash);
    await ethers.provider.send("evm_increaseTime", [7 * 24 * 3600 + 1]);
    await ethers.provider.send("evm_mine", []);
    await ds.connect(foundation).finalizeRearm();
    expect(await ds.bucketTripped(BUCKET_BRIDGE)).to.equal(false);
  });

  it("isBucketTripped is true while master tripped, even for unregistered buckets", async () => {
    const FOO = ethers.keccak256(ethers.toUtf8Bytes("foo"));
    await ds.connect(foundation).armForDeclaredEmergency("incident");
    expect(await ds.isBucketTripped(FOO)).to.equal(true);
  });

  it("constructor with empty conditions list works (manual-only mode)", async () => {
    const D = await ethers.getContractFactory("AereDeathSwitch");
    const manual = await D.deploy(foundation.address, []);
    expect(await manual.conditionsLength()).to.equal(0n);
    await manual.connect(foundation).armForDeclaredEmergency("manual-only");
    expect(await manual.isTripped()).to.equal(true);
  });
});
