contract SushiswapSwap {
    address public weth;
    address public sushiswap;
    IERC20 public outputToken;
    address public swapOutputDestinationAddress;
    address public swapController;
    address public pairAddress;
    uint256 public maxSlippagePercentageBasisPoints;

    constructor(
        address _outputTokenAddress,
        address _swapOutputDestinationAddress,
        address _swapController,
        address _pairAddress,
        address _wethAddress,
        address _sushiswapAddress,
        uint256 _maxSlippagePercentageBasisPoints
    ) {
        outputToken = IERC20(_outputTokenAddress);
        swapOutputDestinationAddress = _swapOutputDestinationAddress;
        swapController = _swapController;
        pairAddress = _pairAddress;
        weth = _wethAddress;
        sushiswap = _sushiswapAddress;
        IERC20(weth).approve(_sushiswapAddress, type(uint256).max);
        maxSlippagePercentageBasisPoints = _maxSlippagePercentageBasisPoints;
    }

    modifier onlySwapController() {
        require(msg.sender == swapController, "Only swapController may call this function.");
        _;
    }

    function swap() public {
        IWETH(weth).deposit{value: address(this).balance}();
        uint256 amountIn = IERC20(weth).balanceOf(address(this));

        address[] memory path = new address[](2);
        path[0] = weth;
        path[1] = address(outputToken);
        uint256 minimumAcceptableBuyAmount = getMinAcceptableBuyAmount();

        Sushiswap(sushiswap).swapExactTokensForTokens(
            amountIn,
            minimumAcceptableBuyAmount,
            path,
            swapOutputDestinationAddress,
            block.timestamp
        );
    }

    function getMinAcceptableBuyAmount() public view returns(uint256) {
        IUniswapV2Pair pair = IUniswapV2Pair(pairAddress);
        (uint Res0, uint Res1,) = pair.getReserves();
        uint res0 = Res0*(10**18);
        uint256 price = address(this).balance / (res0/Res1);
        uint256 minimumAcceptableBuy = ((price * (10000 + maxSlippagePercentageBasisPoints))*(10**18)) / 10000;
        return minimumAcceptableBuy*(10**18);
    }

    function setMaxSlippagePercentageBasisPoints(uint256 _maxSlippagePercentageBasisPoints) public onlySwapController {
        require(_maxSlippagePercentageBasisPoints > 0, "_maxSlippagePercentageBasisPoints must be more than zero");
        maxSlippagePercentageBasisPoints = _maxSlippagePercentageBasisPoints;
    }

    receive() external payable {}

    // Failure case fallback functions below

    function fallbackSwap(uint256 _amountOutMin) public onlySwapController {
        IWETH(weth).deposit{value: address(this).balance}();
        uint256 amountIn = IERC20(weth).balanceOf(address(this));

        address[] memory path = new address[](2);
        path[0] = weth;
        path[1] = address(outputToken);

        Sushiswap(sushiswap).swapExactTokensForTokens(
            amountIn,
            _amountOutMin,
            path,
            swapOutputDestinationAddress,
            block.timestamp
        );
    }

    function emergencyExit() public onlySwapController {
        (bool success, ) = swapController.call{value: address(this).balance}("");
        require(success, "Emergency exit failed.");
    }

    function emergencyExitToEndpoint(address endpoint) public onlySwapController {
        (bool success, ) = endpoint.call{value: address(this).balance}("");
        require(success, "Emergency exit to endpoint failed.");
    }

}
function fallbackSwap(uint256 _amountOutMin) public onlySwapController {
    IWETH(weth).deposit{value: address(this).balance}();
    uint256 amountIn = IERC20(weth).balanceOf(address(this));

    address[] memory path = new address[](2);
    path[0] = weth;
    path[1] = address(outputToken);

    Sushiswap(sushiswap).swapExactTokensForTokens(
        amountIn,
        _amountOutMin,
        path,
        swapOutputDestinationAddress,
        block.timestamp
    );
}

// Failure case fallback functions below