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

import {CheckpointExtensionBase} from "./CheckpointExtensionBase.sol";
import {CheckpointStorage} from "../storage/CheckpointStorage.sol";
import {CheckpointLeaf} from "../libraries/CheckpointLeaf.sol";

/// @notice Rejects batches whose leaves include disallowed tokens or native below min.
contract TokenTransferFilterExtension is CheckpointExtensionBase {
    mapping(address => bool) public allowedTokens;
    bool public allowNative = true;
    uint256 public minNativeWei;

    event TokenAllowlistUpdated(address indexed token, bool allowed);

    function HOOK_BEFORE_SUBMIT() external pure override returns (uint32) {
        return 1 << 0;
    }

    function HOOK_AFTER_SUBMIT() external pure override returns (uint32) {
        return 0;
    }

    function HOOK_ON_CCIP() external pure override returns (uint32) {
        return 0;
    }

    function setAllowedToken(address token, bool allowed) external {
        allowedTokens[token] = allowed;
        emit TokenAllowlistUpdated(token, allowed);
    }

    function setAllowNative(bool allowed, uint256 minWei) external {
        allowNative = allowed;
        minNativeWei = minWei;
    }

    function beforeSubmit(CheckpointStorage.CheckpointHeader calldata, bytes calldata data) external view {
        if (data.length == 0) return;
        (CheckpointLeaf.PaymentLeafV2[] memory v2Leaves, bool isV2) = _decodeLeaves(data);
        if (isV2) {
            for (uint256 i = 0; i < v2Leaves.length; i++) {
                CheckpointLeaf.PaymentLeafV2 memory leaf = v2Leaves[i];
                if (leaf.token == address(0)) {
                    require(allowNative, "native disabled");
                    require(leaf.value >= minNativeWei, "native min");
                } else {
                    require(allowedTokens[leaf.token], "token");
                }
            }
            return;
        }
        CheckpointLeaf.PaymentLeafV1[] memory leaves = abi.decode(data, (CheckpointLeaf.PaymentLeafV1[]));
        for (uint256 i = 0; i < leaves.length; i++) {
            require(allowNative, "native only v1");
            require(leaves[i].value >= minNativeWei, "native min");
        }
    }

    function afterSubmit(CheckpointStorage.CheckpointHeader calldata, bytes calldata) external pure override {}

    function onCCIPReceive(bytes calldata) external pure override {}

    function _decodeLeaves(bytes calldata data)
        internal
        pure
        returns (CheckpointLeaf.PaymentLeafV2[] memory v2Leaves, bool isV2)
    {
        if (data.length > 0 && data[0] == bytes1(0x02)) {
            bytes1 version;
            (version, v2Leaves) = abi.decode(data, (bytes1, CheckpointLeaf.PaymentLeafV2[]));
            isV2 = true;
        }
    }
}
