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

import "./ERC8004IdentityRegistryV3.sol";
import "./ERC8004ReputationRegistryV3.sol";
import "./ERC8004ValidationRegistryV3.sol";
import "../AIPartnershipBondsV2.sol";

/**
 * @title VaultFire ERC-8004 Adapter V3
 * @notice Bridges VaultFire's AI Partnership Bonds with ERC-8004 standard
 * @dev V3 — Applies audit fixes: restrict syncPartnershipReputation, remove timestamp
 *      from claimHash, deduplication guard for validation requests, ETH rescue,
 *      remove broken no-arg discoverVaultfireAgents, bounds-check qualityScore,
 *      CEI fix on bondReputationSynced, emit ValidationRequestCreated with requestId.
 *
 * **Mission Alignment:**
 * - Interoperability: VaultFire reputation works across all ERC-8004 platforms
 * - Privacy over surveillance: Only verified partnership data, no personal info
 * - Morals over metrics: Quality partnerships create portable reputation
 * - Freedom over control: Agents own their reputation across ecosystems
 *
 * **V3 Changes (2026-02 Partner-Ready Audit):**
 * - CRITICAL-AD-01: syncPartnershipReputation restricted to bond participants only
 * - HIGH-AD-01: claimHash uses only bondId + qualityScore (no block.timestamp)
 * - HIGH-AD-02: bondValidationRequested deduplication mapping
 * - HIGH-AD-03: receive() + rescueETH() with owner role
 * - MEDIUM-AD-01: Removed broken no-arg discoverVaultfireAgents()
 * - MEDIUM-AD-02: bounds-check qualityScore <= 10000 in _calculatePartnershipRating
 * - CEI fix: bondReputationSynced = true BEFORE external submitVerifiedFeedback call
 * - Enhancement: emit ValidationRequestCreated with the returned requestId
 *
 * @custom:security Inherits security from VaultFire + ERC-8004 V3 contracts
 * @custom:ethics Human-verified reputation only (no surveillance)
 */
