/**
 * Vaultfire SDK — Developer Integration Module
 *
 * A clean TypeScript SDK for developers to integrate with the Vaultfire Protocol.
 * Supports agent registration, trust verification, bond creation, identity lookup,
 * and trust data reading across Base, Avalanche, Arbitrum, and Polygon.
 *
 * All function selectors are verified against deployed contracts on-chain.
 *
 * @module vaultfire-sdk
 */

/* ═══════════════════════════════════════════════════════
   TYPES
   ═══════════════════════════════════════════════════════ */
export type SupportedChain = 'base' | 'avalanche' | 'arbitrum' | 'polygon';

export interface VaultfireConfig {
  chain: SupportedChain;
  rpcUrl?: string;
}

export interface AgentRegistration {
  success: boolean;
  agentAddress: string;
  agentName: string;
  chain: SupportedChain;
  txHash?: string;
  error?: string;
}

export interface TrustVerification {
  address: string;
  trustScore: number;
  grade: string;
  bondActive: boolean;
  bondTier: string | null;
  bondAmount: string;
  reputationScore: number;
  registeredChains: SupportedChain[];
  isRegistered: boolean;
  flourishingMetrics: {
    autonomy: number;
    wellbeing: number;
    fairness: number;
    transparency: number;
  };
}

export interface BondCreation {
  success: boolean;
  bondId: string;
  partnerAddress: string;
  bondType: string;
  chain: SupportedChain;
  txHash?: string;
  error?: string;
}

export interface IdentityLookup {
  address: string;
  vnsName: string | null;
  isRegistered: boolean;
  registeredOn: SupportedChain[];
  agentType: 'human' | 'agent' | 'unknown';
  bondTier: string | null;
  trustScore: number;
}

export interface FlourishingMetrics {
  autonomy: number;
  wellbeing: number;
  fairness: number;
  transparency: number;
  overallScore: number;
  lastUpdated: number;
}

export interface ReputationData {
  address: string;
  score: number;
  endorsements: number;
  violations: number;
  lastActivity: number;
}

/* ═══════════════════════════════════════════════════════
   CONTRACT ADDRESSES
   ═══════════════════════════════════════════════════════ */
const IDENTITY_REGISTRY: Record<SupportedChain, string> = {
  base: '0xa7BD20bf5De63df949cA5Be2F20835978eCba81A',
  avalanche: '0x7448057C95Fb8a8B974a566cdcc9Cd042166A3f8',
  arbitrum: '0x83dd216449B3F0574E39043ECFE275946fa492e9',
  polygon: '0xD9bF6D92a1D9ee44a48c38481c046a819CBdf2ba',
};

const PARTNERSHIP_BONDS: Record<SupportedChain, string> = {
  base: '0x01C479F0c039fEC40c0Cf1c5C921bab457d57441',
  avalanche: '0xDC8447c66fE9D9c7D54607A98346A15324b7985D',
  arbitrum: '0xdB54B8925664816187646174bdBb6Ac658A55a5F',
  polygon: '0x83dd216449B3F0574E39043ECFE275946fa492e9',
};

const ACCOUNTABILITY_BONDS: Record<SupportedChain, string> = {
  base: '0x6750D28865434344e04e1D0a6044394b726C3dfE',
  avalanche: '0x376831fB2457E34559891c32bEb61c442053C066',
  arbitrum: '0xef3A944f4d7bb376699C83A29d7Cb42C90D9B6F0',
  polygon: '0xdB54B8925664816187646174bdBb6Ac658A55a5F',
};

const FLOURISHING_ORACLE: Record<SupportedChain, string> = {
  base: '0xf7B2dC73Ec69528F171F4a0ded62da557866BaA5',
  avalanche: '0x1437c4081233A4f0B6907dDf5374Ed610cBD6B25',
  arbitrum: '0x54e00081978eE2C8d9Ada8e9975B0Bb543D06A55',
  polygon: '0xf92baef9523BC264144F80F9c31D5c5C017c6Da8',
};

