import { expect } from "chai";
import { ethers } from "hardhat";
import { VKPFactory, MockIdentityRegistry, KeyVault } from "../typechain-types";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";

describe("VKPFactory", function () {
  let identity: MockIdentityRegistry;
  let factory: VKPFactory;
  let agent: SignerWithAddress;
  let unregistered: SignerWithAddress;

  const MAX_PER_TX = ethers.parseEther("1");
  const MAX_HOURLY = ethers.parseEther("5");
  const MAX_DAILY = ethers.parseEther("10");
  const EMERGENCY_THRESHOLD = ethers.parseEther("20");

  beforeEach(async function () {
    [agent, unregistered] = await ethers.getSigners();

    const IdentityFactory = await ethers.getContractFactory("MockIdentityRegistry");
    identity = (await IdentityFactory.deploy()) as MockIdentityRegistry;
    await identity.register(agent.address);

    const FactoryFactory = await ethers.getContractFactory("VKPFactory");
    factory = (await FactoryFactory.deploy(
      await identity.getAddress()
    )) as VKPFactory;
  });

  describe("Vault Creation", function () {
    it("Should create vault with correct owner (NOT the factory)", async function () {
      const tx = await factory.createVault(MAX_PER_TX, MAX_HOURLY, MAX_DAILY, EMERGENCY_THRESHOLD);
      await expect(tx).to.emit(factory, "VaultDeployed");

      const vaults = await factory.getAgentVaults(agent.address);
      expect(vaults.length).to.equal(1);

      // Verify the vault owner is the agent, not the factory
      const VaultFactory = await ethers.getContractFactory("KeyVault");
      const vault = VaultFactory.attach(vaults[0]) as KeyVault;
      expect(await vault.owner()).to.equal(agent.address);
    });

    it("Should reject vault creation from unregistered agent", async function () {
      // Unregistered agent should fail because KeyVault constructor checks registration
      await expect(
        factory.connect(unregistered).createVault(MAX_PER_TX, MAX_HOURLY, MAX_DAILY, EMERGENCY_THRESHOLD)
      ).to.be.reverted;
    });

    it("Should track multiple vaults per agent", async function () {
      await factory.createVault(MAX_PER_TX, MAX_HOURLY, MAX_DAILY, EMERGENCY_THRESHOLD);
      await factory.createVault(
        ethers.parseEther("2"),
        ethers.parseEther("10"),
        ethers.parseEther("20"),
        ethers.parseEther("50")
      );

      const vaults = await factory.getAgentVaults(agent.address);
      expect(vaults.length).to.equal(2);
      expect(await factory.totalVaults()).to.equal(2);
    });

    it("Created vault should be functional — execute works", async function () {
      await factory.createVault(MAX_PER_TX, MAX_HOURLY, MAX_DAILY, EMERGENCY_THRESHOLD);

      const vaults = await factory.getAgentVaults(agent.address);
      const VaultFactory = await ethers.getContractFactory("KeyVault");
      const vault = VaultFactory.attach(vaults[0]) as KeyVault;

      // Fund and execute
      await agent.sendTransaction({ to: vaults[0], value: ethers.parseEther("5") });

      const [, recipient] = await ethers.getSigners();
      await expect(
        vault.execute(recipient.address, ethers.parseEther("0.1"), "0x")
      ).to.emit(vault, "TransactionExecuted");
    });

    it("Vault owner should be able to freeze/unfreeze", async function () {
      await factory.createVault(MAX_PER_TX, MAX_HOURLY, MAX_DAILY, EMERGENCY_THRESHOLD);

      const vaults = await factory.getAgentVaults(agent.address);
      const VaultFactory = await ethers.getContractFactory("KeyVault");
      const vault = VaultFactory.attach(vaults[0]) as KeyVault;

      await vault.freeze();
      expect(await vault.isFrozen()).to.equal(true);

      await vault.unfreeze();
      expect(await vault.isFrozen()).to.equal(false);
    });
  });

  describe("Constructor Validation", function () {
    it("Should reject zero address identity registry", async function () {
      const FactoryFactory = await ethers.getContractFactory("VKPFactory");
      await expect(
        FactoryFactory.deploy(ethers.ZeroAddress)
      ).to.be.revertedWithCustomError(factory, "ZeroAddress");
    });
  });
});
