// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import "../teleporter/ITeleporterMessenger.sol";
import "../teleporter/ITeleporterReceiver.sol";

/**
 * @title VaultfireTeleporterBridge V3.1 (Partner Ready - Base + Avalanche)
 * @author Vaultfire Protocol - Build Games 2026
 * @notice Cross-chain trust portability bridge using Avalanche Teleporter (ICM).
 *
 * @dev Enables AI agent trust state to be synchronized between Base mainnet
 * and Avalanche C-Chain. When an agent registers, bonds, earns reputation,
 * or gets validated on one chain, the bridge relays that state to the other
 * chain so the agent's trust is recognized everywhere.
 *
 * Architecture:
 *   Base Bridge (relayer) <---> Avalanche Bridge (ICM native)
 *
 * On Avalanche C-Chain the bridge implements ITeleporterReceiver for native
 * Teleporter interop with any Avalanche L1. For Base<->Avalanche transport
 * an authorized off-chain relayer submits messages via relayMessage().
 *
 * Message types:
 *   0 - SYNC_AGENT_REGISTRATION
 *   1 - SYNC_PARTNERSHIP_BOND
 *   2 - SYNC_ACCOUNTABILITY_BOND
 *   3 - SYNC_REPUTATION
 *   4 - SYNC_VALIDATION
 *
 * V3.3 Changes (2026-03 Audit Remediation):
 * - @dev [audit-v3.3] CRIT-02 fix: relayMessage now requires an ECDSA signature from
 *   remoteBridgeSigner over keccak256(encodedMessage). Self-reported sender/sourceChainId
 *   fields inside the payload are no longer the sole authentication mechanism on the
 *   relayer path. A new state variable remoteBridgeSigner and a setter are added.
 *   A per-message hash map relayerMessageHashes provides extra replay protection on the
 *   relayer path independent of processedMessages.
 *
 * V3.1 Changes (2026-02 Partner-Ready - Base + Avalanche):
 * - @dev [audit-fix] HIGH-002 NatSpec converted from @custom:audit-fix to @dev to
 *   prevent DocstringParsingError on Solidity 0.8.25
 * - @dev [audit-fix] Unbounded array caps: all tracking arrays capped at MAX entries
 *   with CapReached events to prevent OOG on iterations
 * - @dev [audit-fix] Bond payloads now include stakeToken (address) for V3.1 multi-asset
 *   awareness: partnership and accountability bonds carry the ERC20 token address
 *   (address(0) = native ETH/AVAX) so the destination chain knows the asset type
 * - @dev [audit-fix] O(n) relayer removal replaced with O(1) swap-and-pop using index mapping
 * - All V2 security features preserved (replay protection, nonce tracking, pause, relayer ACL)
 *
 * @custom:security Access-controlled relayer pattern with replay protection
 * @custom:competition Avalanche Build Games 2026 - first cross-chain trust portability
 * @custom:chains Base (ETH + WAVAX), Avalanche (AVAX + WETH)
 */