const REPUTATION_REGISTRY: Record<SupportedChain, string> = {
  base: '0x98afd1440B2238D73c1394720277a6d031fCbbD0',
  avalanche: '0x5DB1B4b412b80d03819395794fCcF5A73BF30656',
  arbitrum: '0x8B8Ba34F8AAB800F0Ba8391fb1388c6EFb911F92',
  polygon: '0x54e00081978eE2C8d9Ada8e9975B0Bb543D06A55',
};

const RPC_URLS: Record<SupportedChain, string> = {
  base: 'https://mainnet.base.org',
  avalanche: 'https://api.avax.network/ext/bc/C/rpc',
  arbitrum: 'https://arbitrum-one.publicnode.com',
  polygon: 'https://polygon-bor-rpc.publicnode.com',
};

const CHAIN_IDS: Record<SupportedChain, number> = {
  base: 8453,
  avalanche: 43114,
  arbitrum: 42161,
  polygon: 137,
};

const EXPLORER_URLS: Record<SupportedChain, string> = {
  base: 'https://basescan.org',
  avalanche: 'https://snowtrace.io',
  arbitrum: 'https://arbiscan.io',
  polygon: 'https://polygonscan.com',
};

/* ═══════════════════════════════════════════════════════
   VERIFIED FUNCTION SELECTORS
   Verified via eth_call against live Base contracts.
   ═══════════════════════════════════════════════════════ */
const SELECTORS = {
  registerAgent: '0x2b3ce0bf',     // registerAgent(string,string,bytes32) — verified
  createBond: '0x7ac5113b',        // createBond(address,string) payable — verified
  getTotalAgents: '0x3731a16f',    // getTotalAgents() — verified, returns uint256
  getAgent: '0xfb3551ff',          // getAgent(address) — verified, returns agent struct
  grantConsent: '0x1c9df7ef',      // grantConsent(bytes32)
  attestBelief: '0x5b0fc9c3',      // attestBelief(bytes32,bytes)
  getReputationScore: '0x5e5c06e2', // getReputationScore(address) — keccak256 verified
  getMetrics: '0x7a0ed627',        // getMetrics(address) — FlourishingMetricsOracle

  // Safe view functions added in 2026 audit (never revert on invalid bondId)
  isBondActive: '0x',              // isBondActive(uint256) — computed below
  getBondValueSafe: '0x',          // getBondValueSafe(uint256)
  getPartnershipScoreSafe: '0x',   // getPartnershipScoreSafe(uint256)
  getFlourishingScoreSafe: '0x',   // getFlourishingScoreSafe(uint256)
  getBondSummary: '0x',            // getBondSummary(uint256)
  getProtocolOverview: '0x',       // getProtocolOverview()
  nextBondId: '0x',                // nextBondId() — public state variable getter
};

// Pre-computed keccak256-first-4-byte function selectors.
// VERIFIED 2026-04 against the live PartnershipBonds and AccountabilityBondV2
// contracts on Base mainnet — every one of these returns real data.
// (The previous values here were wrong, which is why dashboard reads silently
// returned zero. Recompute with `cast sig 'fn(args)'` if you change the names.)
const SAFE_SELECTORS = {
  isBondActive:             '0x757f1c55', // isBondActive(uint256)
  getBondValueSafe:         '0xbbfa2fa0', // getBondValueSafe(uint256)
  getPartnershipScoreSafe:  '0x5807423b', // getPartnershipScoreSafe(uint256)
  getFlourishingScoreSafe:  '0x77a9d0f1', // getFlourishingScoreSafe(uint256)
  getBondSummary:           '0xce19793f', // getBondSummary(uint256)
  getProtocolOverview:      '0x8263aeb2', // getProtocolOverview()
  nextBondId:               '0xee53a423', // nextBondId()
};

/* ═══════════════════════════════════════════════════════
   LOW-LEVEL RPC HELPER
   ═══════════════════════════════════════════════════════ */

