// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.26;

import {IExecutor} from "@interfaces/IExecutor.sol";
import {TransferManager} from "../TransferManager.sol";

interface IBopAmmV2 {
    function swap(
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 minAmountOut,
        uint256 expiry,
        address recipient
    ) external payable;
}

error BopAMMExecutor__ZeroSettlementAddress();
error BopAMMExecutor__InvalidDataLength();

contract BopAMMExecutor is IExecutor {
    IBopAmmV2 public immutable settlement;

    constructor(address settlement_) {
        if (settlement_ == address(0)) {
            revert BopAMMExecutor__ZeroSettlementAddress();
        }
        settlement = IBopAmmV2(settlement_);
    }

    function fundsExpectedAddress(
        bytes calldata /* data */
    )
        external
        view
        returns (address receiver)
    {
        return msg.sender;
    }

    // The router enforces the user's minAmountOut and the Dispatcher measures
    // the output via balance diff, so the protocol-level minAmountOut is 0.
    // Settlement requires expiry >= block.timestamp; the swap is only valid in
    // the block whose timestamp matches the book's committed quote anyway.
    // slither-disable-next-line locked-ether
    function swap(uint256 amountIn, bytes calldata data, address receiver)
        external
        payable
    {
        (address tokenIn, address tokenOut) = _decodeData(data);

        settlement.swap(
            tokenIn, tokenOut, amountIn, 0, block.timestamp, receiver
        );
    }

    function getTransferData(bytes calldata data)
        external
        view
        returns (
            TransferManager.TransferType transferType,
            address receiver,
            address tokenIn,
            address tokenOut,
            bool outputToRouter
        )
    {
        (tokenIn, tokenOut) = _decodeData(data);
        transferType = TransferManager.TransferType.ProtocolWillDebit;
        receiver = address(settlement);
        outputToRouter = false;
    }

    function _decodeData(bytes calldata data)
        internal
        pure
        returns (address tokenIn, address tokenOut)
    {
        if (data.length != 40) {
            revert BopAMMExecutor__InvalidDataLength();
        }

        tokenIn = address(bytes20(data[0:20]));
        tokenOut = address(bytes20(data[20:40]));
    }
}
