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

/**
 * @title Chain138ParticipantSurface
 * @notice Surfaces Chain 138 payment activity on each participant's mainnet address history.
 * @dev Emits indexed events (Etherscan Events tab on participant via log filters) and optional
 *      zero-value wallet touch (Internal Transactions tab). Top-level 0 ETH txs are sent
 *      separately by the relayer when CHECKPOINT_SURFACE_TOPLEVEL_ZERO_ETH=1 (aggregator).
 */
contract Chain138ParticipantSurface {
    uint8 public constant DIRECTION_CREDITED = 0;
    uint8 public constant DIRECTION_DEBITED = 1;

    address public admin;
    bool public paused;
    bool public walletTouchEnabled;

    struct Notification {
        address participant;
        bytes32 chain138TxHash;
        bytes32 instructionId;
        uint8 direction;
        uint64 batchId;
        uint256 valueWei;
        uint64 valueUsdE8;
    }

    mapping(bytes32 => bool) public notified;

    event AdminChanged(address indexed newAdmin);
    event WalletTouchEnabled(bool enabled);
    event Paused();
    event Unpaused();

    /// @notice Indexed for Etherscan log lookup on participant address (topic1).
    event Chain138ActivityNotified(
        address indexed participant,
        bytes32 indexed chain138TxHash,
        bytes32 indexed instructionId,
        uint8 direction,
        uint64 batchId,
        uint256 valueWei,
        uint64 valueUsdE8,
        bool walletTouched
    );

    event BatchNotificationsSurfaced(uint64 indexed batchId, uint256 count);

    modifier onlyAdmin() {
        require(msg.sender == admin, "only admin");
        _;
    }

    modifier whenNotPaused() {
        require(!paused, "paused");
        _;
    }

    constructor(address _admin) {
        require(_admin != address(0), "zero admin");
        admin = _admin;
        walletTouchEnabled = true;
    }

    function setAdmin(address newAdmin) external onlyAdmin {
        require(newAdmin != address(0), "zero admin");
        admin = newAdmin;
        emit AdminChanged(newAdmin);
    }

    function setWalletTouchEnabled(bool enabled) external onlyAdmin {
        walletTouchEnabled = enabled;
        emit WalletTouchEnabled(enabled);
    }

    function pause() external onlyAdmin {
        paused = true;
        emit Paused();
    }

    function unpause() external onlyAdmin {
        paused = false;
        emit Unpaused();
    }

    function notificationKey(
        address participant,
        bytes32 chain138TxHash,
        uint8 direction
    ) public pure returns (bytes32) {
        return keccak256(abi.encodePacked(participant, chain138TxHash, direction));
    }

    /**
     * @notice Record Chain 138 activity for each participant role (credit + debit) in one mainnet tx.
     * @param batchId Checkpoint batch id (for correlation)
     * @param items Parallel notifications (typically 2 per Chain 138 tx: debited from, credited to)
     */
    function notifyBatch(uint64 batchId, Notification[] calldata items) external onlyAdmin whenNotPaused {
        uint256 n = items.length;
        for (uint256 i = 0; i < n; i++) {
            Notification calldata item = items[i];
            require(item.participant != address(0), "zero participant");
            require(item.chain138TxHash != bytes32(0), "zero hash");

            bytes32 key = notificationKey(item.participant, item.chain138TxHash, item.direction);
            if (notified[key]) continue;
            notified[key] = true;

            bool touched = false;
            if (walletTouchEnabled) {
                (bool ok,) = item.participant.call{value: 0}(
                    abi.encodeWithSignature(
                        "chain138Attestation(bytes32,uint8,uint64)",
                        item.chain138TxHash,
                        item.direction,
                        batchId
                    )
                );
                touched = ok;
            }

            emit Chain138ActivityNotified(
                item.participant,
                item.chain138TxHash,
                item.instructionId,
                item.direction,
                batchId,
                item.valueWei,
                item.valueUsdE8,
                touched
            );
        }
        emit BatchNotificationsSurfaced(batchId, n);
    }
}