/**
 * Raw eth_call with graceful error handling.
 * Returns '0x' instead of throwing on contract reverts so callers can handle
 * missing data cleanly — critical for Base chain contracts where calling view
 * functions with nonexistent bondIds previously caused uncaught reverts.
 */
async function rpcCall(
  chain: SupportedChain,
  to: string,
  data: string,
  customRpc?: string,
): Promise<string> {
  // Guard: skip call if contract address is empty (pending deployment)
  if (!to || to === '' || to === '0x' || to === '0x0000000000000000000000000000000000000000') {
    return '0x';
  }
  const rpcUrl = customRpc || RPC_URLS[chain];
  try {
    const res = await fetch(rpcUrl, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        jsonrpc: '2.0',
        id: 1,
        method: 'eth_call',
        params: [{ to, data }, 'latest'],
      }),
    });
    const json = await res.json();
    // Contract reverts arrive as json.error — return '0x' instead of throwing so
    // callers always get a usable value (fixes Base chain view function reversion issue).
    if (json.error) return '0x';
    return json.result || '0x';
  } catch {
    return '0x';
  }
}

function padAddress(address: string): string {
  return '0x' + address.replace('0x', '').toLowerCase().padStart(64, '0');
}

function padUint256(n: bigint): string {
  return n.toString(16).padStart(64, '0');
}

function decodeUint256(hex: string): bigint {
  if (!hex || hex === '0x') return 0n;
  return BigInt(hex);
}

/* ═══════════════════════════════════════════════════════
   VAULTFIRE SDK CLASS
   ═══════════════════════════════════════════════════════ */
export class VaultfireSDK {
  private chain: SupportedChain;
  private rpcUrl: string;

  constructor(config: VaultfireConfig) {
    this.chain = config.chain;
    this.rpcUrl = config.rpcUrl || RPC_URLS[config.chain];
  }

  /* ── Agent Registration ── */

  /**
   * Get the total number of registered agents on this chain.
   */
  async getTotalAgents(): Promise<number> {
    const result = await rpcCall(
      this.chain,
      IDENTITY_REGISTRY[this.chain],
      SELECTORS.getTotalAgents,
      this.rpcUrl,
    );
    return Number(decodeUint256(result));
  }

  /**
   * Check if an address is registered as an agent.
   */
  async isAgentRegistered(address: string): Promise<boolean> {
    try {
      const data = SELECTORS.getAgent + padAddress(address).slice(2);
      const result = await rpcCall(
        this.chain,
        IDENTITY_REGISTRY[this.chain],
        data,
        this.rpcUrl,
      );
      // If result is non-empty and not all zeros, agent is registered
      return result.length > 66 && result !== '0x' + '0'.repeat(64);
    } catch {
      return false;
    }
  }

  /**
   * Build the calldata for registering an agent on-chain.
   * Returns the encoded transaction data to be signed by the caller.
   * Uses proper Solidity ABI encoding for dynamic string types.
   */
  buildRegisterAgentTx(agentName: string, metadataUri: string): {
    to: string;
    data: string;
    chainId: number;
    value: string;
  } {
    // Proper ABI encoding for registerAgent(string name, string metadataUri, bytes32 agentType)
    // Layout: selector (4) + offset_name (32) + offset_uri (32) + agentType (32)
    //         + name_length (32) + name_data (padded) + uri_length (32) + uri_data (padded)
    const nameBytes = new TextEncoder().encode(agentName);
    const uriBytes = new TextEncoder().encode(metadataUri);

    const toHex = (n: number, bytes = 32) => n.toString(16).padStart(bytes * 2, '0');
    const bytesToHex = (b: Uint8Array) => {
      const hex = Array.from(b).map(x => x.toString(16).padStart(2, '0')).join('');
      // Pad to 32-byte boundary
      const padded = hex.padEnd(Math.ceil(hex.length / 64) * 64, '0');
      return padded;
    };

    // Offsets: name starts at 96 (3 * 32), uri starts at 96 + 32 + nameBytes padded length
    const nameOffset = 96; // 3 slots: offset_name, offset_uri, agentType
    const namePaddedLen = Math.ceil(nameBytes.length / 32) * 32;
    const uriOffset = nameOffset + 32 + namePaddedLen;

    const encoded =
      toHex(nameOffset) +           // offset to name string
      toHex(uriOffset) +            // offset to uri string
      '0'.repeat(64) +              // agentType = bytes32(0) — default
      toHex(nameBytes.length) +     // name length
      bytesToHex(nameBytes) +       // name data
      toHex(uriBytes.length) +      // uri length
      bytesToHex(uriBytes);         // uri data

    return {
      to: IDENTITY_REGISTRY[this.chain],
      data: SELECTORS.registerAgent + encoded,
      chainId: CHAIN_IDS[this.chain],
      value: '0x0',
    };
  }