contract VaultfireTeleporterBridgeV3 is ITeleporterReceiver {

    // =====================================================================
    //  CONSTANTS
    // =====================================================================

    /// @notice Maximum entries in any tracking array to prevent unbounded growth
    uint256 public constant MAX_SYNCED_ENTRIES = 10000;

    /// @notice Maximum number of authorized relayers
    uint256 public constant MAX_RELAYERS = 50;

    // =====================================================================
    //  ENUMS
    // =====================================================================

    enum MessageType {
        SYNC_AGENT_REGISTRATION,   // 0
        SYNC_PARTNERSHIP_BOND,     // 1
        SYNC_ACCOUNTABILITY_BOND,  // 2
        SYNC_REPUTATION,           // 3
        SYNC_VALIDATION,           // 4
        SYNC_BOND_DEACTIVATION     // 5 [audit-v3.3] HIGH-18
    }

    // =====================================================================
    //  STRUCTS - Cross-chain payloads
    // =====================================================================

    /**
     * @notice Envelope that wraps every cross-chain message.
     * @param messageType  Discriminator for payload decoding.
     * @param sourceChainId  EVM chain ID of the originating chain.
     * @param nonce  Per-bridge monotonic nonce for replay protection.
     * @param timestamp  Block timestamp on the source chain.
     * @param sender  Address that initiated the bridge call on the source chain.
     * @param payload  ABI-encoded type-specific data.
     */
    struct BridgeMessage {
        MessageType messageType;
        uint256 sourceChainId;
        uint256 nonce;
        uint256 timestamp;
        address sender;
        bytes payload;
    }

    /// @notice Payload for SYNC_AGENT_REGISTRATION
    struct AgentRegistrationPayload {
        address agentAddress;
        string agentURI;
        string agentType;
        bytes32 capabilitiesHash;
        uint256 registeredAt;
    }

    /// @notice Payload for SYNC_PARTNERSHIP_BOND
    /// @dev V3.1: added stakeToken for multi-asset awareness
    struct PartnershipBondPayload {
        uint256 bondId;
        address human;
        address aiAgent;
        string purpose;
        uint256 createdAt;
        bool active;
        address stakeToken;
    }

    /// @notice Payload for SYNC_ACCOUNTABILITY_BOND
    /// @dev V3.1: added stakeToken for multi-asset awareness
    struct AccountabilityBondPayload {
        uint256 bondId;
        address aiCompany;
        string companyName;
        uint256 quarterlyRevenue;
        uint256 stakeAmount;
        uint256 createdAt;
        bool active;
        address stakeToken;
    }

    /// @notice Payload for SYNC_REPUTATION
    struct ReputationPayload {
        address agentAddress;
        uint256 totalFeedbacks;
        uint256 averageRating;
        uint256 verifiedFeedbacks;
        uint256 lastUpdated;
    }

    /// @notice Payload for SYNC_VALIDATION
    struct ValidationPayload {
        uint256 requestId;
        address agentAddress;
        uint8 status;          // ValidationStatus enum value
        uint256 approvalsCount;
        uint256 rejectionsCount;
        uint256 timestamp;
    }

    /// @notice [audit-v3.3] HIGH-18: Payload for SYNC_BOND_DEACTIVATION
    struct BondDeactivationPayload {
        uint256 bondId;
        bool isPartnership; // true = partnership, false = accountability
    }

    // =====================================================================
    //  STATE
    // =====================================================================

    /// @notice Contract owner (deployer)
    address public owner;

    /// @notice Whether the bridge is paused
    bool public paused;

    /// @notice Monotonic nonce for outbound messages (replay protection)
    uint256 public outboundNonce;

    /// @notice Address of the Teleporter messenger on this chain (zero on non-Avalanche chains)
    address public teleporterMessenger;

    /// @notice Blockchain ID of the remote chain (Teleporter bytes32 format)
    bytes32 public remoteBlockchainID;

    /// @notice Address of the peer bridge contract on the remote chain
    address public remoteBridgeAddress;

    /// @notice EVM chain ID of the remote chain
    uint256 public remoteChainId;

    /// @notice Required gas limit for Teleporter message execution on destination
    uint256 public requiredGasLimit;

    // -- Relayer whitelist (O(1) add/remove via index mapping) ------------

    mapping(address => bool) public authorizedRelayers;
    address[] public relayerList;
    /// @dev V3.1: index mapping for O(1) swap-and-pop removal
    mapping(address => uint256) internal _relayerIndex;

    // -- Replay protection ------------------------------------------------

    /// @notice Tracks processed message hashes to prevent replay
    mapping(bytes32 => bool) public processedMessages;

    /// @notice Highest inbound nonce seen from the remote chain
    uint256 public lastProcessedNonce;

    // -- [audit-v3.3] CRIT-02: Remote bridge signing key & relayer-path replay guard --------

    /// @notice Address whose ECDSA signatures authenticate relayer-path messages.
    ///         Must be the private key holder on the remote chain that signs each
    ///         outbound BridgeMessage before handing it to the off-chain relayer.
    ///         Set to address(0) initially; relayMessage is blocked until configured.
    // [audit-v3.3] CRIT-02 fix
    address public remoteBridgeSigner;

    /// @notice Secondary replay-protection map for the relayer path.
    ///         Keyed by keccak256(encodedMessage) — set before processing to prevent
    ///         a single relayer from replaying the same signed message twice.
    // [audit-v3.3] CRIT-02 fix
    mapping(bytes32 => bool) public relayerMessageHashes;

    // -- Synced state (mirror of remote chain) ----------------------------

    /// @notice Agents synced from remote chain: agentAddress => registration payload
    mapping(address => AgentRegistrationPayload) public syncedAgents;
    address[] public syncedAgentList;

    /// @notice Partnership bonds synced from remote chain: bondId => payload
    mapping(uint256 => PartnershipBondPayload) public syncedPartnershipBonds;
    uint256[] public syncedPartnershipBondIds;

    /// @notice Accountability bonds synced from remote chain: bondId => payload
    mapping(uint256 => AccountabilityBondPayload) public syncedAccountabilityBonds;
    uint256[] public syncedAccountabilityBondIds;

    /// @notice Reputation synced from remote chain: agentAddress => payload
    mapping(address => ReputationPayload) public syncedReputations;
    address[] public syncedReputationAgents;

    /// @dev [audit-v3] HIGH-BRIDGE-01: Timestamp of last accepted reputation update per agent
    mapping(address => uint256) public syncedReputationTimestamp;

    /// @notice Validation synced from remote chain: requestId => payload
    mapping(uint256 => ValidationPayload) public syncedValidations;
    uint256[] public syncedValidationIds;

    // =====================================================================
    //  EVENTS
    // =====================================================================

    event MessageSent(
        uint256 indexed nonce,
        MessageType indexed messageType,
        bytes32 messageHash,
        uint256 destinationChainId
    );

    event MessageReceived(
        uint256 indexed nonce,
        MessageType indexed messageType,
        bytes32 messageHash,
        uint256 sourceChainId
    );

    event AgentSynced(address indexed agentAddress, string agentType, uint256 sourceChainId);
    event PartnershipBondSynced(uint256 indexed bondId, address human, address aiAgent, address stakeToken, uint256 sourceChainId);
    event AccountabilityBondSynced(uint256 indexed bondId, address aiCompany, address stakeToken, uint256 sourceChainId);
    event ReputationSynced(address indexed agentAddress, uint256 averageRating, uint256 sourceChainId);
    event ValidationSynced(uint256 indexed requestId, address agentAddress, uint8 status, uint256 sourceChainId);
    event BondDeactivationSynced(uint256 indexed bondId, bool isPartnership, uint256 sourceChainId); // [audit-v3.3]

    event RelayerAdded(address indexed relayer);
    event RelayerRemoved(address indexed relayer);
    event BridgePaused(address indexed by);
    event BridgeUnpaused(address indexed by);
    event RemoteConfigured(bytes32 remoteBlockchainID, address remoteBridgeAddress, uint256 remoteChainId);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    /// @dev [audit-v3.3] CRIT-02 fix: emitted when the remote bridge signer is updated
    event RemoteBridgeSignerSet(address indexed previousSigner, address indexed newSigner);

    /// @dev V3.1: emitted when a tracking array hits its cap
    event SyncedCapReached(string arrayName, uint256 cap);

    // =====================================================================
    //  MODIFIERS
    // =====================================================================

    modifier onlyOwner() {
        require(msg.sender == owner, "VaultfireBridge: caller is not the owner");
        _;
    }

    modifier whenNotPaused() {
        require(!paused, "VaultfireBridge: bridge is paused");
        _;
    }

    modifier onlyAuthorizedRelayer() {
        require(authorizedRelayers[msg.sender], "VaultfireBridge: unauthorized relayer");
        _;
    }

    modifier onlyTeleporter() {
        require(
            teleporterMessenger != address(0) && msg.sender == teleporterMessenger,
            "VaultfireBridge: caller is not TeleporterMessenger"
        );
        _;
    }

    // =====================================================================
    //  CONSTRUCTOR
    // =====================================================================

    /**
     * @param _teleporterMessenger  Address of TeleporterMessenger on this chain.
     *        Set to address(0) on non-Avalanche chains (e.g. Base).
     * @param _requiredGasLimit  Gas limit for Teleporter message execution on the
     *        destination chain.  Recommended: 500_000.
     */
    constructor(address _teleporterMessenger, uint256 _requiredGasLimit) {
        owner = msg.sender;
        teleporterMessenger = _teleporterMessenger;
        requiredGasLimit = _requiredGasLimit;

        // Owner is automatically an authorized relayer
        authorizedRelayers[msg.sender] = true;
        relayerList.push(msg.sender);
        _relayerIndex[msg.sender] = 0;
    }

    // =====================================================================
    //  ADMIN - Configuration
    // =====================================================================

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "VaultfireBridge: zero address");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    /**
     * @notice Configure the remote (peer) bridge.
     * @param _remoteBlockchainID  Teleporter blockchain ID of the remote chain.
     *        For non-Avalanche chains use keccak256(abi.encodePacked(chainId)).
     * @param _remoteBridgeAddress  Address of the VaultfireTeleporterBridge on the remote chain.
     * @param _remoteChainId  EVM chain ID of the remote chain.
     */
    function setRemoteBridge(
        bytes32 _remoteBlockchainID,
        address _remoteBridgeAddress,
        uint256 _remoteChainId
    ) external onlyOwner {
        require(_remoteBridgeAddress != address(0), "VaultfireBridge: zero address");
        remoteBlockchainID = _remoteBlockchainID;
        remoteBridgeAddress = _remoteBridgeAddress;
        remoteChainId = _remoteChainId;
        emit RemoteConfigured(_remoteBlockchainID, _remoteBridgeAddress, _remoteChainId);
    }

    function setRequiredGasLimit(uint256 _gasLimit) external onlyOwner {
        requiredGasLimit = _gasLimit;
    }

    function setTeleporterMessenger(address _messenger) external onlyOwner {
        teleporterMessenger = _messenger;
    }

    /**
     * @notice Set the ECDSA signing address that authenticates relayer-path messages.
     * @dev [audit-v3.3] CRIT-02 fix.
     *      The remote bridge must sign each outbound BridgeMessage with this key before
     *      handing it to the off-chain relayer. Set to a non-zero address before the
     *      relayer path can be used. Setting to address(0) disables the relayer path.
     * @param _signer  Address corresponding to the remote bridge's signing private key.
     */
    function setRemoteBridgeSigner(address _signer) external onlyOwner {
        // [audit-v3.3] CRIT-02 fix
        emit RemoteBridgeSignerSet(remoteBridgeSigner, _signer);
        remoteBridgeSigner = _signer;
    }

    // -- Relayer management (O(1) add/remove) -----------------------------

    /// @dev [audit-fix] HIGH-002 - O(1) relayer add with index tracking
    function addRelayer(address relayer) external onlyOwner {
        require(relayer != address(0), "VaultfireBridge: zero address");
        require(!authorizedRelayers[relayer], "VaultfireBridge: already authorized");
        require(relayerList.length < MAX_RELAYERS, "VaultfireBridge: relayer cap reached");
        authorizedRelayers[relayer] = true;
        _relayerIndex[relayer] = relayerList.length;
        relayerList.push(relayer);
        emit RelayerAdded(relayer);
    }

    /// @dev [audit-fix] HIGH-002 - O(1) swap-and-pop removal (was O(n) linear scan)
    function removeRelayer(address relayer) external onlyOwner {
        require(authorizedRelayers[relayer], "VaultfireBridge: not authorized");
        /// @dev [audit-v3] LOW-BRIDGE-02: Prevent removal of the last relayer
        require(relayerList.length > 1, "VaultfireBridge: cannot remove last relayer");
        authorizedRelayers[relayer] = false;

        uint256 idx = _relayerIndex[relayer];
        uint256 lastIdx = relayerList.length - 1;
        if (idx != lastIdx) {
            address lastRelayer = relayerList[lastIdx];
            relayerList[idx] = lastRelayer;
            _relayerIndex[lastRelayer] = idx;
        }
        relayerList.pop();
        delete _relayerIndex[relayer];

        emit RelayerRemoved(relayer);
    }

    function getRelayerCount() external view returns (uint256) {
        return relayerList.length;
    }

    // -- Pause ------------------------------------------------------------

    function pause() external onlyOwner {
        paused = true;
        emit BridgePaused(msg.sender);
    }

    function unpause() external onlyOwner {
        paused = false;
        emit BridgeUnpaused(msg.sender);
    }

    // =====================================================================
    //  OUTBOUND - Send trust state to remote chain
    // =====================================================================

    /**
     * @notice Send an agent registration to the remote chain.
     * @dev [audit-fix] HIGH-002 - onlyAuthorizedRelayer prevents arbitrary trust injection
     */
    function sendAgentRegistration(
        address agentAddress,
        string calldata agentURI,
        string calldata agentType,
        bytes32 capabilitiesHash,
        uint256 registeredAt
    ) external onlyAuthorizedRelayer whenNotPaused returns (uint256 nonce) {
        AgentRegistrationPayload memory payload = AgentRegistrationPayload({
            agentAddress: agentAddress,
            agentURI: agentURI,
            agentType: agentType,
            capabilitiesHash: capabilitiesHash,
            registeredAt: registeredAt
        });
        return _sendMessage(MessageType.SYNC_AGENT_REGISTRATION, abi.encode(payload));
    }

    /**
     * @notice Send a partnership bond to the remote chain.
     * @dev [audit-fix] HIGH-002 - onlyAuthorizedRelayer
     * @dev V3.1: stakeToken param for multi-asset awareness
     * @param stakeToken  ERC20 token address (address(0) = native ETH/AVAX)
     */
    function sendPartnershipBond(
        uint256 bondId,
        address human,
        address aiAgent,
        string calldata purpose,
        uint256 createdAt,
        bool active,
        address stakeToken
    ) external onlyAuthorizedRelayer whenNotPaused returns (uint256 nonce) {
        PartnershipBondPayload memory payload = PartnershipBondPayload({
            bondId: bondId,
            human: human,
            aiAgent: aiAgent,
            purpose: purpose,
            createdAt: createdAt,
            active: active,
            stakeToken: stakeToken
        });
        return _sendMessage(MessageType.SYNC_PARTNERSHIP_BOND, abi.encode(payload));
    }

    /**
     * @notice Send an accountability bond to the remote chain.
     * @dev [audit-fix] HIGH-002 - onlyAuthorizedRelayer
     * @dev V3.1: stakeToken param for multi-asset awareness
     * @param stakeToken  ERC20 token address (address(0) = native ETH/AVAX)
     */
    function sendAccountabilityBond(
        uint256 bondId,
        address aiCompany,
        string calldata companyName,
        uint256 quarterlyRevenue,
        uint256 stakeAmount,
        uint256 createdAt,
        bool active,
        address stakeToken
    ) external onlyAuthorizedRelayer whenNotPaused returns (uint256 nonce) {
        AccountabilityBondPayload memory payload = AccountabilityBondPayload({
            bondId: bondId,
            aiCompany: aiCompany,
            companyName: companyName,
            quarterlyRevenue: quarterlyRevenue,
            stakeAmount: stakeAmount,
            createdAt: createdAt,
            active: active,
            stakeToken: stakeToken
        });
        return _sendMessage(MessageType.SYNC_ACCOUNTABILITY_BOND, abi.encode(payload));
    }

    /**
     * @notice Send a reputation update to the remote chain.
     * @dev [audit-fix] HIGH-002 - onlyAuthorizedRelayer
     */
    function sendReputation(
        address agentAddress,
        uint256 totalFeedbacks,
        uint256 averageRating,
        uint256 verifiedFeedbacks,
        uint256 lastUpdated
    ) external onlyAuthorizedRelayer whenNotPaused returns (uint256 nonce) {
        ReputationPayload memory payload = ReputationPayload({
            agentAddress: agentAddress,
            totalFeedbacks: totalFeedbacks,
            averageRating: averageRating,
            verifiedFeedbacks: verifiedFeedbacks,
            lastUpdated: lastUpdated
        });
        return _sendMessage(MessageType.SYNC_REPUTATION, abi.encode(payload));
    }

    /**
     * @notice Send a validation status update to the remote chain.
     * @dev [audit-fix] HIGH-002 - onlyAuthorizedRelayer
     */
    function sendValidation(
        uint256 requestId,
        address agentAddress,
        uint8 status,
        uint256 approvalsCount,
        uint256 rejectionsCount,
        uint256 timestamp
    ) external onlyAuthorizedRelayer whenNotPaused returns (uint256 nonce) {
        ValidationPayload memory payload = ValidationPayload({
            requestId: requestId,
            agentAddress: agentAddress,
            status: status,
            approvalsCount: approvalsCount,
            rejectionsCount: rejectionsCount,
            timestamp: timestamp
        });
        return _sendMessage(MessageType.SYNC_VALIDATION, abi.encode(payload));
    }

    // =====================================================================
    //  INBOUND - Receive trust state from remote chain
    // =====================================================================

    /**
     * @notice ITeleporterReceiver implementation - called by TeleporterMessenger
     *         on Avalanche when a native Teleporter message arrives.
     */
    function receiveTeleporterMessage(
        bytes32 sourceBlockchainID,
        address originSenderAddress,
        bytes calldata message
    ) external override onlyTeleporter whenNotPaused {
        require(sourceBlockchainID == remoteBlockchainID, "VaultfireBridge: unknown source chain");
        require(originSenderAddress == remoteBridgeAddress, "VaultfireBridge: unknown sender");
        _processInboundMessage(message);
    }

    /**
     * @notice Relayer-based message delivery for cross-ecosystem bridges
     *         (e.g. Base to Avalanche or Avalanche to Base).
     *
     * @dev [audit-v3.3] CRIT-02 fix.
     *      Previously the only source-chain authentication was the self-reported
     *      `sender` and `sourceChainId` fields embedded in the ABI-encoded payload,
     *      which an authorized relayer could freely fabricate.
     *
     *      Now the remote bridge must produce an EIP-191 personal_sign-style ECDSA
     *      signature over `keccak256(encodedMessage)` with `remoteBridgeSigner`'s
     *      private key before handing the message to the off-chain relayer. The
     *      signature is verified here with inline ecrecover. This is the
     *      cryptographic equivalent of what Avalanche Teleporter provides at the
     *      protocol transport layer for `receiveTeleporterMessage`.
     *
     *      Additionally, `relayerMessageHashes` tracks the message hash before
     *      `_processInboundMessage` is called, providing a second, independent
     *      replay-protection layer on the relayer path (complementing
     *      `processedMessages` which is also checked inside `_processInboundMessage`).
     *
     * @param encodedMessage  The ABI-encoded BridgeMessage.
     * @param signature       65-byte ECDSA signature produced by `remoteBridgeSigner`
     *                        over the EIP-191 hash of `keccak256(encodedMessage)`.
     *                        Encoding: abi.encodePacked(r, s, v)  (r=32B, s=32B, v=1B).
     */
    function relayMessage(
        bytes calldata encodedMessage,
        bytes calldata signature
    ) external onlyAuthorizedRelayer whenNotPaused {
        // [audit-v3.3] CRIT-02 fix — require a configured signer before accepting any
        // relayer-path message.
        require(remoteBridgeSigner != address(0), "VaultfireBridge: signer not configured");

        // [audit-v3.3] CRIT-02 fix — secondary replay guard on the relayer path.
        bytes32 msgHash = keccak256(encodedMessage);
        require(!relayerMessageHashes[msgHash], "VaultfireBridge: relayer message replayed");
        relayerMessageHashes[msgHash] = true;

        // [audit-v3.3] CRIT-02 fix — verify ECDSA signature from remoteBridgeSigner.
        // We follow EIP-191 personal_sign: the signed digest is
        //   keccak256("\x19Ethereum Signed Message:\n32" || msgHash)
        // which is the standard produced by eth_sign / signMessage on most wallets
        // and bridge signer implementations.
        require(signature.length == 65, "VaultfireBridge: bad signature length");
        bytes32 ethSignedHash = keccak256(
            abi.encodePacked("\x19Ethereum Signed Message:\n32", msgHash)
        );
        uint8  v;
        bytes32 r;
        bytes32 s;
        assembly {
            // signature is calldata; load via calldataload after skipping the
            // 4-byte selector (already stripped by Solidity's calldata slice).
            // Use the standard 65-byte (r,s,v) layout.
            r := calldataload(signature.offset)
            s := calldataload(add(signature.offset, 32))
            v := byte(0, calldataload(add(signature.offset, 64)))
        }
        // Normalize v: some signers emit 0/1 instead of 27/28.
        if (v < 27) v += 27;
        address recovered = ecrecover(ethSignedHash, v, r, s);
        require(recovered != address(0) && recovered == remoteBridgeSigner,
            "VaultfireBridge: invalid bridge signature");

        _processInboundMessage(encodedMessage);
    }

    // =====================================================================
    //  VIEW - Query synced state
    // =====================================================================

    function getSyncedAgent(address agent_) external view returns (AgentRegistrationPayload memory) {
        return syncedAgents[agent_];
    }

    function getSyncedPartnershipBond(uint256 bondId) external view returns (PartnershipBondPayload memory) {
        return syncedPartnershipBonds[bondId];
    }

    function getSyncedAccountabilityBond(uint256 bondId) external view returns (AccountabilityBondPayload memory) {
        return syncedAccountabilityBonds[bondId];
    }

    function getSyncedReputation(address agent_) external view returns (ReputationPayload memory) {
        return syncedReputations[agent_];
    }

    function getSyncedValidation(uint256 requestId) external view returns (ValidationPayload memory) {
        return syncedValidations[requestId];
    }

    function getSyncedAgentCount() external view returns (uint256) {
        return syncedAgentList.length;
    }

    function getSyncedPartnershipBondCount() external view returns (uint256) {
        return syncedPartnershipBondIds.length;
    }

    function getSyncedAccountabilityBondCount() external view returns (uint256) {
        return syncedAccountabilityBondIds.length;
    }

    function getSyncedReputationCount() external view returns (uint256) {
        return syncedReputationAgents.length;
    }

    function getSyncedValidationCount() external view returns (uint256) {
        return syncedValidationIds.length;
    }

    /**
     * @notice Check if an agent is recognized on this chain (either native or synced).
     */
    function isAgentRecognized(address agent_) external view returns (bool) {
        return syncedAgents[agent_].agentAddress != address(0);
    }

    // =====================================================================
    //  INTERNAL - Message construction & processing
    // =====================================================================

    function _sendMessage(
        MessageType messageType,
        bytes memory payload
    ) internal returns (uint256 nonce) {
        require(remoteBridgeAddress != address(0), "VaultfireBridge: remote not configured");

        nonce = ++outboundNonce;

        BridgeMessage memory bridgeMsg = BridgeMessage({
            messageType: messageType,
            sourceChainId: block.chainid,
            nonce: nonce,
            timestamp: block.timestamp,
            sender: msg.sender,
            payload: payload
        });

        bytes memory encoded = abi.encode(bridgeMsg);
        bytes32 msgHash = keccak256(encoded);

        // If Teleporter is available, send via native ICM
        if (teleporterMessenger != address(0)) {
            _sendViaTeleporter(encoded);
        }
        // Otherwise the off-chain relayer picks up the event

        emit MessageSent(nonce, messageType, msgHash, remoteChainId);
    }

    function _sendViaTeleporter(bytes memory encoded) internal {
        TeleporterMessageInput memory input = TeleporterMessageInput({
            destinationBlockchainID: remoteBlockchainID,
            destinationAddress: remoteBridgeAddress,
            feeInfo: TeleporterFeeInfo({feeTokenAddress: address(0), amount: 0}),
            requiredGasLimit: requiredGasLimit,
            allowedRelayerAddresses: new address[](0),
            message: encoded
        });

        ITeleporterMessenger(teleporterMessenger).sendCrossChainMessage(input);
    }

    function _processInboundMessage(bytes calldata encoded) internal {
        BridgeMessage memory bridgeMsg = abi.decode(encoded, (BridgeMessage));

        // Replay protection: check message hash
        bytes32 msgHash = keccak256(encoded);
        require(!processedMessages[msgHash], "VaultfireBridge: message already processed");
        processedMessages[msgHash] = true;

        // Nonce ordering check (allow out-of-order but track highest)
        if (bridgeMsg.nonce > lastProcessedNonce) {
            lastProcessedNonce = bridgeMsg.nonce;
        }

        // Source chain verification
        require(bridgeMsg.sourceChainId == remoteChainId, "VaultfireBridge: wrong source chain");

        /// @dev [audit-v3] HIGH-BRIDGE-02: Validate message sender is the authorized remote bridge
        require(bridgeMsg.sender == remoteBridgeAddress, "VaultfireBridge: wrong message sender");

        // Route to type-specific handler
        if (bridgeMsg.messageType == MessageType.SYNC_AGENT_REGISTRATION) {
            _handleAgentRegistration(bridgeMsg);
        } else if (bridgeMsg.messageType == MessageType.SYNC_PARTNERSHIP_BOND) {
            _handlePartnershipBond(bridgeMsg);
        } else if (bridgeMsg.messageType == MessageType.SYNC_ACCOUNTABILITY_BOND) {
            _handleAccountabilityBond(bridgeMsg);
        } else if (bridgeMsg.messageType == MessageType.SYNC_REPUTATION) {
            _handleReputation(bridgeMsg);
        } else if (bridgeMsg.messageType == MessageType.SYNC_VALIDATION) {
            _handleValidation(bridgeMsg);
        } else if (bridgeMsg.messageType == MessageType.SYNC_BOND_DEACTIVATION) { // [audit-v3.3]
            _handleBondDeactivation(bridgeMsg); // [audit-v3.3]
        } else {
            revert("VaultfireBridge: unknown message type");
        }

        emit MessageReceived(
            bridgeMsg.nonce,
            bridgeMsg.messageType,
            msgHash,
            bridgeMsg.sourceChainId
        );
    }

    // =====================================================================
    //  INTERNAL - Type-specific handlers (capped arrays)
    // =====================================================================

    function _handleAgentRegistration(BridgeMessage memory bridgeMsg) internal {
        AgentRegistrationPayload memory p = abi.decode(bridgeMsg.payload, (AgentRegistrationPayload));
        require(p.agentAddress != address(0), "VaultfireBridge: zero agent address");

        // Store if new; update if existing
        if (syncedAgents[p.agentAddress].agentAddress == address(0)) {
            if (syncedAgentList.length < MAX_SYNCED_ENTRIES) {
                syncedAgentList.push(p.agentAddress);
            } else {
                emit SyncedCapReached("syncedAgentList", MAX_SYNCED_ENTRIES);
            }
        }
        syncedAgents[p.agentAddress] = p;

        emit AgentSynced(p.agentAddress, p.agentType, bridgeMsg.sourceChainId);
    }

    function _handlePartnershipBond(BridgeMessage memory bridgeMsg) internal {
        PartnershipBondPayload memory p = abi.decode(bridgeMsg.payload, (PartnershipBondPayload));

        if (syncedPartnershipBonds[p.bondId].bondId == 0) {
            if (syncedPartnershipBondIds.length < MAX_SYNCED_ENTRIES) {
                syncedPartnershipBondIds.push(p.bondId);
            } else {
                emit SyncedCapReached("syncedPartnershipBondIds", MAX_SYNCED_ENTRIES);
            }
        }
        syncedPartnershipBonds[p.bondId] = p;

        emit PartnershipBondSynced(p.bondId, p.human, p.aiAgent, p.stakeToken, bridgeMsg.sourceChainId);
    }

    function _handleAccountabilityBond(BridgeMessage memory bridgeMsg) internal {
        AccountabilityBondPayload memory p = abi.decode(bridgeMsg.payload, (AccountabilityBondPayload));

        if (syncedAccountabilityBonds[p.bondId].bondId == 0) {
            if (syncedAccountabilityBondIds.length < MAX_SYNCED_ENTRIES) {
                syncedAccountabilityBondIds.push(p.bondId);
            } else {
                emit SyncedCapReached("syncedAccountabilityBondIds", MAX_SYNCED_ENTRIES);
            }
        }
        syncedAccountabilityBonds[p.bondId] = p;

        emit AccountabilityBondSynced(p.bondId, p.aiCompany, p.stakeToken, bridgeMsg.sourceChainId);
    }

    function _handleReputation(BridgeMessage memory bridgeMsg) internal {
        ReputationPayload memory p = abi.decode(bridgeMsg.payload, (ReputationPayload));
        require(p.agentAddress != address(0), "VaultfireBridge: zero agent address");

        // [audit-v3] HIGH-BRIDGE-01: Reject stale updates
        if (syncedReputationTimestamp[p.agentAddress] >= p.lastUpdated) return;
        syncedReputationTimestamp[p.agentAddress] = p.lastUpdated;

        if (syncedReputations[p.agentAddress].agentAddress == address(0)) {
            if (syncedReputationAgents.length < MAX_SYNCED_ENTRIES) {
                syncedReputationAgents.push(p.agentAddress);
            } else {
                emit SyncedCapReached("syncedReputationAgents", MAX_SYNCED_ENTRIES);
            }
        }
        syncedReputations[p.agentAddress] = p;

        emit ReputationSynced(p.agentAddress, p.averageRating, bridgeMsg.sourceChainId);
    }

    function _handleValidation(BridgeMessage memory bridgeMsg) internal {
        ValidationPayload memory p = abi.decode(bridgeMsg.payload, (ValidationPayload));

        if (syncedValidations[p.requestId].requestId == 0) {
            if (syncedValidationIds.length < MAX_SYNCED_ENTRIES) {
                syncedValidationIds.push(p.requestId);
            } else {
                emit SyncedCapReached("syncedValidationIds", MAX_SYNCED_ENTRIES);
            }
        }
        syncedValidations[p.requestId] = p;

        emit ValidationSynced(p.requestId, p.agentAddress, p.status, bridgeMsg.sourceChainId);
    }

    // =====================================================================
    //  [audit-v3.3] HIGH-18: Bond Deactivation Notification
    // =====================================================================

    /// @notice [audit-v3.3] HIGH-18: Called by authorized relayers to notify that a partnership bond was deactivated.
    ///         Marks the synced bond as inactive on this chain.
    /// @dev [audit-v3.3-r2] NEW-02 HOTFIX: Added onlyAuthorizedRelayer + whenNotPaused.
    ///      Previously unguarded — any address could falsify bond deactivation state.
    function notifyPartnershipBondDeactivated(uint256 bondId) external onlyAuthorizedRelayer whenNotPaused { // [audit-v3.3-r2] NEW-02 fix
        require(syncedPartnershipBonds[bondId].bondId != 0, "VaultfireBridge: bond not found"); // [audit-v3.3-r2]
        syncedPartnershipBonds[bondId].active = false; // [audit-v3.3]
        emit BondDeactivationSynced(bondId, true, block.chainid); // [audit-v3.3]
    } // [audit-v3.3]

    /// @notice [audit-v3.3] HIGH-18: Called by authorized relayers to notify accountability bond deactivation.
    /// @dev [audit-v3.3-r2] NEW-02 HOTFIX: Added onlyAuthorizedRelayer + whenNotPaused.
    function notifyAccountabilityBondDeactivated(uint256 bondId) external onlyAuthorizedRelayer whenNotPaused { // [audit-v3.3-r2] NEW-02 fix
        require(syncedAccountabilityBonds[bondId].bondId != 0, "VaultfireBridge: bond not found"); // [audit-v3.3-r2]
        syncedAccountabilityBonds[bondId].active = false; // [audit-v3.3]
        emit BondDeactivationSynced(bondId, false, block.chainid); // [audit-v3.3]
    } // [audit-v3.3]

    /// @dev [audit-v3.3] HIGH-18: Handle incoming SYNC_BOND_DEACTIVATION from remote chain.
    function _handleBondDeactivation(BridgeMessage memory bridgeMsg) internal { // [audit-v3.3]
        BondDeactivationPayload memory p = abi.decode(bridgeMsg.payload, (BondDeactivationPayload)); // [audit-v3.3]
        if (p.isPartnership) { // [audit-v3.3]
            if (syncedPartnershipBonds[p.bondId].bondId != 0) { // [audit-v3.3]
                syncedPartnershipBonds[p.bondId].active = false; // [audit-v3.3]
            } // [audit-v3.3]
        } else { // [audit-v3.3]
            if (syncedAccountabilityBonds[p.bondId].bondId != 0) { // [audit-v3.3]
                syncedAccountabilityBonds[p.bondId].active = false; // [audit-v3.3]
            } // [audit-v3.3]
        } // [audit-v3.3]
        emit BondDeactivationSynced(p.bondId, p.isPartnership, bridgeMsg.sourceChainId); // [audit-v3.3]
    } // [audit-v3.3]
}
