import { expect } from "chai";
import { ethers } from "hardhat";
import { time } from "@nomicfoundation/hardhat-network-helpers";
import { ComplianceRegistry, MockIdentityRegistry } from "../typechain-types";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";

describe("ComplianceRegistry", function () {
  let identity: MockIdentityRegistry;
  let compliance: ComplianceRegistry;
  let owner: SignerWithAddress;
  let auditor: SignerWithAddress;
  let agent1: SignerWithAddress;
  let random: SignerWithAddress;

  const DURATION = 365 * 24 * 3600; // 1 year
  const URI = "ipfs://QmCompliance123";
  const FRAMEWORK = "EU_AI_ACT_2026";

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

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

    const ComplianceFactory = await ethers.getContractFactory("ComplianceRegistry");
    compliance = (await ComplianceFactory.deploy(
      await identity.getAddress()
    )) as ComplianceRegistry;

    // Authorize auditor
    await compliance.authorizeAuditor(auditor.address, "ipfs://auditor-creds");
  });

  describe("Deployment", function () {
    it("Should deploy with correct identity registry", async function () {
      expect(await compliance.identityRegistry()).to.equal(await identity.getAddress());
      expect(await compliance.owner()).to.equal(owner.address);
    });

    it("Should reject zero address", async function () {
      const Factory = await ethers.getContractFactory("ComplianceRegistry");
      await expect(Factory.deploy(ethers.ZeroAddress)).to.be.reverted;
    });
  });

  describe("Auditor Management", function () {
    it("Should authorize auditor", async function () {
      const info = await compliance.auditors(auditor.address);
      expect(info.authorized).to.be.true;
      expect(await compliance.totalAuditors()).to.equal(1);
    });

    it("Should revoke auditor", async function () {
      await compliance.revokeAuditor(auditor.address);
      const info = await compliance.auditors(auditor.address);
      expect(info.authorized).to.be.false;
    });

    it("Should reject duplicate authorization", async function () {
      await expect(compliance.authorizeAuditor(auditor.address, "dup")).to.be.reverted;
    });

    it("Should reject non-owner auditor management", async function () {
      await expect(compliance.connect(random).authorizeAuditor(random.address, "test")).to.be.reverted;
    });
  });

  describe("Attestation Issuance", function () {
    it("Should issue attestation for minimal risk agent", async function () {
      const tx = await compliance.connect(auditor).issueAttestation(
        agent1.address, 0, DURATION, URI, FRAMEWORK, false, false, false, false
      );
      await expect(tx).to.emit(compliance, "AttestationIssued");

      const att = await compliance.attestations(0);
      expect(att.agent).to.equal(agent1.address);
      expect(att.riskLevel).to.equal(0); // Minimal
      expect(att.status).to.equal(1); // Compliant
      expect(att.framework).to.equal(FRAMEWORK);
    });

    it("Should issue attestation for high-risk agent with all checks", async function () {
      const tx = await compliance.connect(auditor).issueAttestation(
        agent1.address, 2, DURATION, URI, FRAMEWORK, true, true, true, true
      );
      await expect(tx).to.emit(compliance, "AttestationIssued");
    });

    it("Should reject high-risk without all compliance checks", async function () {
      await expect(compliance.connect(auditor).issueAttestation(
        agent1.address, 2, DURATION, URI, FRAMEWORK, false, true, true, true
      )).to.be.reverted;

      await expect(compliance.connect(auditor).issueAttestation(
        agent1.address, 2, DURATION, URI, FRAMEWORK, true, false, true, true
      )).to.be.reverted;

      await expect(compliance.connect(auditor).issueAttestation(
        agent1.address, 2, DURATION, URI, FRAMEWORK, true, true, false, true
      )).to.be.reverted;

      await expect(compliance.connect(auditor).issueAttestation(
        agent1.address, 2, DURATION, URI, FRAMEWORK, true, true, true, false
      )).to.be.reverted;
    });

    it("Should reject unacceptable risk level", async function () {
      await expect(compliance.connect(auditor).issueAttestation(
        agent1.address, 3, DURATION, URI, FRAMEWORK, true, true, true, true
      )).to.be.reverted;
    });

    it("Should reject unauthorized auditor", async function () {
      await expect(compliance.connect(random).issueAttestation(
        agent1.address, 0, DURATION, URI, FRAMEWORK, false, false, false, false
      )).to.be.reverted;
    });

    it("Should reject unregistered agent", async function () {
      await expect(compliance.connect(auditor).issueAttestation(
        random.address, 0, DURATION, URI, FRAMEWORK, false, false, false, false
      )).to.be.reverted;
    });

    it("Should mark agent as compliant", async function () {
      await compliance.connect(auditor).issueAttestation(
        agent1.address, 0, DURATION, URI, FRAMEWORK, false, false, false, false
      );
      expect(await compliance.isCompliant(agent1.address)).to.be.true;
    });
  });

  describe("Attestation Revocation", function () {
    beforeEach(async function () {
      await compliance.connect(auditor).issueAttestation(
        agent1.address, 0, DURATION, URI, FRAMEWORK, false, false, false, false
      );
    });

    it("Should revoke attestation", async function () {
      const tx = await compliance.connect(auditor).revokeAttestation(0, "Compliance issue found");
      await expect(tx).to.emit(compliance, "AttestationRevoked");

      const att = await compliance.attestations(0);
      expect(att.status).to.equal(4); // Revoked
    });

    it("Should update compliance status on revocation", async function () {
      await compliance.connect(auditor).revokeAttestation(0, "Revoked");
      expect(await compliance.isCompliant(agent1.address)).to.be.false;
    });

    it("Should reject revocation by non-auditor", async function () {
      await expect(compliance.connect(random).revokeAttestation(0, "test")).to.be.reverted;
    });
  });

  describe("Attestation Expiry", function () {
    it("Should expire attestation after duration", async function () {
      await compliance.connect(auditor).issueAttestation(
        agent1.address, 0, DURATION, URI, FRAMEWORK, false, false, false, false
      );

      await time.increase(DURATION + 1);
      await compliance.expireAttestation(0);

      const att = await compliance.attestations(0);
      expect(att.status).to.equal(3); // Expired
    });

    it("Should reject early expiry", async function () {
      await compliance.connect(auditor).issueAttestation(
        agent1.address, 0, DURATION, URI, FRAMEWORK, false, false, false, false
      );
      await expect(compliance.expireAttestation(0)).to.be.reverted;
    });
  });

  describe("View Functions", function () {
    it("Should check compliance status", async function () {
      const [compliant, active] = await compliance.checkCompliance(agent1.address);
      expect(compliant).to.be.false;
      expect(active).to.equal(0);
    });

    it("Should return compliance stats", async function () {
      const [total, auditors_, compliant] = await compliance.getComplianceStats();
      expect(total).to.equal(0);
      expect(auditors_).to.equal(1);
      expect(compliant).to.equal(0);
    });
  });
});