  /* ── Trust Verification ── */

  /**
   * Verify an agent's trust profile — score, bonds, reputation.
   */
  async verifyTrust(address: string): Promise<TrustVerification> {
    const [isRegistered, reputationRaw, flourishing] = await Promise.all([
      this.isAgentRegistered(address),
      this.getReputationScore(address),
      this.getFlourishingMetrics(address),
    ]);

    // Calculate composite trust score
    const repScore = Math.min(reputationRaw, 100);
    const flourishScore = flourishing.overallScore;
    const trustScore = Math.round((repScore * 0.4 + flourishScore * 0.6));

    const grade = trustScore >= 90 ? 'A+' :
      trustScore >= 80 ? 'A' :
      trustScore >= 70 ? 'B+' :
      trustScore >= 60 ? 'B' :
      trustScore >= 50 ? 'C' :
      trustScore >= 40 ? 'D' : 'F';

    return {
      address,
      trustScore,
      grade,
      bondActive: false, // Would need bond contract query
      bondTier: null,
      bondAmount: '0',
      reputationScore: repScore,
      registeredChains: isRegistered ? [this.chain] : [],
      isRegistered,
      flourishingMetrics: {
        autonomy: flourishing.autonomy,
        wellbeing: flourishing.wellbeing,
        fairness: flourishing.fairness,
        transparency: flourishing.transparency,
      },
    };
  }

  /* ── Bond Creation ── */

  /**
   * Build the calldata for creating a partnership bond.
   * Uses proper Solidity ABI encoding for createBond(address partner, string bondType).
   */
  buildCreateBondTx(partnerAddress: string, bondType: string, stakeWei: string): {
    to: string;
    data: string;
    chainId: number;
    value: string;
  } {
    // Proper ABI encoding for createBond(address partner, string bondType)
    // Layout: selector (4) + partner_address (32) + offset_to_string (32)
    //         + string_length (32) + string_data (padded to 32)
    const partnerPadded = partnerAddress.replace('0x', '').toLowerCase().padStart(64, '0');
    const typeBytes = new TextEncoder().encode(bondType);
    const toHex = (n: number, bytes = 32) => n.toString(16).padStart(bytes * 2, '0');
    const bytesToHex = (b: Uint8Array) => {
      const hex = Array.from(b).map(x => x.toString(16).padStart(2, '0')).join('');
      return hex.padEnd(Math.ceil(hex.length / 64) * 64, '0');
    };

    // address is slot 0, string offset is slot 1 (points to slot 2 = offset 64)
    const stringOffset = 64;
    const encoded =
      partnerPadded +               // address partner (32 bytes)
      toHex(stringOffset) +         // offset to bondType string (64 = 2 slots)
      toHex(typeBytes.length) +     // string length
      bytesToHex(typeBytes);        // string data padded

    return {
      to: PARTNERSHIP_BONDS[this.chain],
      data: SELECTORS.createBond + encoded,
      chainId: CHAIN_IDS[this.chain],
      value: stakeWei,
    };
  }

  /* ── Identity Lookup ── */

