import { expect } from "chai";
import { ethers } from "hardhat";
import { time } from "@nomicfoundation/hardhat-network-helpers";
import {
  AgentInsurancePool,
  AgentSafetyManager,
  ComplianceRegistry,
  ReputationDecay,
  ScopedDelegation,
  AgentSLAEnforcer,
  MockIdentityRegistry,
  MockBondsRegistry,
  MockReputationRegistry,
} from "../typechain-types";
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";

/**
 * ═══════════════════════════════════════════════════════════════
 * END-TO-END INTEGRATION TEST — Vaultfire Agent Protection Layer
 * ═══════════════════════════════════════════════════════════════
 *
 * Tests all 6 contracts working together in real-world scenarios:
 *
 * Scenario 1: Agent onboarding (register → compliance → insurance → SLA → delegation)
 * Scenario 2: Agent misbehavior (bad reports → reputation decay → auto-safety trigger → insurance claim)
 * Scenario 3: Multi-agent delegation chain with progressive narrowing
 * Scenario 4: Full lifecycle — create, operate, violate, recover, expire
 * Scenario 5: Edge cases — all limits, expirations, revocations
 */
describe("E2E Integration — Agent Protection Layer", function () {
  // ───── Shared State ─────
  let identity: MockIdentityRegistry;
  let bonds: MockBondsRegistry;
  let reputation: MockReputationRegistry;

  let insurance: AgentInsurancePool;
  let safety: AgentSafetyManager;
  let compliance: ComplianceRegistry;
  let decay: ReputationDecay;
  let delegation: ScopedDelegation;
  let sla: AgentSLAEnforcer;

  let owner: SignerWithAddress;
  let agentA: SignerWithAddress;   // High-trust agent (score 85)
  let agentB: SignerWithAddress;   // Medium-trust agent (score 50)
  let agentC: SignerWithAddress;   // Low-trust agent (score 20)
  let auditor: SignerWithAddress;  // Compliance auditor
  let monitor: SignerWithAddress;  // SLA monitor
  let claimant: SignerWithAddress; // Insurance claimant
  let random: SignerWithAddress;   // Unauthorized user

  beforeEach(async function () {
    [owner, agentA, agentB, agentC, auditor, monitor, claimant, random] =
      await ethers.getSigners();

    // ───── Deploy Mock Registries ─────
    const IdentityFactory = await ethers.getContractFactory("MockIdentityRegistry");
    identity = (await IdentityFactory.deploy()) as MockIdentityRegistry;

    const BondsFactory = await ethers.getContractFactory("MockBondsRegistry");
    bonds = (await BondsFactory.deploy()) as MockBondsRegistry;

    const ReputationFactory = await ethers.getContractFactory("MockReputationRegistry");
    reputation = (await ReputationFactory.deploy()) as MockReputationRegistry;

    // Register agents
    await identity.register(agentA.address);
    await identity.register(agentB.address);
    await identity.register(agentC.address);

    // Set reputation scores
    await reputation.setReputation(agentA.address, 85, 100); // High trust
    await reputation.setReputation(agentB.address, 50, 40);  // Medium trust
    await reputation.setReputation(agentC.address, 20, 10);  // Low trust

    // ───── Deploy All 6 V2 Contracts ─────
    const idAddr = await identity.getAddress();
    const bondsAddr = await bonds.getAddress();
    const repAddr = await reputation.getAddress();

    const InsuranceFactory = await ethers.getContractFactory("AgentInsurancePool");
    insurance = (await InsuranceFactory.deploy(idAddr, bondsAddr, repAddr)) as AgentInsurancePool;

    const SafetyFactory = await ethers.getContractFactory("AgentSafetyManager");
    safety = (await SafetyFactory.deploy(idAddr, bondsAddr, repAddr)) as AgentSafetyManager;

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

    const DecayFactory = await ethers.getContractFactory("ReputationDecay");
    decay = (await DecayFactory.deploy(idAddr, repAddr)) as ReputationDecay;

    const DelegationFactory = await ethers.getContractFactory("ScopedDelegation");
    delegation = (await DelegationFactory.deploy(idAddr, bondsAddr, repAddr)) as ScopedDelegation;

    const SLAFactory = await ethers.getContractFactory("AgentSLAEnforcer");
    sla = (await SLAFactory.deploy(idAddr, bondsAddr, repAddr)) as AgentSLAEnforcer;

    // ───── Configure Permissions ─────
    // Authorize auditor for compliance
    await compliance.authorizeAuditor(auditor.address, "ipfs://auditor-creds");
    // Authorize monitor for SLA
    await sla.setMonitor(monitor.address, true);
    // Authorize owner as reporter for decay
    await decay.setReporter(owner.address, true);
    // Authorize monitor as safety operator
    await safety.setSafetyOperator(monitor.address, true);
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 1: AGENT ONBOARDING — Full Happy Path
  // ═══════════════════════════════════════════════════

  describe("Scenario 1: Agent Onboarding (Happy Path)", function () {
    it("Should complete full onboarding: compliance → insurance → SLA → delegation", async function () {
      // Step 1: Compliance attestation
      const attestDuration = 365 * 24 * 3600; // 1 year
      await compliance.connect(auditor).issueAttestation(
        agentA.address,
        1, // Limited risk
        attestDuration,
        "ipfs://compliance-doc-agentA",
        "EU_AI_ACT_2026",
        true, true, true, true
      );

      // Verify compliance
      const [isComp, activeAttest] = await compliance.checkCompliance(agentA.address);
      expect(isComp).to.be.true;
      expect(activeAttest).to.equal(1);

      // Step 2: Create insurance policy
      const coverage = ethers.parseEther("10");
      const policyDuration = 90 * 24 * 3600; // 90 days
      const [premium, riskTier] = await insurance.getPremiumQuote(agentA.address, coverage, policyDuration);
      expect(riskTier).to.equal(1); // Low risk (rep 85 >= 70)
      expect(premium).to.be.gt(0);

      await insurance.connect(agentA).createPolicy(coverage, policyDuration, { value: premium });

      // Verify policy
      const [balance, policyCount] = await insurance.getPoolStats();
      expect(balance).to.equal(premium);
      expect(policyCount).to.equal(1);
      expect(await insurance.hasActivePolicy(agentA.address)).to.be.true;

      // Step 3: Create SLA commitment
      const slaStake = ethers.parseEther("1");
      const slaDuration = 30 * 24 * 3600; // 30 days
      await sla.connect(agentA).createSLA(
        9500, // 95% uptime
        5000, // 5 second response time
        9800, // 98% accuracy
        500,  // 5% penalty per violation
        slaDuration,
        { value: slaStake }
      );

      // Verify SLA
      const [activeSLAs, totalStaked] = await sla.getAgentPerformance(agentA.address);
      expect(activeSLAs).to.equal(1);
      expect(totalStaked).to.equal(slaStake);

      // Step 4: Initialize reputation decay profile
      await decay.initializeProfile(agentA.address);
      const [decayedScore, baseScore] = await decay.getCurrentScore(agentA.address);
      expect(baseScore).to.equal(85);
      expect(decayedScore).to.equal(85);

      // Step 5: Create delegation scope (agentA → agentB)
      await delegation.connect(agentA).createScope(
        agentB.address,
        ethers.parseEther("1"),  // 1 ETH max per tx
        ethers.parseEther("10"), // 10 ETH total
        100,  // 100 tx max
        60,   // 60 second rate limit
        90 * 24 * 3600 // 90 days
      );

      // Verify delegation
      const grantedScopes = await delegation.getGrantedScopes(agentA.address);
      expect(grantedScopes.length).to.equal(1);
      expect(await delegation.isScopeValid(0)).to.be.true;

      // Step 6: Safety check — agent should be operational
      const isOp = await safety.isAgentOperational(agentA.address);
      expect(isOp).to.be.true;
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 2: AGENT MISBEHAVIOR — SLA violation → decay → safety
  // ═══════════════════════════════════════════════════

  describe("Scenario 2: Agent Misbehavior Chain", function () {
    it("Should enforce SLA violation → penalty, then safety freeze on low reputation", async function () {
      // Setup: Agent B has SLA
      const slaStake = ethers.parseEther("2");
      await sla.connect(agentB).createSLA(
        9000, // 90% uptime
        10000, // 10s response
        9000,  // 90% accuracy
        500,   // 5% penalty
        30 * 24 * 3600, // 30 days
        { value: slaStake }
      );

      // Submit a bad report — all 3 metrics violated
      const now = await time.latest();
      const periodStart = now - 7200; // 2 hours ago
      const periodEnd = now - 3600;   // 1 hour ago

      await sla.connect(monitor).submitReport(
        0, // slaId
        7000,  // 70% uptime (target 90%) — VIOLATION
        15000, // 15s response (target 10s) — VIOLATION
        8000,  // 80% accuracy (target 90%) — VIOLATION
        periodStart,
        periodEnd
      );

      // Verify violations and penalties
      const slaData = await sla.slas(0);
      expect(slaData.totalViolations).to.equal(3); // 3 violations
      expect(slaData.totalPenalties).to.be.gt(0);

      // Penalty = stake * 500 * 3 / 10000 = 30% of 2 ETH = 0.6 ETH
      const expectedPenalty = (slaStake * BigInt(500) * BigInt(3)) / BigInt(10000);
      expect(slaData.totalPenalties).to.equal(expectedPenalty);

      // Agent's reputation is 50 (medium) — put them in safe mode
      // Safety manager: safeModeThreshold=30, freezeThreshold=10
      // Rep 50 is above safe mode threshold, so checkAndEnforceSafety won't auto-trigger
      // But owner can manually freeze
      await safety.connect(owner).freezeAgent(agentB.address, "Multiple SLA violations detected");

      // Verify frozen state
      expect(await safety.isAgentFrozen(agentB.address)).to.be.true;
      expect(await safety.isAgentOperational(agentB.address)).to.be.false;

      // Verify safety log
      const events = await safety.getAgentSafetyEvents(agentB.address);
      expect(events.length).to.equal(1);

      const [frozenCount, safeModeCount, totalEvents] = await safety.getSafetyStats();
      expect(frozenCount).to.equal(1);
      expect(totalEvents).to.equal(1);
    });

    it("Should auto-freeze agent when reputation drops below threshold", async function () {
      // Set agent C reputation to 5 (below freeze threshold of 10)
      await reputation.setReputation(agentC.address, 5, 10);

      // Anyone can call checkAndEnforceSafety
      await safety.connect(random).checkAndEnforceSafety(agentC.address);

      // Agent C should now be frozen
      expect(await safety.isAgentFrozen(agentC.address)).to.be.true;
      expect(await safety.isAgentOperational(agentC.address)).to.be.false;

      // Verify it was an auto-trigger
      const state = await safety.agentSafety(agentC.address);
      expect(state.autoTriggerCount).to.equal(1);
    });

    it("Should auto-safe-mode agent when reputation is between freeze and safe thresholds", async function () {
      // Set agent C reputation to 25 (above freeze=10, below safeMode=30)
      await reputation.setReputation(agentC.address, 25, 10);

      await safety.connect(random).checkAndEnforceSafety(agentC.address);

      expect(await safety.isAgentInSafeMode(agentC.address)).to.be.true;
      expect(await safety.isAgentOperational(agentC.address)).to.be.false;
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 3: INSURANCE CLAIM LIFECYCLE
  // ═══════════════════════════════════════════════════

  describe("Scenario 3: Insurance Claim — File → Resolve → Payout", function () {
    it("Should process a full insurance claim lifecycle", async function () {
      // Setup: Agent A creates policy with large premium to cover claims
      // Use high coverage so premium is substantial enough for claim payouts
      const coverage = ethers.parseEther("100");
      const duration = 365 * 24 * 3600; // 1 year to maximize premium
      const [premium] = await insurance.getPremiumQuote(agentA.address, coverage, duration);
      await insurance.connect(agentA).createPolicy(coverage, duration, { value: premium });

      // Claim amount must be less than pool balance (premium collected)
      // Premium for 100 ETH coverage, 1yr, low risk = 100 * 0.01 = 1 ETH
      const claimAmount = premium / 2n; // Claim half the pool
      await insurance.connect(claimant).fileClaim(
        0, // policyId
        claimAmount,
        "ipfs://evidence-of-damage"
      );

      // Verify claim filed
      const claim = await insurance.claims(0);
      expect(claim.claimant).to.equal(claimant.address);
      expect(claim.amount).to.equal(claimAmount);
      expect(claim.status).to.equal(0); // Pending

      // Get claimant balance before payout
      const balBefore = await ethers.provider.getBalance(claimant.address);

      // Arbiter (owner) approves the claim
      await insurance.connect(owner).resolveClaim(0, true);

      // Verify claim resolved and paid
      const claimAfter = await insurance.claims(0);
      expect(claimAfter.status).to.equal(3); // Paid

      // Verify claimant received funds
      const balAfter = await ethers.provider.getBalance(claimant.address);
      expect(balAfter - balBefore).to.equal(claimAmount);

      // Verify pool stats updated
      const [poolBal, , , paidOut] = await insurance.getPoolStats();
      expect(paidOut).to.equal(claimAmount);
      expect(poolBal).to.equal(premium - claimAmount);
    });

    it("Should deny a claim and not pay out", async function () {
      const coverage = ethers.parseEther("5");
      const duration = 90 * 24 * 3600;
      const [premium] = await insurance.getPremiumQuote(agentA.address, coverage, duration);
      await insurance.connect(agentA).createPolicy(coverage, duration, { value: premium });

      await insurance.connect(claimant).fileClaim(0, ethers.parseEther("1"), "ipfs://evidence");

      // Deny the claim
      await insurance.connect(owner).resolveClaim(0, false);

      const claim = await insurance.claims(0);
      expect(claim.status).to.equal(2); // Denied
      expect(await insurance.totalClaimsPaid()).to.equal(0);
    });

    it("Should prevent claims exceeding coverage limit", async function () {
      const coverage = ethers.parseEther("1");
      const duration = 90 * 24 * 3600;
      const [premium] = await insurance.getPremiumQuote(agentA.address, coverage, duration);
      await insurance.connect(agentA).createPolicy(coverage, duration, { value: premium });

      // Try to claim more than coverage
      await expect(
        insurance.connect(claimant).fileClaim(0, ethers.parseEther("2"), "ipfs://evidence")
      ).to.be.revertedWith("AgentInsurancePool: invalid claim amount");
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 4: REPUTATION DECAY — Time-based scoring
  // ═══════════════════════════════════════════════════

  describe("Scenario 4: Reputation Decay — Time-based Trust", function () {
    it("Should decay reputation over time without activity", async function () {
      await decay.initializeProfile(agentA.address);

      // Check initial score
      let [score] = await decay.getCurrentScore(agentA.address);
      expect(score).to.equal(85);

      // Advance 7 days (grace period) — no decay
      await time.increase(7 * 24 * 3600);
      [score] = await decay.getCurrentScore(agentA.address);
      expect(score).to.equal(85); // Still 85 — within grace period

      // Advance another 10 days (now 17 days, 10 past grace)
      await time.increase(10 * 24 * 3600);
      [score] = await decay.getCurrentScore(agentA.address);
      expect(score).to.equal(75); // 85 - (10 * 1) = 75
    });

    it("Should record positive activity and reset decay clock", async function () {
      await decay.initializeProfile(agentA.address);

      // Advance past grace period
      await time.increase(17 * 24 * 3600); // 10 days of decay

      // Record positive activity — should apply decay first, then boost
      await decay.connect(owner).recordActivity(agentA.address, "trading", true, 5);

      const profile = await decay.profiles(agentA.address);
      // After decay: 85 - 10 = 75, then +5 = 80
      expect(profile.decayedScore).to.equal(80);
      expect(profile.totalActivities).to.equal(1);
      expect(profile.positiveActivities).to.equal(1);
    });

    it("Should handle negative activity with 2x penalty", async function () {
      await decay.initializeProfile(agentA.address);

      // Record negative activity (weight 5 → penalty = 10)
      await decay.connect(owner).recordActivity(agentA.address, "trading", false, 5);

      const profile = await decay.profiles(agentA.address);
      expect(profile.decayedScore).to.equal(75); // 85 - (5*2) = 75
      expect(profile.negativeActivities).to.equal(1);
    });

    it("Should track context-specific reputation", async function () {
      await decay.initializeProfile(agentA.address);

      // Record activity in "trading" context
      await decay.connect(owner).recordActivity(agentA.address, "trading", true, 8);

      // Record activity in "data_analysis" context
      await decay.connect(owner).recordActivity(agentA.address, "data_analysis", true, 3);

      // Check context scores
      const [tradingScore, tradingActivities] = await decay.getContextScore(agentA.address, "trading");
      expect(tradingScore).to.equal(58); // 50 + 8 = 58
      expect(tradingActivities).to.equal(1);

      const [dataScore, dataActivities] = await decay.getContextScore(agentA.address, "data_analysis");
      expect(dataScore).to.equal(53); // 50 + 3 = 53
      expect(dataActivities).to.equal(1);
    });

    it("Should give recovery bonus for low-score agents", async function () {
      await decay.initializeProfile(agentC.address); // base score 20

      // Record positive activity — agent C has low score, should get recovery bonus
      await decay.connect(owner).recordActivity(agentC.address, "recovery", true, 5);

      const profile = await decay.profiles(agentC.address);
      // Recovery bonus = (50 - 20) * 10 / 50 = 6
      // New score = 20 + 5 + 6 = 31
      expect(profile.decayedScore).to.equal(31);
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 5: DELEGATION CHAIN — Progressive Narrowing
  // ═══════════════════════════════════════════════════

  describe("Scenario 5: Multi-Agent Delegation Chain", function () {
    it("Should create delegation chain A → B → C with progressive narrowing", async function () {
      // A delegates to B
      const scopeAB = await delegation.connect(agentA).createScope.staticCall(
        agentB.address,
        ethers.parseEther("1"),  // 1 ETH per tx
        ethers.parseEther("10"), // 10 ETH total
        100, 60, // 100 tx, 60s rate limit
        90 * 24 * 3600
      );
      await delegation.connect(agentA).createScope(
        agentB.address,
        ethers.parseEther("1"),
        ethers.parseEther("10"),
        100, 60,
        90 * 24 * 3600
      );

      expect(scopeAB).to.equal(0);
      expect(await delegation.isScopeValid(0)).to.be.true;

      // B sub-delegates to C (must be narrower)
      await delegation.connect(agentB).subDelegate(
        0, // parent scope
        agentC.address,
        ethers.parseEther("0.5"),  // Narrower: 0.5 ETH per tx (was 1)
        ethers.parseEther("5"),    // Narrower: 5 ETH total (was 10)
        50,  // Narrower: 50 tx (was 100)
        120, // More restrictive: 120s rate limit (was 60)
        45 * 24 * 3600 // Shorter: 45 days (was 90)
      );

      // Verify both scopes
      expect(await delegation.totalScopes()).to.equal(2);
      expect(await delegation.isScopeValid(1)).to.be.true;

      const receivedByC = await delegation.getReceivedScopes(agentC.address);
      expect(receivedByC.length).to.equal(1);
    });

    it("Should prevent widening permissions in sub-delegation", async function () {
      await delegation.connect(agentA).createScope(
        agentB.address,
        ethers.parseEther("1"),
        ethers.parseEther("10"),
        100, 60,
        90 * 24 * 3600
      );

      // Try to sub-delegate with WIDER permissions — should fail
      await expect(
        delegation.connect(agentB).subDelegate(
          0,
          agentC.address,
          ethers.parseEther("2"),    // WIDER: 2 ETH per tx (parent has 1)
          ethers.parseEther("5"),
          50, 120,
          45 * 24 * 3600
        )
      ).to.be.revertedWith("ScopedDelegation: maxValuePerTx exceeds parent");
    });

    it("Should enforce scope limits when using delegation", async function () {
      await delegation.connect(agentA).createScope(
        agentB.address,
        ethers.parseEther("1"),  // 1 ETH per tx
        ethers.parseEther("2"),  // 2 ETH total
        3,   // 3 tx max
        0,   // No rate limit
        90 * 24 * 3600
      );

      // Use scope 3 times — should succeed
      await delegation.connect(agentB).useScope(0, ethers.parseEther("0.5"), ethers.ZeroAddress, "0x00000000");
      await delegation.connect(agentB).useScope(0, ethers.parseEther("0.5"), ethers.ZeroAddress, "0x00000000");
      await delegation.connect(agentB).useScope(0, ethers.parseEther("0.5"), ethers.ZeroAddress, "0x00000000");

      // 4th usage — tx count exceeded
      await expect(
        delegation.connect(agentB).useScope(0, ethers.parseEther("0.5"), ethers.ZeroAddress, "0x00000000")
      ).to.be.revertedWith("ScopedDelegation: tx count exceeded");
    });

    it("Should enforce rate limiting", async function () {
      await delegation.connect(agentA).createScope(
        agentB.address,
        ethers.parseEther("1"),
        ethers.parseEther("10"),
        0,    // Unlimited tx
        3600, // 1 hour rate limit
        90 * 24 * 3600
      );

      // First use — should succeed
      await delegation.connect(agentB).useScope(0, ethers.parseEther("0.1"), ethers.ZeroAddress, "0x00000000");

      // Immediate second use — should fail (rate limited)
      await expect(
        delegation.connect(agentB).useScope(0, ethers.parseEther("0.1"), ethers.ZeroAddress, "0x00000000")
      ).to.be.revertedWith("ScopedDelegation: rate limited");

      // Advance 1 hour
      await time.increase(3600);

      // Now should succeed
      await delegation.connect(agentB).useScope(0, ethers.parseEther("0.1"), ethers.ZeroAddress, "0x00000000");
    });

    it("Should enforce contract and function allowlists", async function () {
      await delegation.connect(agentA).createScope(
        agentB.address,
        ethers.parseEther("1"),
        ethers.parseEther("10"),
        0, 0,
        90 * 24 * 3600
      );

      const allowedContract = await insurance.getAddress();
      const blockedContract = await sla.getAddress();

      // Set contract allowlist
      await delegation.connect(agentA).setContractAllowlist(0, allowedContract, true);

      // Set function allowlist
      const allowedSelector = "0x12345678";
      await delegation.connect(agentA).setFunctionAllowlist(0, allowedSelector, true);

      // Use with allowed contract + function — should work
      await delegation.connect(agentB).useScope(0, ethers.parseEther("0.1"), allowedContract, allowedSelector);

      // Use with blocked contract — should fail
      await expect(
        delegation.connect(agentB).useScope(0, ethers.parseEther("0.1"), blockedContract, allowedSelector)
      ).to.be.revertedWith("ScopedDelegation: contract not allowed");

      // Use with blocked function — should fail
      await expect(
        delegation.connect(agentB).useScope(0, ethers.parseEther("0.1"), allowedContract, "0xdeadbeef")
      ).to.be.revertedWith("ScopedDelegation: function not allowed");
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 6: COMPLIANCE — Full Attestation Lifecycle
  // ═══════════════════════════════════════════════════

  describe("Scenario 6: Compliance Attestation Lifecycle", function () {
    it("Should issue, verify, and revoke compliance attestation", async function () {
      // Issue attestation
      await compliance.connect(auditor).issueAttestation(
        agentA.address,
        2, // High risk
        365 * 24 * 3600,
        "ipfs://full-compliance-doc",
        "EU_AI_ACT_2026",
        true, true, true, true // All checks required for high risk
      );

      // Verify
      expect(await compliance.isCompliant(agentA.address)).to.be.true;
      const [comp, active] = await compliance.checkCompliance(agentA.address);
      expect(comp).to.be.true;
      expect(active).to.equal(1);

      // Revoke
      await compliance.connect(auditor).revokeAttestation(0, "Agent failed audit");

      // No longer compliant
      expect(await compliance.isCompliant(agentA.address)).to.be.false;
      const [comp2, active2] = await compliance.checkCompliance(agentA.address);
      expect(comp2).to.be.false;
      expect(active2).to.equal(0);
    });

    it("Should require all checks for high-risk agents", async function () {
      // High risk without human oversight — should fail
      await expect(
        compliance.connect(auditor).issueAttestation(
          agentA.address, 2, 365 * 24 * 3600, "ipfs://doc", "EU_AI_ACT_2026",
          false, true, true, true // Missing human oversight
        )
      ).to.be.revertedWith("ComplianceRegistry: high-risk requires human oversight");
    });

    it("Should reject unacceptable risk level", async function () {
      await expect(
        compliance.connect(auditor).issueAttestation(
          agentA.address, 3, 365 * 24 * 3600, "ipfs://doc", "EU_AI_ACT_2026",
          true, true, true, true
        )
      ).to.be.revertedWith("ComplianceRegistry: unacceptable risk agents cannot be attested");
    });

    it("Should expire attestation after duration", async function () {
      const shortDuration = 30 * 24 * 3600; // 30 days
      await compliance.connect(auditor).issueAttestation(
        agentA.address, 0, shortDuration, "ipfs://doc", "NIST_AI_RMF",
        false, false, false, false // Minimal risk — no special requirements
      );

      expect(await compliance.isCompliant(agentA.address)).to.be.true;

      // Advance past expiry
      await time.increase(shortDuration + 1);

      // Expire the attestation
      await compliance.expireAttestation(0);

      expect(await compliance.isCompliant(agentA.address)).to.be.false;
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 7: SLA — Performance Reports & Penalties
  // ═══════════════════════════════════════════════════

  describe("Scenario 7: SLA Performance & Penalties", function () {
    it("Should create SLA, submit clean report, expire, and return stake", async function () {
      const stake = ethers.parseEther("1");
      const duration = 7 * 24 * 3600; // 7 days (minimum)

      await sla.connect(agentA).createSLA(
        9500, 5000, 9800, 500, duration, { value: stake }
      );

      // Submit a clean report — no violations
      const now = await time.latest();
      await sla.connect(monitor).submitReport(
        0,
        9900,  // 99% uptime (target 95%) — PASS
        3000,  // 3s response (target 5s) — PASS
        9900,  // 99% accuracy (target 98%) — PASS
        now - 7200,
        now - 3600
      );

      const report = await sla.reports(0);
      expect(report.uptimeViolation).to.be.false;
      expect(report.responseTimeViolation).to.be.false;
      expect(report.accuracyViolation).to.be.false;
      expect(report.penaltyApplied).to.equal(0);

      // Advance to expiry
      await time.increase(duration + 1);

      // Get agent balance before stake return
      const balBefore = await ethers.provider.getBalance(agentA.address);

      // Expire SLA — should return full stake
      await sla.expireSLA(0);

      const balAfter = await ethers.provider.getBalance(agentA.address);
      expect(balAfter - balBefore).to.equal(stake);
    });

    it("Should calculate penalties correctly for multiple violations", async function () {
      const stake = ethers.parseEther("10");
      await sla.connect(agentB).createSLA(
        9000, 10000, 9000, 1000, // 10% penalty rate
        30 * 24 * 3600, { value: stake }
      );

      const now = await time.latest();

      // All 3 metrics violated
      await sla.connect(monitor).submitReport(
        0, 5000, 20000, 5000, now - 7200, now - 3600
      );

      const slaData = await sla.slas(0);
      // Penalty = 10 ETH * 1000 * 3 / 10000 = 3 ETH
      expect(slaData.totalPenalties).to.equal(ethers.parseEther("3"));
      expect(slaData.totalViolations).to.equal(3);

      // Global stats
      const stats = await sla.getSLAStats();
      expect(stats._totalViolations).to.equal(3);
      expect(stats._penaltyPool).to.equal(ethers.parseEther("3"));
    });

    it("Should drain SLA stake through repeated violations", async function () {
      const stake = ethers.parseEther("1");
      // Use maximum penalty rate (10%) — 3 violations = 30% of remaining each time
      await sla.connect(agentA).createSLA(
        9900, 1000, 9900, 1000, // 10% penalty per violation
        30 * 24 * 3600, { value: stake }
      );

      // Submit 10 bad reports — each drains 30% of remaining stake
      for (let i = 0; i < 10; i++) {
        const slaCheck = await sla.slas(0);
        if (!slaCheck.active) break;

        const now = await time.latest();
        await sla.connect(monitor).submitReport(
          0, 1000, 50000, 1000,
          now - 7200, now - 3600
        );
      }

      // After 10 rounds of 30% reduction, less than 3% of original stake remains
      // 1 ETH * (0.7)^10 = ~0.028 ETH remaining
      const slaFinal = await sla.slas(0);
      const remaining = slaFinal.stakedAmount - slaFinal.totalPenalties;
      expect(remaining).to.be.lt(stake / 20n); // Less than 5% remains
      expect(slaFinal.totalViolations).to.equal(30); // 3 violations × 10 reports
      expect(slaFinal.totalPenalties).to.be.gt(stake * 95n / 100n); // >95% consumed
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 8: SAFETY — Reactivation and Audit Trail
  // ═══════════════════════════════════════════════════

  describe("Scenario 8: Safety Controls & Audit Trail", function () {
    it("Should freeze → reactivate → full audit trail", async function () {
      // Freeze agent
      await safety.connect(owner).freezeAgent(agentB.address, "Suspicious activity detected");
      expect(await safety.isAgentFrozen(agentB.address)).to.be.true;

      // Safe mode on another agent
      await safety.connect(monitor).activateSafeMode(agentA.address, "Under review");
      expect(await safety.isAgentInSafeMode(agentA.address)).to.be.true;

      // Check global stats
      let [frozen, safeMode, events] = await safety.getSafetyStats();
      expect(frozen).to.equal(1);
      expect(safeMode).to.equal(1);
      expect(events).to.equal(2);

      // Reactivate agent B
      await safety.connect(owner).reactivateAgent(agentB.address, "Investigation cleared");
      expect(await safety.isAgentOperational(agentB.address)).to.be.true;

      // Reactivate agent A
      await safety.connect(owner).reactivateAgent(agentA.address, "Review complete");
      expect(await safety.isAgentOperational(agentA.address)).to.be.true;

      // Check counters adjusted
      [frozen, safeMode, events] = await safety.getSafetyStats();
      expect(frozen).to.equal(0);
      expect(safeMode).to.equal(0);
      expect(events).to.equal(4); // 2 freezes + 2 reactivations

      // Full audit trail for agent B
      const agentBEvents = await safety.getAgentSafetyEvents(agentB.address);
      expect(agentBEvents.length).to.equal(2);
    });

    it("Should prevent reactivation of already active agent", async function () {
      // Agent without safety record = operational, no record
      await expect(
        safety.connect(owner).reactivateAgent(agentA.address, "Already active")
      ).to.be.revertedWith("AgentSafetyManager: no safety record");
    });

    it("Should allow only safety operators to trigger controls", async function () {
      await expect(
        safety.connect(random).freezeAgent(agentA.address, "Unauthorized attempt")
      ).to.be.revertedWith("AgentSafetyManager: caller is not authorized");
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 9: ACCESS CONTROL — All contracts
  // ═══════════════════════════════════════════════════

  describe("Scenario 9: Access Control Across All Contracts", function () {
    it("Insurance: only arbiter can resolve claims", async function () {
      const [premium] = await insurance.getPremiumQuote(agentA.address, ethers.parseEther("5"), 90 * 24 * 3600);
      await insurance.connect(agentA).createPolicy(ethers.parseEther("5"), 90 * 24 * 3600, { value: premium });
      await insurance.connect(claimant).fileClaim(0, ethers.parseEther("1"), "ipfs://evidence");

      await expect(
        insurance.connect(random).resolveClaim(0, true)
      ).to.be.revertedWith("AgentInsurancePool: caller is not arbiter");
    });

    it("Insurance: only registered agents can create policies", async function () {
      await expect(
        insurance.connect(random).createPolicy(ethers.parseEther("1"), 90 * 24 * 3600, { value: ethers.parseEther("1") })
      ).to.be.revertedWith("AgentInsurancePool: agent not registered or inactive");
    });

    it("Compliance: only authorized auditors can issue attestations", async function () {
      await expect(
        compliance.connect(random).issueAttestation(
          agentA.address, 0, 365 * 24 * 3600, "ipfs://doc", "EU_AI_ACT_2026",
          false, false, false, false
        )
      ).to.be.revertedWith("ComplianceRegistry: caller is not authorized auditor");
    });

    it("SLA: only monitors can submit reports", async function () {
      await sla.connect(agentA).createSLA(9500, 5000, 9800, 500, 30 * 24 * 3600, { value: ethers.parseEther("1") });
      const now = await time.latest();

      await expect(
        sla.connect(random).submitReport(0, 9900, 3000, 9900, now - 7200, now - 3600)
      ).to.be.revertedWith("AgentSLAEnforcer: caller is not authorized monitor");
    });

    it("Decay: only reporters can record activity", async function () {
      await decay.initializeProfile(agentA.address);

      await expect(
        decay.connect(random).recordActivity(agentA.address, "trading", true, 5)
      ).to.be.revertedWith("ReputationDecay: caller is not authorized reporter");
    });

    it("Delegation: only registered agents can create scopes", async function () {
      await expect(
        delegation.connect(random).createScope(
          agentA.address, ethers.parseEther("1"), ethers.parseEther("10"),
          100, 60, 90 * 24 * 3600
        )
      ).to.be.revertedWith("ScopedDelegation: agent not registered or inactive");
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 10: CROSS-CONTRACT INTEGRATION
  // ═══════════════════════════════════════════════════

  describe("Scenario 10: Cross-Contract Integration Flows", function () {
    it("Should handle full agent lifecycle: onboard → operate → violate → recover", async function () {
      // ── Step 1: Onboard Agent A ──
      // Compliance
      await compliance.connect(auditor).issueAttestation(
        agentA.address, 1, 365 * 24 * 3600, "ipfs://compliance-A", "EU_AI_ACT_2026",
        true, true, true, true
      );
      expect(await compliance.isCompliant(agentA.address)).to.be.true;

      // Insurance
      const [premium] = await insurance.getPremiumQuote(agentA.address, ethers.parseEther("5"), 90 * 24 * 3600);
      await insurance.connect(agentA).createPolicy(ethers.parseEther("5"), 90 * 24 * 3600, { value: premium });

      // SLA
      await sla.connect(agentA).createSLA(9500, 5000, 9800, 500, 30 * 24 * 3600, { value: ethers.parseEther("1") });

      // Decay profile
      await decay.initializeProfile(agentA.address);

      // Delegation
      await delegation.connect(agentA).createScope(
        agentB.address, ethers.parseEther("1"), ethers.parseEther("10"), 100, 60, 90 * 24 * 3600
      );

      // Safety check — all good
      expect(await safety.isAgentOperational(agentA.address)).to.be.true;

      // ── Step 2: Operate Successfully ──
      // Good SLA report
      let now = await time.latest();
      await sla.connect(monitor).submitReport(0, 9900, 3000, 9900, now - 7200, now - 3600);

      // Positive reputation activity
      await decay.connect(owner).recordActivity(agentA.address, "service_delivery", true, 8);

      let [dScore] = await decay.getCurrentScore(agentA.address);
      expect(dScore).to.equal(93); // 85 + 8 = 93

      // ── Step 3: Agent Violates SLA ──
      now = await time.latest();
      await sla.connect(monitor).submitReport(0, 5000, 20000, 5000, now - 7200, now - 3600);

      // Record negative reputation activity
      await decay.connect(owner).recordActivity(agentA.address, "service_delivery", false, 7);

      [dScore] = await decay.getCurrentScore(agentA.address);
      expect(dScore).to.equal(79); // 93 - (7*2) = 79

      // ── Step 4: File insurance claim ──
      // Claim must be <= pool balance (which equals the premium paid)
      const poolStats = await insurance.getPoolStats();
      const poolBalance = poolStats[0];
      const claimAmt = poolBalance / 2n; // Claim half the pool
      await insurance.connect(claimant).fileClaim(0, claimAmt, "ipfs://damage-evidence");
      await insurance.connect(owner).resolveClaim(0, true);

      const [, , , paidOut] = await insurance.getPoolStats();
      expect(paidOut).to.equal(claimAmt);

      // ── Step 5: Recovery ──
      // More positive activity to recover
      await decay.connect(owner).recordActivity(agentA.address, "service_delivery", true, 10);

      [dScore] = await decay.getCurrentScore(agentA.address);
      expect(dScore).to.equal(89); // 79 + 10 = 89

      // Agent still operational
      expect(await safety.isAgentOperational(agentA.address)).to.be.true;
    });

    it("Risk tier affects insurance premium: high-trust agent pays less", async function () {
      const coverage = ethers.parseEther("10");
      const duration = 90 * 24 * 3600;

      // Agent A: rep 85 → risk tier 1 (low risk)
      const [premiumA, tierA] = await insurance.getPremiumQuote(agentA.address, coverage, duration);
      expect(tierA).to.equal(1);

      // Agent B: rep 50 → risk tier 2 (medium risk)
      const [premiumB, tierB] = await insurance.getPremiumQuote(agentB.address, coverage, duration);
      expect(tierB).to.equal(2);

      // Agent C: rep 20 → risk tier 3 (high risk)
      const [premiumC, tierC] = await insurance.getPremiumQuote(agentC.address, coverage, duration);
      expect(tierC).to.equal(3);

      // Low risk pays less than medium, which pays less than high
      expect(premiumA).to.be.lt(premiumB);
      expect(premiumB).to.be.lt(premiumC);
    });
  });

  // ═══════════════════════════════════════════════════
  // SCENARIO 11: ADMIN FUNCTIONS
  // ═══════════════════════════════════════════════════

  describe("Scenario 11: Admin Functions", function () {
    it("Insurance: owner can change arbiter", async function () {
      await insurance.setArbiter(monitor.address);
      expect(await insurance.arbiter()).to.equal(monitor.address);
    });

    it("Safety: owner can update thresholds", async function () {
      await safety.setThresholds(50, 20);
      expect(await safety.safeModeThreshold()).to.equal(50);
      expect(await safety.freezeThreshold()).to.equal(20);
    });

    it("Safety: safe mode must be above freeze", async function () {
      await expect(safety.setThresholds(20, 50)).to.be.revertedWith(
        "AgentSafetyManager: safe mode must be above freeze"
      );
    });

    it("Decay: owner can update decay parameters", async function () {
      await decay.setDecayParameters(3, 14 * 24 * 3600, 10, 15);
      expect(await decay.decayRatePerDay()).to.equal(3);
      expect(await decay.decayGracePeriod()).to.equal(14 * 24 * 3600);
      expect(await decay.minDecayedScore()).to.equal(10);
      expect(await decay.recoveryBonusMax()).to.equal(15);
    });

    it("SLA: owner can withdraw penalties", async function () {
      // Create SLA, trigger violations, then withdraw
      await sla.connect(agentA).createSLA(9900, 1000, 9900, 1000, 30 * 24 * 3600, { value: ethers.parseEther("10") });
      const now = await time.latest();
      await sla.connect(monitor).submitReport(0, 1000, 50000, 1000, now - 7200, now - 3600);

      const poolBal = await sla.penaltyPool();
      expect(poolBal).to.be.gt(0);

      const ownerBalBefore = await ethers.provider.getBalance(owner.address);
      const tx = await sla.withdrawPenalties(owner.address, poolBal);
      const receipt = await tx.wait();
      const gasUsed = receipt!.gasUsed * receipt!.gasPrice;

      const ownerBalAfter = await ethers.provider.getBalance(owner.address);
      expect(ownerBalAfter).to.equal(ownerBalBefore + poolBal - gasUsed);
    });

    it("All contracts: transferOwnership", async function () {
      const newOwner = agentA.address;
      await insurance.transferOwnership(newOwner);
      expect(await insurance.owner()).to.equal(newOwner);

      await safety.transferOwnership(newOwner);
      expect(await safety.owner()).to.equal(newOwner);

      await compliance.transferOwnership(newOwner);
      expect(await compliance.owner()).to.equal(newOwner);

      await decay.transferOwnership(newOwner);
      expect(await decay.owner()).to.equal(newOwner);

      await delegation.transferOwnership(newOwner);
      expect(await delegation.owner()).to.equal(newOwner);

      await sla.transferOwnership(newOwner);
      expect(await sla.owner()).to.equal(newOwner);
    });
  });
});
