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

/**
 * @title VaultfireTrustOracle
 * @author Vaultfire Protocol
 * @notice Verifiable off-chain performance attestations. Task requestors submit
 *         signed attestations of task quality back on-chain, building a verifiable
 *         performance history — not just "this agent is bonded" but "this agent
 *         completed 47 tasks with a 96% satisfaction rate."
 * @dev Self-contained — no OpenZeppelin imports. Manual ReentrancyGuard included.
 *      Designed to be deployed on Base, Avalanche, Arbitrum, and Polygon.
 */

// ---------------------------------------------------------------------------
//  Minimal interfaces
// ---------------------------------------------------------------------------

interface IIdentityRegistry_TO {
    function isAgentActive(address agent) external view returns (bool);
}

// ---------------------------------------------------------------------------
//  Contract
// ---------------------------------------------------------------------------

contract VaultfireTrustOracle {

    // -----------------------------------------------------------------------
    //  Types
    // -----------------------------------------------------------------------

    struct Attestation {
        uint256 id;
        address attester;       // Who submitted the attestation (task poster)
        address agent;          // Agent being attested
        uint256 taskId;         // Reference to task (0 if standalone)
        uint256 qualityScore;   // 0-10000 (basis points, 10000 = perfect)
        bytes32 categoryHash;   // keccak256 of capability category
        bytes32 evidenceHash;   // keccak256 of off-chain evidence URI
        uint256 timestamp;
        bool verified;          // True if attester is a registered agent
    }

    struct AgentPerformance {
        uint256 totalAttestations;
        uint256 averageQuality;     // 0-10000
        uint256 verifiedAttestations;
        uint256 taskCompletions;    // Attestations linked to tasks
        uint256 lastUpdated;
    }

    // -----------------------------------------------------------------------
    //  Constants
    // -----------------------------------------------------------------------

    /// @notice Max feedbacks for true running average before switching to EMA
    uint256 private constant MAX_FOR_TRUE_AVG = 1000;

    // -----------------------------------------------------------------------
    //  Immutables
    // -----------------------------------------------------------------------

    IIdentityRegistry_TO public immutable identityRegistry;

    // -----------------------------------------------------------------------
    //  State
    // -----------------------------------------------------------------------

    uint256 private _attestationCounter;
    mapping(uint256 => Attestation) private _attestations;
    mapping(address => AgentPerformance) public performance;

    /// @dev Agent => attestation IDs received
    mapping(address => uint256[]) private _agentAttestations;

    /// @dev Attester => attestation IDs given
    mapping(address => uint256[]) private _attesterAttestations;

    /// @dev Prevent double-attestation: attester => agent => taskId => bool
    mapping(address => mapping(address => mapping(uint256 => bool))) private _hasAttested;

    // -----------------------------------------------------------------------
    //  Reentrancy guard
    // -----------------------------------------------------------------------

    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED     = 2;
    uint256 private _reentrancyStatus = _NOT_ENTERED;

    modifier nonReentrant() {
        require(_reentrancyStatus != _ENTERED, "VaultfireTrustOracle: reentrant call");
        _reentrancyStatus = _ENTERED;
        _;
        _reentrancyStatus = _NOT_ENTERED;
    }

    // -----------------------------------------------------------------------
    //  Events
    // -----------------------------------------------------------------------

    event AttestationSubmitted(
        uint256 indexed attestationId,
        address indexed attester,
        address indexed agent,
        uint256 qualityScore,
        bytes32 categoryHash,
        uint256 taskId,
        bool verified,
        uint256 timestamp
    );

    event PerformanceUpdated(
        address indexed agent,
        uint256 averageQuality,
        uint256 totalAttestations,
        uint256 timestamp
    );

    // -----------------------------------------------------------------------
    //  Constructor
    // -----------------------------------------------------------------------

    constructor(address identityRegistry_) {
        require(identityRegistry_ != address(0), "VaultfireTrustOracle: zero identityRegistry");
        identityRegistry = IIdentityRegistry_TO(identityRegistry_);
    }

    // -----------------------------------------------------------------------
    //  Core functions
    // -----------------------------------------------------------------------

    /**
     * @notice Submit a performance attestation for an agent.
     * @param agent The agent being attested.
     * @param taskId Reference task ID (0 for standalone attestation).
     * @param qualityScore Quality rating 0-10000.
     * @param categoryHash keccak256 of the capability category string.
     * @param evidenceHash keccak256 of the off-chain evidence URI.
     * @return attestationId The newly created attestation ID.
     */
    function submitAttestation(
        address agent,
        uint256 taskId,
        uint256 qualityScore,
        bytes32 categoryHash,
        bytes32 evidenceHash
    )
        external
        nonReentrant
        returns (uint256 attestationId)
    {
        require(agent != address(0), "VaultfireTrustOracle: zero agent address");
        require(agent != msg.sender, "VaultfireTrustOracle: cannot attest for self");
        require(qualityScore <= 10000, "VaultfireTrustOracle: score must be 0-10000");
        require(categoryHash != bytes32(0), "VaultfireTrustOracle: category hash required");
        require(
            identityRegistry.isAgentActive(agent),
            "VaultfireTrustOracle: agent not registered"
        );

        // Prevent double-attestation for the same task
        if (taskId > 0) {
            require(
                !_hasAttested[msg.sender][agent][taskId],
                "VaultfireTrustOracle: already attested for this task"
            );
            _hasAttested[msg.sender][agent][taskId] = true;
        }

        bool isVerified = identityRegistry.isAgentActive(msg.sender);

        unchecked { attestationId = ++_attestationCounter; }

        _attestations[attestationId] = Attestation({
            id: attestationId,
            attester: msg.sender,
            agent: agent,
            taskId: taskId,
            qualityScore: qualityScore,
            categoryHash: categoryHash,
            evidenceHash: evidenceHash,
            timestamp: block.timestamp,
            verified: isVerified
        });

        _agentAttestations[agent].push(attestationId);
        _attesterAttestations[msg.sender].push(attestationId);

        _updatePerformance(agent, qualityScore, isVerified, taskId > 0);

        emit AttestationSubmitted(
            attestationId,
            msg.sender,
            agent,
            qualityScore,
            categoryHash,
            taskId,
            isVerified,
            block.timestamp
        );
    }

    // -----------------------------------------------------------------------
    //  Internal
    // -----------------------------------------------------------------------

    function _updatePerformance(
        address agent,
        uint256 newScore,
        bool verified,
        bool isTaskLinked
    ) internal {
        AgentPerformance storage perf = performance[agent];

        // Overflow-safe running average (same pattern as ReputationRegistry)
        if (perf.totalAttestations < MAX_FOR_TRUE_AVG) {
            uint256 totalScore = perf.averageQuality * perf.totalAttestations;
            totalScore += newScore;
            perf.totalAttestations += 1;
            perf.averageQuality = totalScore / perf.totalAttestations;
        } else {
            perf.averageQuality = (perf.averageQuality * 9 + newScore) / 10;
            perf.totalAttestations += 1;
        }

        if (verified) {
            perf.verifiedAttestations += 1;
        }
        if (isTaskLinked) {
            perf.taskCompletions += 1;
        }

        perf.lastUpdated = block.timestamp;

        emit PerformanceUpdated(
            agent,
            perf.averageQuality,
            perf.totalAttestations,
            block.timestamp
        );
    }

    // -----------------------------------------------------------------------
    //  View functions
    // -----------------------------------------------------------------------

    function getAttestation(uint256 attestationId) external view returns (Attestation memory) {
        require(
            attestationId > 0 && attestationId <= _attestationCounter,
            "VaultfireTrustOracle: attestation does not exist"
        );
        return _attestations[attestationId];
    }

    function getPerformance(address agent)
        external
        view
        returns (
            uint256 totalAttestations,
            uint256 averageQuality,
            uint256 verifiedAttestations,
            uint256 taskCompletions,
            uint256 lastUpdated
        )
    {
        AgentPerformance memory p = performance[agent];
        return (
            p.totalAttestations,
            p.averageQuality,
            p.verifiedAttestations,
            p.taskCompletions,
            p.lastUpdated
        );
    }

    function getAgentAttestations(address agent) external view returns (uint256[] memory) {
        return _agentAttestations[agent];
    }

    function getAttesterAttestations(address attester) external view returns (uint256[] memory) {
        return _attesterAttestations[attester];
    }

    function attestationCount() external view returns (uint256) {
        return _attestationCounter;
    }
}