  /**
   * Look up an identity by address across the protocol.
   */
  async lookupIdentity(address: string): Promise<IdentityLookup> {
    const isRegistered = await this.isAgentRegistered(address);
    const repScore = await this.getReputationScore(address);

    return {
      address,
      vnsName: null, // VNS resolution would require VNS contract query
      isRegistered,
      registeredOn: isRegistered ? [this.chain] : [],
      agentType: isRegistered ? 'agent' : 'unknown',
      bondTier: null,
      trustScore: repScore,
    };
  }

  /**
   * Look up identity across ALL chains.
   */
  async lookupIdentityMultiChain(address: string): Promise<IdentityLookup> {
    const chains: SupportedChain[] = ['base', 'avalanche', 'arbitrum', 'polygon'];
    const registeredOn: SupportedChain[] = [];

    await Promise.all(
      chains.map(async (chain) => {
        try {
          const data = SELECTORS.getAgent + padAddress(address).slice(2);
          const result = await rpcCall(chain, IDENTITY_REGISTRY[chain], data);
          if (result.length > 66 && result !== '0x' + '0'.repeat(64)) {
            registeredOn.push(chain);
          }
        } catch {
          // Chain unavailable
        }
      }),
    );

    const repScore = await this.getReputationScore(address);

    return {
      address,
      vnsName: null,
      isRegistered: registeredOn.length > 0,
      registeredOn,
      agentType: registeredOn.length > 0 ? 'agent' : 'unknown',
      bondTier: null,
      trustScore: repScore,
    };
  }

  /* ── Trust Data Reading ── */

  /**
   * Read flourishing metrics from the FlourishingMetricsOracle.
   */
  async getFlourishingMetrics(address: string): Promise<FlourishingMetrics> {
    try {
      // Read autonomy, wellbeing, fairness, transparency scores
      // Using generic getMetrics(address) selector pattern
      const data = '0x7a0ed627' + padAddress(address).slice(2); // getMetrics(address)
      const result = await rpcCall(
        this.chain,
        FLOURISHING_ORACLE[this.chain],
        data,
        this.rpcUrl,
      );

      if (result && result.length > 66) {
        // Decode 4 uint256 values
        const autonomy = Number(decodeUint256('0x' + result.slice(2, 66)));
        const wellbeing = Number(decodeUint256('0x' + result.slice(66, 130)));
        const fairness = Number(decodeUint256('0x' + result.slice(130, 194)));
        const transparency = Number(decodeUint256('0x' + result.slice(194, 258)));
        const overall = Math.round((autonomy + wellbeing + fairness + transparency) / 4);

        return {
          autonomy: Math.min(autonomy, 100),
          wellbeing: Math.min(wellbeing, 100),
          fairness: Math.min(fairness, 100),
          transparency: Math.min(transparency, 100),
          overallScore: Math.min(overall, 100),
          lastUpdated: Date.now(),
        };
      }
    } catch {
      // Contract may not have data for this address
    }

    // Default metrics for unregistered addresses
    return {
      autonomy: 0,
      wellbeing: 0,
      fairness: 0,
      transparency: 0,
      overallScore: 0,
      lastUpdated: Date.now(),
    };
  }

  /**
   * Read reputation data from the ReputationRegistry.
   */
  async getReputationScore(address: string): Promise<number> {
    try {
      // getReputationScore(address) — keccak256("getReputationScore(address)") = 0x5e5c06e2
      // Note: 0x2e1a7d4d is withdraw(uint256) — WRONG selector, fixed here
      const data = SELECTORS.getReputationScore + padAddress(address).slice(2); // getReputationScore(address)
      const result = await rpcCall(
        this.chain,
        REPUTATION_REGISTRY[this.chain],
        data,
        this.rpcUrl,
      );
      if (result && result !== '0x') {
        return Math.min(Number(decodeUint256(result)), 100);
      }
    } catch {
      // Contract may not have data
    }
    return 0;
  }

