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

import "../ccip/IRouterClient.sol";

/**
 * @title CCIPTxReporter
 * @notice Sends Chain-138 transaction reports to Ethereum Mainnet via Chainlink CCIP
 * @dev Encodes batch data for CCIPLogger.ccipReceive
 */
contract CCIPTxReporter {
    IRouterClient public immutable router;
    uint64 public destChainSelector;
    address public destReceiver;
    address public owner;

    event SingleTxReported(bytes32 indexed txHash, address from, address to, uint256 value);
    event BatchReported(bytes32 indexed batchId, uint256 count);

    modifier onlyOwner() {
        require(msg.sender == owner, "CCIPTxReporter: only owner");
        _;
    }

    constructor(
        address _router,
        uint64 _destChainSelector,
        address _destReceiver
    ) {
        require(_router != address(0), "CCIPTxReporter: zero router");
        require(_destReceiver != address(0), "CCIPTxReporter: zero receiver");
        router = IRouterClient(payable(_router));
        destChainSelector = _destChainSelector;
        destReceiver = _destReceiver;
        owner = msg.sender;
    }

    /**
     * @notice Report a single transaction to the CCIPLogger on destination chain
     */
    function reportTx(
        bytes32 txHash,
        address from,
        address to,
        uint256 value,
        bytes calldata extraData
    ) external payable returns (bytes32 messageId) {
        bytes32[] memory txHashes = new bytes32[](1);
        address[] memory froms = new address[](1);
        address[] memory tos = new address[](1);
        uint256[] memory values = new uint256[](1);
        txHashes[0] = txHash;
        froms[0] = from;
        tos[0] = to;
        values[0] = value;
        messageId = _reportBatch(txHash, txHashes, froms, tos, values, extraData);
        emit SingleTxReported(txHash, from, to, value);
    }

    /**
     * @notice Report a batch of transactions to the CCIPLogger on destination chain
     */
    function reportBatch(
        bytes32 batchId,
        bytes32[] calldata txHashes,
        address[] calldata froms,
        address[] calldata tos,
        uint256[] calldata values,
        bytes calldata extraData
    ) external payable returns (bytes32 messageId) {
        messageId = _reportBatch(batchId, txHashes, froms, tos, values, extraData);
        emit BatchReported(batchId, txHashes.length);
    }

    function _reportBatch(
        bytes32 batchId,
        bytes32[] memory txHashes,
        address[] memory froms,
        address[] memory tos,
        uint256[] memory values,
        bytes memory extraData
    ) internal returns (bytes32 messageId) {
        bytes memory data = abi.encode(
            batchId,
            txHashes,
            froms,
            tos,
            values,
            extraData
        );
        IRouterClient.EVM2AnyMessage memory message = IRouterClient.EVM2AnyMessage({
            receiver: abi.encode(destReceiver),
            data: data,
            tokenAmounts: new IRouterClient.TokenAmount[](0),
            feeToken: address(0),
            extraArgs: ""
        });
        (messageId, ) = router.ccipSend{value: msg.value}(destChainSelector, message);
    }

    /**
     * @notice Estimate fee for sending a batch
     */
    function estimateFee(
        bytes32[] calldata txHashes,
        address[] calldata froms,
        address[] calldata tos,
        uint256[] calldata values
    ) external view returns (uint256 fee) {
        // Use block.difficulty for London EVM compatibility (prevrandao is Paris+)
        bytes32 batchId = keccak256(abi.encodePacked(block.timestamp, block.difficulty, txHashes.length));
        bytes memory data = abi.encode(
            batchId,
            txHashes,
            froms,
            tos,
            values,
            bytes("")
        );
        IRouterClient.EVM2AnyMessage memory message = IRouterClient.EVM2AnyMessage({
            receiver: abi.encode(destReceiver),
            data: data,
            tokenAmounts: new IRouterClient.TokenAmount[](0),
            feeToken: address(0),
            extraArgs: ""
        });
        return router.getFee(destChainSelector, message);
    }

    function setDestReceiver(address _destReceiver) external onlyOwner {
        require(_destReceiver != address(0), "CCIPTxReporter: zero address");
        destReceiver = _destReceiver;
    }

    function transferOwnership(address newOwner) external onlyOwner {
        require(newOwner != address(0), "CCIPTxReporter: zero address");
        owner = newOwner;
    }
}