contract VaultfireERC8004AdapterV3 {

    AIPartnershipBondsV2 public immutable partnershipBonds;
    ERC8004IdentityRegistryV3 public immutable identityRegistry;
    ERC8004ReputationRegistryV3 public immutable reputationRegistry;
    ERC8004ValidationRegistryV3 public immutable validationRegistry;

    // =========================================================================
    //  Owner role
    // =========================================================================

    address public owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    // =========================================================================
    //  State
    // =========================================================================

    // Track which AI agents are auto-registered
    mapping(address => bool) public autoRegisteredAgents;

    // Bond ID => submitted to reputation registry
    mapping(uint256 => bool) public bondReputationSynced;

    /// @custom:audit-v3 HIGH-AD-02: Deduplication guard for validation requests per bond
    mapping(uint256 => bool) public bondValidationRequested;

    // =========================================================================
    //  Events
    // =========================================================================

    event AgentAutoRegistered(
        address indexed agentAddress,
        string agentType,
        uint256 timestamp
    );

    event PartnershipReputationSynced(
        uint256 indexed bondId,
        address indexed agentAddress,
        uint256 rating,
        uint256 timestamp
    );

    /// @custom:audit-v3 Enhancement: emit with actual requestId from validation registry
    event ValidationRequestCreated(
        uint256 indexed requestId,
        uint256 indexed bondId,
        address indexed agentAddress,
        uint256 timestamp
    );

    // =========================================================================
    //  Constructor
    // =========================================================================

    constructor(
        address _partnershipBonds,
        address _identityRegistry,
        address _reputationRegistry,
        address _validationRegistry
    ) {
        require(_partnershipBonds != address(0), "Invalid partnership bonds");
        require(_identityRegistry != address(0), "Invalid identity registry");
        require(_reputationRegistry != address(0), "Invalid reputation registry");
        require(_validationRegistry != address(0), "Invalid validation registry");

        partnershipBonds = AIPartnershipBondsV2(_partnershipBonds);
        identityRegistry = ERC8004IdentityRegistryV3(_identityRegistry);
        reputationRegistry = ERC8004ReputationRegistryV3(_reputationRegistry);
        validationRegistry = ERC8004ValidationRegistryV3(_validationRegistry);

        owner = msg.sender;
    }

    // =========================================================================
    //  Owner management
    // =========================================================================

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

    // =========================================================================
    //  ETH handling
    // =========================================================================

    /// @custom:audit-v3 HIGH-AD-03: Receive ETH (e.g. for forwarding to validationRegistry)
    receive() external payable {}

    /// @custom:audit-v3 HIGH-AD-03: Emergency ETH rescue — only owner
    function rescueETH() external onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0, "No ETH to rescue");
        (bool success, ) = payable(owner).call{value: balance}("");
        require(success, "ETH rescue failed");
    }

    // =========================================================================
    //  Agent registration
    // =========================================================================

    /**
     * @notice Register agent for VaultFire partnerships
     * @param agentURI URI to agent card (capabilities, protocols, contact)
     * @param agentType Type of AI agent
     * @dev Agent must call identity registry FIRST, then call this function.
     */
    function registerAgentForPartnership(
        string calldata agentURI,
        string calldata agentType
    ) external {
        require(!autoRegisteredAgents[msg.sender], "Already registered for VaultFire");
        require(bytes(agentURI).length > 0, "Agent URI required");
        require(bytes(agentType).length > 0, "Agent type required");

        // Agent must have already called identityRegistry.registerAgent() first,
        // because the identity registry uses msg.sender as the agent address.
        if (!identityRegistry.isAgentActive(msg.sender)) {
            revert("Agent must call identityRegistry.registerAgent() first, then call this function");
        }

        // Track that this agent is registered for VaultFire
        autoRegisteredAgents[msg.sender] = true;

        emit AgentAutoRegistered(msg.sender, agentType, block.timestamp);
    }

    // =========================================================================
    //  Reputation sync
    // =========================================================================

    /**
     * @notice Sync VaultFire partnership verification to ERC-8004 reputation
     * @param bondId VaultFire partnership bond ID
     *
     * @dev CRITICAL-AD-01: Restricted to the bond's human or AI agent only.
     *      Prevents arbitrary callers from triggering reputation writes.
     * @dev CEI fix: bondReputationSynced = true is set BEFORE the external
     *      submitVerifiedFeedback call to prevent reentrancy manipulation.
     */
    /// @custom:audit-v3 CRITICAL-AD-01: Restrict to bond participants (human or aiAgent)
    /// @custom:audit-v3 CEI fix: set bondReputationSynced=true BEFORE external call
    function syncPartnershipReputation(uint256 bondId) external {
        require(!bondReputationSynced[bondId], "Already synced");

        // Get bond data from AIPartnershipBonds
        (
            ,
            address human,
            address aiAgent,
            string memory partnershipType,
            ,
            ,
            ,
            ,
            bool active
        ) = partnershipBonds.bonds(bondId);

        require(active, "Bond not active");

        /// @custom:audit-v3 CRITICAL-AD-01: Only the bond's human or AI agent may sync
        require(
            msg.sender == human || msg.sender == aiAgent,
            "Caller is not a bond participant"
        );

        require(aiAgent != address(0), "Invalid agent address");
        require(identityRegistry.isAgentActive(aiAgent), "Agent not registered in ERC-8004");

        // Calculate partnership quality rating
        uint256 rating = _calculatePartnershipRating(bondId);

        /// @custom:audit-v3 CEI fix: update state BEFORE external call
        bondReputationSynced[bondId] = true;

        // Submit to ERC-8004 Reputation Registry (authorized call — adapter must be set as
        // authorized caller in reputationRegistry by the owner prior to calling this)
        reputationRegistry.submitVerifiedFeedback(
            aiAgent,
            rating,
            partnershipType,
            "", // No off-chain URI needed (data is on VaultFire)
            bondId
        );

        emit PartnershipReputationSynced(bondId, aiAgent, rating, block.timestamp);
    }

    // =========================================================================
    //  Internal rating calculation
    // =========================================================================

    /**
     * @notice Internal: Calculate partnership rating from VaultFire metrics
     * @param bondId Partnership bond ID
     * @return rating Partnership quality rating (0-10000)
     *
     * @dev MEDIUM-AD-02: bounds-check — qualityScore is already 0-10000 per AIPartnershipBonds,
     *      but we cap defensively in case of upstream changes.
     */
    /// @custom:audit-v3 MEDIUM-AD-02: Bounds-check qualityScore <= 10000
    function _calculatePartnershipRating(uint256 bondId) internal view returns (uint256 rating) {
        // Get partnership quality score from VaultFire
        uint256 qualityScore = partnershipBonds.partnershipQualityScore(bondId);

        /// @custom:audit-v3 MEDIUM-AD-02: Cap at 10000 to prevent out-of-range ratings
        require(qualityScore <= 10000, "Quality score out of range");

        // VaultFire quality score is 0-10000, which maps directly to ERC-8004 rating
        return qualityScore;
    }

    // =========================================================================
    //  Validation requests
    // =========================================================================

    /**
     * @notice Request ERC-8004 validation for VaultFire partnership claim
     * @param bondId Partnership bond ID
     * @param claimURI Off-chain URI describing the claim
     * @param validationType Type of validation (ZK proof, multi-validator, etc.)
     *
     * @dev HIGH-AD-01: claimHash uses only bondId + qualityScore (no block.timestamp),
     *      making it deterministic and tamper-evident.
     * @dev HIGH-AD-02: Deduplication — each bond can only have one active validation request.
     * @dev Enhancement: ValidationRequestCreated event emitted with the actual requestId.
     *
     * NOTE: Because ERC8004ValidationRegistryV3.requestValidation() does not return the
     *   requestId, we compute it deterministically as nextRequestId - 1 immediately after
     *   the call. This is safe as long as this call is not interrupted (single tx).
     *   Alternatively, deploy a registry wrapper that returns the requestId.
     */
    /// @custom:audit-v3 HIGH-AD-01: claimHash excludes block.timestamp for determinism
    /// @custom:audit-v3 HIGH-AD-02: bondValidationRequested deduplication
    /// @custom:audit-v3 Enhancement: emit ValidationRequestCreated with requestId
    function requestPartnershipValidation(
        uint256 bondId,
        string calldata claimURI,
        ERC8004ValidationRegistryV3.ValidationType validationType
    ) external payable {
        require(bytes(claimURI).length > 0, "Claim URI required");

        /// @custom:audit-v3 HIGH-AD-02: Prevent duplicate validation requests per bond
        require(!bondValidationRequested[bondId], "Validation already requested for this bond");

        // Get bond data
        (
            ,
            ,
            address aiAgent,
            ,
            ,
            ,
            ,
            ,
            bool active
        ) = partnershipBonds.bonds(bondId);

        require(active, "Bond not active");
        require(aiAgent != address(0), "Invalid agent address");
        require(identityRegistry.isAgentActive(aiAgent), "Agent not registered");

        // Generate claim hash from bond ID and quality score only
        /// @custom:audit-v3 HIGH-AD-01: No block.timestamp in claimHash
        uint256 qualityScore = partnershipBonds.partnershipQualityScore(bondId);
        bytes32 claimHash = keccak256(abi.encodePacked(bondId, qualityScore));

        // Mark as requested BEFORE external call (CEI pattern)
        bondValidationRequested[bondId] = true;

        // Read the next request ID BEFORE the call to compute the emitted ID
        uint256 expectedRequestId = validationRegistry.nextRequestId();

        // Request validation via ERC-8004 Validation Registry V3
        validationRegistry.requestValidation{value: msg.value}(
            aiAgent,
            claimURI,
            claimHash,
            validationType,
            validationType == ERC8004ValidationRegistryV3.ValidationType.MULTI_VALIDATOR ? 3 : 1
        );

        /// @custom:audit-v3 Enhancement: emit event with the requestId we just created
        emit ValidationRequestCreated(expectedRequestId, bondId, aiAgent, block.timestamp);
    }

    // =========================================================================
    //  Cross-platform view functions
    // =========================================================================

    /**
     * @notice Get agent's cross-platform reputation
     * @param agentAddress Address of AI agent
     * @return vaultfireRating Average rating from VaultFire partnerships (placeholder)
     * @return erc8004Rating Average rating from ERC-8004 reputation registry
     * @return totalFeedbacks Total ERC-8004 feedbacks
     * @return verifiedPercentage Percentage of verified feedbacks
     */
    function getAgentCrossPlatformReputation(address agentAddress)
        external
        view
        returns (
            uint256 vaultfireRating,
            uint256 erc8004Rating,
            uint256 totalFeedbacks,
            uint256 verifiedPercentage
        )
    {
        require(agentAddress != address(0), "Invalid agent address");

        // Get VaultFire rating (placeholder — add when AIPartnershipBonds exposes this)
        vaultfireRating = 0;

        // Get ERC-8004 reputation
        (erc8004Rating, totalFeedbacks, , ) = reputationRegistry.getReputation(agentAddress);

        // Get verified percentage
        verifiedPercentage = reputationRegistry.getVerifiedFeedbackPercentage(agentAddress);
    }

    /**
     * @notice Check if agent is registered in both VaultFire and ERC-8004
     * @param agentAddress Address of AI agent
     * @return registeredERC8004 Whether agent is registered in ERC-8004
     * @return registeredVaultFire Whether agent has VaultFire partnerships
     */
    function isAgentFullyRegistered(address agentAddress)
        external
        view
        returns (
            bool registeredERC8004,
            bool registeredVaultFire
        )
    {
        require(agentAddress != address(0), "Invalid agent address");
        registeredERC8004 = identityRegistry.isAgentActive(agentAddress);
        registeredVaultFire = autoRegisteredAgents[agentAddress];
    }

    // =========================================================================
    //  Agent discovery
    // =========================================================================

    /**
     * @notice Discover VaultFire-compatible agents via ERC-8004 events
     * @dev MEDIUM-AD-01: The no-arg version that looked up by base hash (without agentType)
     *      always returned incorrect results and has been removed. Use the typed version
     *      or index AgentRegistered events from the identity registry.
     *
     * @dev HIGH-IR-01 note: In V3, agentsByCapability arrays have been removed from
     *      the identity registry. Discovery now happens via indexed AgentRegistered events
     *      off-chain. This function is provided as a compatibility shim that always returns
     *      an empty array — callers should migrate to event-based discovery.
     *
     * @param agentType The agent type to filter by (e.g., "AI Assistant")
     * @return agents Always returns empty array — use indexed AgentRegistered events instead
     */
    /// @custom:audit-v3 MEDIUM-AD-01: No-arg variant removed; typed variant kept as shim
    function discoverVaultfireAgents(string calldata agentType) external pure returns (address[] memory agents) {
        // V3: agentsByCapability arrays removed from IdentityRegistry to fix HIGH-IR-01/02.
        // Discovery now happens via indexed `AgentRegistered(address indexed agentAddress,
        // ..., bytes32 indexed capabilitiesHash, ...)` events emitted by the identity registry.
        //
        // Off-chain: filter AgentRegistered events where
        //   capabilitiesHash == keccak256(abi.encodePacked("vaultfire-ai-partnership", agentType))
        //
        // This shim is kept to avoid breaking callers at the ABI level.
        agentType; // silence unused parameter warning
        return new address[](0);
    }
}
