// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

import { IERC8004IdentityRegistry } from "./IERC8004IdentityRegistry.sol";

/// @title CanonicalIdentityRegistryV3
/// @notice Minimal, non-upgradeable, self-authenticated identity source for
///         the supported Vaultfire Base V3 mandate path.
/// @dev No administrator, oracle, score, evidence hash, or arbitrary attester
///      can create or mutate an identity. The principal is always msg.sender.
contract CanonicalIdentityRegistryV3 is IERC8004IdentityRegistry {
    bytes32 public constant IDENTITY_SEMANTICS =
        keccak256("Vaultfire.BaseV3.SelfAuthenticatedIdentity.NoBehavioralInputs.v1");

    mapping(uint256 => Agent) private _agents;

    error InvalidIdentity();
    error IdentityExists();
    error NotPrincipal();

    event AgentRegisteredV3(
        uint256 indexed agentId,
        address indexed principal,
        address indexed agentWallet,
        address operator,
        address beneficiary
    );
    event OperatorUpdatedV3(uint256 indexed agentId, address indexed operator);
    event ActiveUpdatedV3(uint256 indexed agentId, bool active);

    function register(uint256 agentId, address agentWallet, address operator, address beneficiary) external {
        if (agentId == 0 || agentWallet == address(0) || operator == address(0) || beneficiary == address(0)) {
            revert InvalidIdentity();
        }
        if (agentId == 0 || _agents[agentId].agentId != 0) revert IdentityExists();
        _agents[agentId] = Agent({
            agentId: agentId,
            agentWallet: agentWallet,
            principal: msg.sender,
            operator: operator,
            beneficiary: beneficiary,
            registeredAt: uint64(block.timestamp),
            active: true
        });
        emit AgentRegisteredV3(agentId, msg.sender, agentWallet, operator, beneficiary);
    }

    function setOperator(uint256 agentId, address operator) external {
        if (operator == address(0)) revert InvalidIdentity();
        Agent storage agent = _agents[agentId];
        if (agent.principal != msg.sender) revert NotPrincipal();
        agent.operator = operator;
        emit OperatorUpdatedV3(agentId, operator);
    }

    function setActive(uint256 agentId, bool active) external {
        Agent storage agent = _agents[agentId];
        if (agent.principal != msg.sender) revert NotPrincipal();
        agent.active = active;
        emit ActiveUpdatedV3(agentId, active);
    }

    function getAgent(uint256 agentId) external view returns (Agent memory) {
        return _agents[agentId];
    }

    function isAuthorizedOperator(uint256 agentId, address caller) external view returns (bool) {
        Agent storage agent = _agents[agentId];
        return agent.active && (caller == agent.operator || caller == agent.agentWallet || caller == agent.principal);
    }

    function isActive(uint256 agentId) external view returns (bool) {
        return _agents[agentId].active;
    }
}
