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

import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

/**
 * @title ISO20022IntakeGateway
 * @notice On-chain entry for canonical ISO 20022 / MT-103 mapped instructions (idempotent by instructionId).
 * @dev Full MX/MT payloads remain off-chain; keyed by payloadHash. See SMART_CONTRACTS_ISO20022_FIN_METHODOLOGY.md
 */
contract ISO20022IntakeGateway is AccessControl {
    bytes32 public constant RELAYER_ROLE = keccak256("RELAYER_ROLE");

    struct CanonicalMessage {
        bytes32 instructionId;
        bytes32 endToEndIdHash;
        bytes32 uetr;
        bytes32 payloadHash;
        bytes32 debtorRefHash;
        bytes32 creditorRefHash;
        bytes32 purposeHash;
        bytes32 authAttestationHash;
        uint8 msgTypeCode;
        address token;
        uint256 amount;
        bytes3 currencyCode;
    }

    mapping(bytes32 => bool) public processedInstructions;
    mapping(bytes32 => CanonicalMessage) public inboundMessages;

    event InboundAccepted(
        bytes32 indexed instructionId,
        bytes32 indexed uetr,
        bytes32 payloadHash,
        bytes32 authAttestationHash,
        uint8 msgTypeCode,
        address token,
        uint256 amount,
        bytes3 currencyCode,
        address indexed relayer
    );

    event CCIPRouterUpdated(address router);

    address public ccipRouter;

    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(RELAYER_ROLE, admin);
    }

    function setCCIPRouter(address router) external onlyRole(DEFAULT_ADMIN_ROLE) {
        ccipRouter = router;
        emit CCIPRouterUpdated(router);
    }

    function submitInbound(CanonicalMessage calldata m) external onlyRole(RELAYER_ROLE) {
        require(m.instructionId != bytes32(0), "instructionId");
        require(m.payloadHash != bytes32(0), "payloadHash");
        require(!processedInstructions[m.instructionId], "duplicate instruction");
        processedInstructions[m.instructionId] = true;
        inboundMessages[m.instructionId] = m;
        emit InboundAccepted(
            m.instructionId,
            m.uetr,
            m.payloadHash,
            m.authAttestationHash,
            m.msgTypeCode,
            m.token,
            m.amount,
            m.currencyCode,
            msg.sender
        );
    }
}