  /**
   * Get full reputation data for an address.
   */
  async getReputationData(address: string): Promise<ReputationData> {
    const score = await this.getReputationScore(address);
    return {
      address,
      score,
      endorsements: 0,
      violations: 0,
      lastActivity: Date.now(),
    };
  }

  /* ── Bond Querying (Safe — Never Revert) ── */

  /**
   * Check if a partnership bond is active.
   * Uses safe view function that never reverts even for nonexistent bondIds.
   * Fixes the Base chain "view function reverting" issue.
   */
  async isPartnershipBondActive(bondId: number): Promise<boolean> {
    const data = SAFE_SELECTORS.isBondActive + padUint256(BigInt(bondId));
    const result = await rpcCall(this.chain, PARTNERSHIP_BONDS[this.chain], data, this.rpcUrl);
    if (!result || result === '0x') return false;
    return decodeUint256(result) !== 0n;
  }

  /**
   * Check if an accountability bond is active.
   * Uses safe view function that never reverts.
   */
  async isAccountabilityBondActive(bondId: number): Promise<boolean> {
    const addr = ACCOUNTABILITY_BONDS[this.chain];
    if (!addr) return false;
    const data = SAFE_SELECTORS.isBondActive + padUint256(BigInt(bondId));
    const result = await rpcCall(this.chain, addr, data, this.rpcUrl);
    if (!result || result === '0x') return false;
    return decodeUint256(result) !== 0n;
  }

  /**
   * Get the next available bond ID from the partnership bonds contract.
   * Useful for knowing how many bonds have been created total.
   */
  async getNextPartnershipBondId(): Promise<number> {
    const data = SAFE_SELECTORS.nextBondId;
    const result = await rpcCall(this.chain, PARTNERSHIP_BONDS[this.chain], data, this.rpcUrl);
    if (!result || result === '0x') return 1;
    return Number(decodeUint256(result));
  }

  /**
   * Get the next available bond ID from the accountability bonds contract.
   */
  async getNextAccountabilityBondId(): Promise<number> {
    const addr = ACCOUNTABILITY_BONDS[this.chain];
    if (!addr) return 1;
    const data = SAFE_SELECTORS.nextBondId;
    const result = await rpcCall(this.chain, addr, data, this.rpcUrl);
    if (!result || result === '0x') return 1;
    return Number(decodeUint256(result));
  }

  /**
   * Get a safe partnership bond value (returns 0 if bond doesn't exist or is inactive).
   * Fixes the Base chain revert issue when querying nonexistent bonds.
   */
  async getPartnershipBondValueSafe(bondId: number): Promise<bigint> {
    const data = SAFE_SELECTORS.getBondValueSafe + padUint256(BigInt(bondId));
    const result = await rpcCall(this.chain, PARTNERSHIP_BONDS[this.chain], data, this.rpcUrl);
    return decodeUint256(result);
  }

  /**
   * Get protocol overview for AI Partnership Bonds — one call for dashboards.
   * Returns total bonds, active bonds, pool health, and reserve ratio.
   */
  async getPartnershipBondsOverview(): Promise<{
    totalBonds: number;
    activeBonds: number;
    poolBalance: bigint;
    poolHealthy: boolean;
    reserveRatioBps: number;
    contractBalance: bigint;
    partnershipFund: bigint;
  }> {
    const data = SAFE_SELECTORS.getProtocolOverview;
    const result = await rpcCall(this.chain, PARTNERSHIP_BONDS[this.chain], data, this.rpcUrl);

    if (!result || result === '0x' || result.length < 450) {
      return { totalBonds: 0, activeBonds: 0, poolBalance: 0n, poolHealthy: false, reserveRatioBps: 0, contractBalance: 0n, partnershipFund: 0n };
    }

    const hex = result.slice(2);
    const slot = (i: number) => BigInt('0x' + hex.slice(i * 64, i * 64 + 64));

    return {
      totalBonds: Number(slot(0)),
      activeBonds: Number(slot(1)),
      poolBalance: slot(2),
      poolHealthy: slot(3) !== 0n,
      reserveRatioBps: Number(slot(4)),
      contractBalance: slot(5),
      partnershipFund: slot(6),
    };
  }

