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

/**
 * @title VaultfireCapabilityCredentials
 * @author Vaultfire Protocol
 * @notice On-chain proof that an agent can actually do what it claims. Agents earn
 *         verified capability credentials through proven task completions in specific
 *         skill areas. When routing tasks, agents with verified credentials get
 *         priority over self-reported capabilities.
 * @dev Self-contained — no OpenZeppelin imports. Manual ReentrancyGuard included.
 *      Designed to be deployed on Base, Avalanche, Arbitrum, and Polygon.
 *
 *      Credential issuers are verified addresses (the deployer/governance or
 *      approved issuers) who confirm an agent demonstrated a capability.
 */

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

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

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

contract VaultfireCapabilityCredentials {

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

    struct Credential {
        uint256 id;
        address agent;              // Agent who holds the credential
        bytes32 capabilityHash;     // keccak256 of capability name
        address issuedBy;           // Who issued the credential
        uint256 issuedAt;
        uint256 expiresAt;          // 0 = never expires
        uint256 taskProofCount;     // Number of tasks proving this capability
        bool active;
    }

    struct AgentCredentialProfile {
        uint256 totalCredentials;
        uint256 activeCredentials;
        uint256 totalTaskProofs;
    }

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

    IIdentityRegistry_CC public immutable identityRegistry;
    address public immutable owner;

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

    uint256 private _credentialCounter;
    mapping(uint256 => Credential) private _credentials;
    mapping(address => AgentCredentialProfile) public agentProfiles;

    /// @dev Agent => capability hash => credential ID (0 = none)
    mapping(address => mapping(bytes32 => uint256)) public agentCapability;

    /// @dev Agent => list of credential IDs
    mapping(address => uint256[]) private _agentCredentials;

    /// @dev Approved credential issuers
    mapping(address => bool) public approvedIssuers;

    /// @dev Capability hash => list of agents with that credential
    mapping(bytes32 => address[]) private _capabilityHolders;

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

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

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

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

    event CredentialIssued(
        uint256 indexed credentialId,
        address indexed agent,
        bytes32 indexed capabilityHash,
        address issuedBy,
        uint256 expiresAt,
        uint256 timestamp
    );

    event CredentialRevoked(
        uint256 indexed credentialId,
        address indexed agent,
        bytes32 indexed capabilityHash,
        uint256 timestamp
    );

    event TaskProofAdded(
        uint256 indexed credentialId,
        address indexed agent,
        bytes32 indexed capabilityHash,
        uint256 newProofCount,
        uint256 timestamp
    );

    event IssuerApproved(address indexed issuer, uint256 timestamp);
    event IssuerRemoved(address indexed issuer, uint256 timestamp);

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

    constructor(address identityRegistry_) {
        require(identityRegistry_ != address(0), "VaultfireCapabilityCredentials: zero identityRegistry");
        identityRegistry = IIdentityRegistry_CC(identityRegistry_);
        owner = msg.sender;
        // Owner is always an approved issuer
        approvedIssuers[msg.sender] = true;
    }

    // -----------------------------------------------------------------------
    //  Modifiers
    // -----------------------------------------------------------------------

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

    modifier onlyIssuer() {
        require(
            approvedIssuers[msg.sender],
            "VaultfireCapabilityCredentials: not an approved issuer"
        );
        _;
    }

    // -----------------------------------------------------------------------
    //  Admin functions
    // -----------------------------------------------------------------------

    function approveIssuer(address issuer) external onlyOwner {
        require(issuer != address(0), "VaultfireCapabilityCredentials: zero address");
        approvedIssuers[issuer] = true;
        emit IssuerApproved(issuer, block.timestamp);
    }

    function removeIssuer(address issuer) external onlyOwner {
        approvedIssuers[issuer] = false;
        emit IssuerRemoved(issuer, block.timestamp);
    }

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

    /**
     * @notice Issue a capability credential to an agent.
     * @param agent The agent receiving the credential.
     * @param capabilityHash keccak256 of the capability name (e.g., "data-analysis").
     * @param expiresAt Expiration timestamp (0 for permanent).
     * @return credentialId The newly created credential ID.
     */
    function issueCredential(
        address agent,
        bytes32 capabilityHash,
        uint256 expiresAt
    )
        external
        nonReentrant
        onlyIssuer
        returns (uint256 credentialId)
    {
        require(agent != address(0), "VaultfireCapabilityCredentials: zero agent");
        require(capabilityHash != bytes32(0), "VaultfireCapabilityCredentials: zero capability");
        require(
            identityRegistry.isAgentActive(agent),
            "VaultfireCapabilityCredentials: agent not registered"
        );
        require(
            expiresAt == 0 || expiresAt > block.timestamp,
            "VaultfireCapabilityCredentials: expiry in the past"
        );

        // Check no active credential for this capability exists
        uint256 existingId = agentCapability[agent][capabilityHash];
        if (existingId != 0) {
            Credential storage existing = _credentials[existingId];
            require(
                !existing.active ||
                (existing.expiresAt > 0 && existing.expiresAt <= block.timestamp),
                "VaultfireCapabilityCredentials: active credential already exists"
            );
            // Deactivate the expired one
            if (existing.active) {
                existing.active = false;
                agentProfiles[agent].activeCredentials -= 1;
            }
        }

        unchecked { credentialId = ++_credentialCounter; }

        _credentials[credentialId] = Credential({
            id: credentialId,
            agent: agent,
            capabilityHash: capabilityHash,
            issuedBy: msg.sender,
            issuedAt: block.timestamp,
            expiresAt: expiresAt,
            taskProofCount: 0,
            active: true
        });

        agentCapability[agent][capabilityHash] = credentialId;
        _agentCredentials[agent].push(credentialId);
        _capabilityHolders[capabilityHash].push(agent);

        AgentCredentialProfile storage profile = agentProfiles[agent];
        profile.totalCredentials += 1;
        profile.activeCredentials += 1;

        emit CredentialIssued(
            credentialId,
            agent,
            capabilityHash,
            msg.sender,
            expiresAt,
            block.timestamp
        );
    }

    /**
     * @notice Add a task completion proof to an existing credential.
     * @dev Only approved issuers can add proofs.
     * @param credentialId The credential to add proof to.
     */
    function addTaskProof(uint256 credentialId)
        external
        nonReentrant
        onlyIssuer
    {
        require(
            credentialId > 0 && credentialId <= _credentialCounter,
            "VaultfireCapabilityCredentials: credential does not exist"
        );

        Credential storage cred = _credentials[credentialId];
        require(cred.active, "VaultfireCapabilityCredentials: credential not active");
        require(
            cred.expiresAt == 0 || cred.expiresAt > block.timestamp,
            "VaultfireCapabilityCredentials: credential expired"
        );

        cred.taskProofCount += 1;
        agentProfiles[cred.agent].totalTaskProofs += 1;

        emit TaskProofAdded(
            credentialId,
            cred.agent,
            cred.capabilityHash,
            cred.taskProofCount,
            block.timestamp
        );
    }

    /**
     * @notice Revoke a credential.
     * @dev Only the issuer or owner can revoke.
     * @param credentialId The credential to revoke.
     */
    function revokeCredential(uint256 credentialId)
        external
        nonReentrant
    {
        require(
            credentialId > 0 && credentialId <= _credentialCounter,
            "VaultfireCapabilityCredentials: credential does not exist"
        );

        Credential storage cred = _credentials[credentialId];
        require(cred.active, "VaultfireCapabilityCredentials: credential not active");
        require(
            msg.sender == cred.issuedBy || msg.sender == owner,
            "VaultfireCapabilityCredentials: only issuer or owner can revoke"
        );

        cred.active = false;
        agentCapability[cred.agent][cred.capabilityHash] = 0;
        agentProfiles[cred.agent].activeCredentials -= 1;

        emit CredentialRevoked(credentialId, cred.agent, cred.capabilityHash, block.timestamp);
    }

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

    function getCredential(uint256 credentialId) external view returns (Credential memory) {
        require(
            credentialId > 0 && credentialId <= _credentialCounter,
            "VaultfireCapabilityCredentials: credential does not exist"
        );
        return _credentials[credentialId];
    }

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

    function getCapabilityHolders(bytes32 capabilityHash) external view returns (address[] memory) {
        return _capabilityHolders[capabilityHash];
    }

    function hasActiveCredential(address agent, bytes32 capabilityHash) external view returns (bool) {
        uint256 credId = agentCapability[agent][capabilityHash];
        if (credId == 0) return false;
        Credential memory cred = _credentials[credId];
        return cred.active && (cred.expiresAt == 0 || cred.expiresAt > block.timestamp);
    }

    function getAgentProfile(address agent)
        external
        view
        returns (
            uint256 totalCredentials,
            uint256 activeCredentials,
            uint256 totalTaskProofs
        )
    {
        AgentCredentialProfile memory p = agentProfiles[agent];
        return (p.totalCredentials, p.activeCredentials, p.totalTaskProofs);
    }

    function credentialCount() external view returns (uint256) {
        return _credentialCounter;
    }
}
