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

describe("CCIP Integration", function () {
  let ccipLogger, ccipReporter;
  let owner, relayer;
  let mockRouter, mockSourceRouter;

  beforeEach(async function () {
    [owner, relayer] = await ethers.getSigners();

    // Deploy mock routers for testing
    // In production, these would be the actual Chainlink CCIP routers
    const MockRouter = await ethers.getContractFactory("MockCCIPRouter");
    mockRouter = await MockRouter.deploy();
    await mockRouter.waitForDeployment();

    mockSourceRouter = await MockRouter.deploy();
    await mockSourceRouter.waitForDeployment();

    // Deploy CCIPLogger (Ethereum receiver)
    const CCIPLogger = await ethers.getContractFactory("CCIPLogger");
    ccipLogger = await CCIPLogger.deploy(
      await mockRouter.getAddress(),
      ethers.ZeroAddress, // No authorized signer for basic test
      "0x000000000000008a" // Chain-138 selector
    );
    await ccipLogger.waitForDeployment();

    // Deploy CCIPTxReporter (Chain-138 sender)
    const CCIPTxReporter = await ethers.getContractFactory("CCIPTxReporter");
    ccipReporter = await CCIPTxReporter.deploy(
      await mockSourceRouter.getAddress(),
      "0x500147", // Ethereum Mainnet selector
      await ccipLogger.getAddress()
    );
    await ccipReporter.waitForDeployment();
  });

  describe("CCIPTxReporter", function () {
    it("Should report a single transaction", async function () {
      const txHash = ethers.keccak256(ethers.toUtf8Bytes("test-tx"));
      const fromAddr = owner.address;
      const toAddr = relayer.address;
      const value = ethers.parseEther("1.0");

      await expect(
        ccipReporter.reportTx(txHash, fromAddr, toAddr, value, "0x", {
          value: ethers.parseEther("0.01"),
        })
      ).to.emit(ccipReporter, "SingleTxReported");
    });

    it("Should report a batch of transactions", async function () {
      const batchId = ethers.keccak256(ethers.toUtf8Bytes("test-batch"));
      const txHashes = [
        ethers.keccak256(ethers.toUtf8Bytes("tx1")),
        ethers.keccak256(ethers.toUtf8Bytes("tx2")),
      ];
      const froms = [owner.address, relayer.address];
      const tos = [relayer.address, owner.address];
      const values = [ethers.parseEther("1.0"), ethers.parseEther("2.0")];

      await expect(
        ccipReporter.reportBatch(batchId, txHashes, froms, tos, values, "0x", {
          value: ethers.parseEther("0.01"),
        })
      ).to.emit(ccipReporter, "BatchReported");
    });

    it("Should estimate fee correctly", async function () {
      const txHashes = [ethers.keccak256(ethers.toUtf8Bytes("tx1"))];
      const froms = [owner.address];
      const tos = [relayer.address];
      const values = [ethers.parseEther("1.0")];

      const fee = await ccipReporter.estimateFee(
        txHashes,
        froms,
        tos,
        values,
        "0x"
      );

      expect(fee).to.be.gt(0);
    });
  });

  describe("CCIPLogger", function () {
    it("Should receive and log transactions", async function () {
      const batchId = ethers.keccak256(ethers.toUtf8Bytes("test-batch"));
      const txHashes = [ethers.keccak256(ethers.toUtf8Bytes("tx1"))];
      const froms = [owner.address];
      const tos = [relayer.address];
      const values = [ethers.parseEther("1.0")];

      const payload = ethers.AbiCoder.defaultAbiCoder().encode(
        ["bytes32", "bytes32[]", "address[]", "address[]", "uint256[]", "bytes"],
        [batchId, txHashes, froms, tos, values, "0x"]
      );

      // Simulate CCIP message delivery
      const message = {
        messageId: ethers.keccak256(ethers.toUtf8Bytes("test-message")),
        sourceChainSelector: "0x000000000000008a",
        sender: await ccipReporter.getAddress(),
        data: payload,
        destTokenAmounts: [],
      };

      await expect(
        mockRouter.deliverMessage(await ccipLogger.getAddress(), message)
      ).to.emit(ccipLogger, "RemoteBatchLogged");
    });

    it("Should prevent replay attacks", async function () {
      const batchId = ethers.keccak256(ethers.toUtf8Bytes("replay-test"));
      const txHashes = [ethers.keccak256(ethers.toUtf8Bytes("tx1"))];
      const froms = [owner.address];
      const tos = [relayer.address];
      const values = [ethers.parseEther("1.0")];

      const payload = ethers.AbiCoder.defaultAbiCoder().encode(
        ["bytes32", "bytes32[]", "address[]", "address[]", "uint256[]", "bytes"],
        [batchId, txHashes, froms, tos, values, "0x"]
      );

      const message = {
        messageId: ethers.keccak256(ethers.toUtf8Bytes("test-message-1")),
        sourceChainSelector: "0x000000000000008a",
        sender: await ccipReporter.getAddress(),
        data: payload,
        destTokenAmounts: [],
      };

      // First delivery should succeed
      await mockRouter.deliverMessage(await ccipLogger.getAddress(), message);

      // Second delivery with same batchId should fail
      const message2 = {
        ...message,
        messageId: ethers.keccak256(ethers.toUtf8Bytes("test-message-2")),
      };

      await expect(
        mockRouter.deliverMessage(await ccipLogger.getAddress(), message2)
      ).to.be.revertedWith("CCIPLogger: batch already processed");
    });
  });
});

