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

import "./IVaultfireIdentity.sol";

/// @title ComplianceRegistry — EU AI Act on-chain compliance attestation for AI agents
/// @author Vaultfire Protocol
/// @notice Stores and verifies compliance attestations for AI agents per the EU AI Act.
///         Maps to EU AI Act requirements: identity, audit trail, risk classification,
///         human oversight, and post-market monitoring. Full enforcement August 2026.
/// @dev Each agent can have multiple compliance attestations from authorized auditors.
contract ComplianceRegistry {
    // ═══════════════════════════════════════════════════
    // ENUMS & STRUCTS
    // ═══════════════════════════════════════════════════

    enum RiskLevel { Minimal, Limited, High, Unacceptable }

    enum ComplianceStatus { Pending, Compliant, NonCompliant, Expired, Revoked }

    struct ComplianceAttestation {
        address agent;                  // Agent being attested
        address auditor;                // Authorized auditor who issued attestation
        RiskLevel riskLevel;            // EU AI Act risk classification
        ComplianceStatus status;        // Current compliance status
        uint256 issuedAt;               // Timestamp when attestation was issued
        uint256 expiresAt;              // Timestamp when attestation expires
        uint256 revokedAt;              // Timestamp when revoked (0 if not revoked)
        string attestationURI;          // IPFS/URI to full compliance documentation
        string framework;              // e.g., "EU_AI_ACT_2026", "NIST_AI_RMF"
        bool humanOversightVerified;    // Whether human oversight mechanism is confirmed
        bool technicalDocVerified;      // Whether technical documentation exists
        bool postMarketMonitoring;      // Whether post-market monitoring is active
        bool incidentReporting;         // Whether incident reporting is configured
    }

    struct AuditorInfo {
        bool authorized;
        uint256 authorizedAt;
        uint256 attestationsIssued;
        string auditorURI;             // IPFS/URI to auditor credentials
    }

    // ═══════════════════════════════════════════════════
    // STATE
    // ═══════════════════════════════════════════════════

    IVaultfireIdentity public immutable identityRegistry;

    address public owner;

    uint256 public totalAttestations;
    uint256 public totalAuditors;
    uint256 public totalCompliantAgents;

    uint256 public constant MIN_ATTESTATION_DURATION = 30 days;
    uint256 public constant MAX_ATTESTATION_DURATION = 730 days; // 2 years

    mapping(uint256 => ComplianceAttestation) public attestations;
    mapping(address => uint256[]) public agentAttestations;
    mapping(address => AuditorInfo) public auditors;
    mapping(address => bool) public isCompliant; // Quick lookup: does agent have valid attestation?

    // ═══════════════════════════════════════════════════
    // EVENTS
    // ═══════════════════════════════════════════════════

    event AttestationIssued(uint256 indexed attestationId, address indexed agent, address indexed auditor, RiskLevel riskLevel, string framework);
    event AttestationRevoked(uint256 indexed attestationId, address indexed agent, address indexed auditor, string reason);
    event AttestationExpired(uint256 indexed attestationId, address indexed agent);
    event AuditorAuthorized(address indexed auditor, string auditorURI);
    event AuditorRevoked(address indexed auditor);
    event ComplianceStatusChanged(address indexed agent, bool isCompliant);

    // ═══════════════════════════════════════════════════
    // MODIFIERS
    // ═══════════════════════════════════════════════════

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

    modifier onlyAuthorizedAuditor() {
        require(auditors[msg.sender].authorized, "ComplianceRegistry: caller is not authorized auditor");
        _;
    }

    modifier onlyRegisteredAgent(address agent) {
        (string memory uri, , , ) = identityRegistry.getAgent(agent);
        require(bytes(uri).length > 0, "ComplianceRegistry: agent not registered");
        _;
    }

    // ═══════════════════════════════════════════════════
    // CONSTRUCTOR
    // ═══════════════════════════════════════════════════

    constructor(address _identityRegistry) {
        require(_identityRegistry != address(0), "ComplianceRegistry: zero identity address");
        identityRegistry = IVaultfireIdentity(_identityRegistry);
        owner = msg.sender;
    }

    // ═══════════════════════════════════════════════════
    // ATTESTATION MANAGEMENT
    // ═══════════════════════════════════════════════════

    /// @notice Issue a compliance attestation for an agent
    /// @param agent The agent being attested
    /// @param riskLevel EU AI Act risk classification
    /// @param duration Duration of attestation validity (seconds)
    /// @param attestationURI URI to full compliance documentation
    /// @param framework Compliance framework (e.g., "EU_AI_ACT_2026")
    /// @param humanOversight Whether human oversight mechanism is confirmed
    /// @param technicalDoc Whether technical documentation exists
    /// @param postMarket Whether post-market monitoring is active
    /// @param incident Whether incident reporting is configured
    function issueAttestation(
        address agent,
        RiskLevel riskLevel,
        uint256 duration,
        string calldata attestationURI,
        string calldata framework,
        bool humanOversight,
        bool technicalDoc,
        bool postMarket,
        bool incident
    ) external onlyAuthorizedAuditor onlyRegisteredAgent(agent) {
        require(riskLevel != RiskLevel.Unacceptable, "ComplianceRegistry: unacceptable risk agents cannot be attested");
        require(duration >= MIN_ATTESTATION_DURATION && duration <= MAX_ATTESTATION_DURATION, "ComplianceRegistry: invalid duration");
        require(bytes(attestationURI).length > 0, "ComplianceRegistry: empty attestation URI");
        require(bytes(framework).length > 0, "ComplianceRegistry: empty framework");

        // High-risk agents must have all compliance checks verified
        if (riskLevel == RiskLevel.High) {
            require(humanOversight, "ComplianceRegistry: high-risk requires human oversight");
            require(technicalDoc, "ComplianceRegistry: high-risk requires technical documentation");
            require(postMarket, "ComplianceRegistry: high-risk requires post-market monitoring");
            require(incident, "ComplianceRegistry: high-risk requires incident reporting");
        }

        uint256 attestationId = totalAttestations;
        totalAttestations++;

        attestations[attestationId] = ComplianceAttestation({
            agent: agent,
            auditor: msg.sender,
            riskLevel: riskLevel,
            status: ComplianceStatus.Compliant,
            issuedAt: block.timestamp,
            expiresAt: block.timestamp + duration,
            revokedAt: 0,
            attestationURI: attestationURI,
            framework: framework,
            humanOversightVerified: humanOversight,
            technicalDocVerified: technicalDoc,
            postMarketMonitoring: postMarket,
            incidentReporting: incident
        });

        agentAttestations[agent].push(attestationId);
        auditors[msg.sender].attestationsIssued++;

        // Update compliance status
        if (!isCompliant[agent]) {
            isCompliant[agent] = true;
            totalCompliantAgents++;
            emit ComplianceStatusChanged(agent, true);
        }

        emit AttestationIssued(attestationId, agent, msg.sender, riskLevel, framework);
    }

    /// @notice Revoke an attestation
    /// @param attestationId The attestation to revoke
    /// @param reason Reason for revocation
    function revokeAttestation(uint256 attestationId, string calldata reason) external onlyAuthorizedAuditor {
        ComplianceAttestation storage att = attestations[attestationId];
        require(att.status == ComplianceStatus.Compliant, "ComplianceRegistry: not compliant");
        require(att.auditor == msg.sender || msg.sender == owner, "ComplianceRegistry: not attestation auditor");

        att.status = ComplianceStatus.Revoked;
        att.revokedAt = block.timestamp;

        // Check if agent still has any valid attestation
        _recalculateCompliance(att.agent);

        emit AttestationRevoked(attestationId, att.agent, msg.sender, reason);
    }

    /// @notice Mark an attestation as expired
    /// @param attestationId The attestation to expire
    function expireAttestation(uint256 attestationId) external {
        ComplianceAttestation storage att = attestations[attestationId];
        require(att.status == ComplianceStatus.Compliant, "ComplianceRegistry: not compliant");
        require(block.timestamp >= att.expiresAt, "ComplianceRegistry: not expired yet");

        att.status = ComplianceStatus.Expired;
        _recalculateCompliance(att.agent);

        emit AttestationExpired(attestationId, att.agent);
    }

    // ═══════════════════════════════════════════════════
    // INTERNAL
    // ═══════════════════════════════════════════════════

    /// @notice Recalculate if agent is still compliant after revocation/expiry
    function _recalculateCompliance(address agent) internal {
        uint256[] storage ids = agentAttestations[agent];
        bool hasValid = false;

        for (uint256 i = 0; i < ids.length; i++) {
            ComplianceAttestation storage att = attestations[ids[i]];
            if (att.status == ComplianceStatus.Compliant && block.timestamp < att.expiresAt) {
                hasValid = true;
                break;
            }
        }

        if (isCompliant[agent] && !hasValid) {
            isCompliant[agent] = false;
            totalCompliantAgents--;
            emit ComplianceStatusChanged(agent, false);
        }
    }

    // ═══════════════════════════════════════════════════
    // VIEW FUNCTIONS
    // ═══════════════════════════════════════════════════

    /// @notice Check if an agent is currently compliant
    function checkCompliance(address agent) external view returns (bool compliant, uint256 activeAttestations) {
        uint256[] storage ids = agentAttestations[agent];
        uint256 active = 0;
        bool hasValid = false;

        for (uint256 i = 0; i < ids.length; i++) {
            ComplianceAttestation storage att = attestations[ids[i]];
            if (att.status == ComplianceStatus.Compliant && block.timestamp < att.expiresAt) {
                hasValid = true;
                active++;
            }
        }

        return (hasValid, active);
    }

    /// @notice Get all attestation IDs for an agent
    function getAgentAttestations(address agent) external view returns (uint256[] memory) {
        return agentAttestations[agent];
    }

    /// @notice Get compliance registry stats
    function getComplianceStats() external view returns (
        uint256 _totalAttestations,
        uint256 _totalAuditors,
        uint256 _totalCompliantAgents
    ) {
        return (totalAttestations, totalAuditors, totalCompliantAgents);
    }

    // ═══════════════════════════════════════════════════
    // AUDITOR MANAGEMENT
    // ═══════════════════════════════════════════════════

    /// @notice Authorize an auditor to issue compliance attestations
    function authorizeAuditor(address auditor, string calldata auditorURI) external onlyOwner {
        require(auditor != address(0), "ComplianceRegistry: zero auditor");
        require(!auditors[auditor].authorized, "ComplianceRegistry: already authorized");

        auditors[auditor] = AuditorInfo({
            authorized: true,
            authorizedAt: block.timestamp,
            attestationsIssued: 0,
            auditorURI: auditorURI
        });
        totalAuditors++;

        emit AuditorAuthorized(auditor, auditorURI);
    }

    /// @notice Revoke an auditor's authorization
    function revokeAuditor(address auditor) external onlyOwner {
        require(auditors[auditor].authorized, "ComplianceRegistry: not authorized");
        auditors[auditor].authorized = false;
        totalAuditors--;
        emit AuditorRevoked(auditor);
    }

    /// @notice Transfer ownership
    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "ComplianceRegistry: zero owner");
        owner = newOwner;
    }
}
