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

import "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title IndyVerifier
 * @notice Verifies Hyperledger Indy credentials before allowing bridge operations
 * @dev Uses zero-knowledge proofs for privacy-preserving credential verification
 */
contract IndyVerifier is AccessControl {
    bytes32 public constant VERIFIER_ROLE = keccak256("VERIFIER_ROLE");
    bytes32 public constant ISSUER_ROLE = keccak256("ISSUER_ROLE");

    enum CredentialType { 
        KYC, 
        AccreditedInvestor, 
        Institution, 
        Jurisdiction,
        Compliance
    }

    struct CredentialSchema {
        string schemaId;       // Indy schema ID
        string credDefId;      // Credential definition ID
        bool required;
        CredentialType credType;
        uint256 minScore;       // Minimum verification score (0-100)
    }

    struct VerificationResult {
        bool verified;
        uint256 score;
        uint256 verifiedAt;
        string proofId;
    }

    mapping(address => mapping(CredentialType => VerificationResult)) public userCredentials;
    mapping(CredentialType => CredentialSchema) public schemas;
    mapping(string => bool) public verifiedProofs; // proofId => verified

    event CredentialVerified(
        address indexed user,
        CredentialType credType,
        uint256 score,
        string proofId
    );
    
    event CredentialRevoked(
        address indexed user,
        CredentialType credType
    );
    
    event SchemaRegistered(
        CredentialType credType,
        string schemaId,
        string credDefId
    );

    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(VERIFIER_ROLE, admin);
        _grantRole(ISSUER_ROLE, admin);
    }

    /**
     * @notice Register credential schema
     */
    function registerSchema(
        CredentialType credType,
        string calldata schemaId,
        string calldata credDefId,
        bool required,
        uint256 minScore
    ) external onlyRole(ISSUER_ROLE) {
        schemas[credType] = CredentialSchema({
            schemaId: schemaId,
            credDefId: credDefId,
            required: required,
            credType: credType,
            minScore: minScore
        });
        
        emit SchemaRegistered(credType, schemaId, credDefId);
    }

    /**
     * @notice Verify credential proof (called by oracle/agent)
     * @dev Off-chain: Verify ZK proof with Indy
     *      On-chain: Store verification result
     */
    function verifyCredentialProof(
        address user,
        CredentialType credType,
        bytes calldata zkProof,
        uint256 score,
        string calldata proofId
    ) external onlyRole(VERIFIER_ROLE) returns (bool) {
        require(bytes(schemas[credType].schemaId).length > 0, "Schema not registered");
        require(!verifiedProofs[proofId], "Proof already used");
        require(score >= schemas[credType].minScore, "Score too low");
        
        // Mark proof as used (prevent replay)
        verifiedProofs[proofId] = true;
        
        // Store verification result
        userCredentials[user][credType] = VerificationResult({
            verified: true,
            score: score,
            verifiedAt: block.timestamp,
            proofId: proofId
        });
        
        emit CredentialVerified(user, credType, score, proofId);
        
        return true;
    }

    /**
     * @notice Check if user has required credential
     */
    function hasCredential(
        address user,
        CredentialType credType
    ) external view returns (bool) {
        VerificationResult memory result = userCredentials[user][credType];
        if (!result.verified) return false;
        
        CredentialSchema memory schema = schemas[credType];
        return result.score >= schema.minScore;
    }

    /**
     * @notice Get credential verification details
     */
    function getCredentialDetails(
        address user,
        CredentialType credType
    ) external view returns (VerificationResult memory) {
        return userCredentials[user][credType];
    }

    /**
     * @notice Revoke credential (for compliance)
     */
    function revokeCredential(
        address user,
        CredentialType credType
    ) external onlyRole(VERIFIER_ROLE) {
        delete userCredentials[user][credType];
        emit CredentialRevoked(user, credType);
    }

    /**
     * @notice Batch verify credentials (for efficiency)
     */
    function batchVerifyCredentials(
        address[] calldata users,
        CredentialType[] calldata credTypes,
        bytes[] calldata zkProofs,
        uint256[] calldata scores,
        string[] calldata proofIds
    ) external onlyRole(VERIFIER_ROLE) {
        require(
            users.length == credTypes.length &&
            credTypes.length == zkProofs.length &&
            zkProofs.length == scores.length &&
            scores.length == proofIds.length,
            "Array length mismatch"
        );
        
        for (uint256 i = 0; i < users.length; i++) {
            this.verifyCredentialProof(users[i], credTypes[i], zkProofs[i], scores[i], proofIds[i]);
        }
    }
}