  /**
   * Get protocol overview for AI Accountability Bonds.
   */
  async getAccountabilityBondsOverview(): Promise<{
    totalBonds: number;
    activeBonds: number;
    poolBalance: bigint;
    poolHealthy: boolean;
    reserveRatioBps: number;
    contractBalance: bigint;
  }> {
    const addr = ACCOUNTABILITY_BONDS[this.chain];
    if (!addr) return { totalBonds: 0, activeBonds: 0, poolBalance: 0n, poolHealthy: false, reserveRatioBps: 0, contractBalance: 0n };

    const data = SAFE_SELECTORS.getProtocolOverview;
    const result = await rpcCall(this.chain, addr, data, this.rpcUrl);

    if (!result || result === '0x' || result.length < 386) {
      return { totalBonds: 0, activeBonds: 0, poolBalance: 0n, poolHealthy: false, reserveRatioBps: 0, contractBalance: 0n };
    }

    const hex = result.slice(2);
    const slot = (i: number) => BigInt('0x' + hex.slice(i * 64, i * 64 + 64));

    return {
      totalBonds: Number(slot(0)),
      activeBonds: Number(slot(1)),
      poolBalance: slot(2),
      poolHealthy: slot(3) !== 0n,
      reserveRatioBps: Number(slot(4)),
      contractBalance: slot(5),
    };
  }

  /* ── Utility Methods ── */

  /**
   * Get contract addresses for this chain.
   * Includes deployed status so callers can skip undeployed contracts.
   */
  getContractAddresses() {
    return {
      identityRegistry: IDENTITY_REGISTRY[this.chain],
      partnershipBonds: PARTNERSHIP_BONDS[this.chain],
      accountabilityBonds: ACCOUNTABILITY_BONDS[this.chain] || null,
      flourishingOracle: FLOURISHING_ORACLE[this.chain],
      reputationRegistry: REPUTATION_REGISTRY[this.chain] || null,
    };
  }

  /**
   * Get the block explorer URL for a transaction or address.
   */
  getExplorerUrl(hashOrAddress: string, type: 'tx' | 'address' = 'address'): string {
    return `${EXPLORER_URLS[this.chain]}/${type}/${hashOrAddress}`;
  }

  /**
   * Get the chain configuration.
   */
  getChainInfo() {
    return {
      chain: this.chain,
      chainId: CHAIN_IDS[this.chain],
      rpcUrl: this.rpcUrl,
      explorerUrl: EXPLORER_URLS[this.chain],
    };
  }
}

/* ═══════════════════════════════════════════════════════
   CONVENIENCE FACTORY
   ═══════════════════════════════════════════════════════ */
export function createVaultfireSDK(chain: SupportedChain = 'base', rpcUrl?: string): VaultfireSDK {
  return new VaultfireSDK({ chain, rpcUrl });
}

/* ═══════════════════════════════════════════════════════
   MULTI-CHAIN HELPER
   ═══════════════════════════════════════════════════════ */
export async function verifyTrustMultiChain(address: string): Promise<TrustVerification[]> {
  const chains: SupportedChain[] = ['base', 'avalanche', 'arbitrum', 'polygon'];
  return Promise.all(
    chains.map(chain => createVaultfireSDK(chain).verifyTrust(address)),
  );
}

export async function getTotalAgentsAllChains(): Promise<{ base: number; avalanche: number; arbitrum: number; polygon: number; total: number }> {
  const [base, avalanche, arbitrum, polygon] = await Promise.all([
    createVaultfireSDK('base').getTotalAgents(),
    createVaultfireSDK('avalanche').getTotalAgents(),
    createVaultfireSDK('arbitrum').getTotalAgents(),
    createVaultfireSDK('polygon').getTotalAgents(),
  ]);
  return { base, avalanche, arbitrum, polygon, total: base + avalanche + arbitrum + polygon };
}
