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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../interfaces/ICurvePool.sol";
import "../RouteTypesV2.sol";
import "../interfaces/IRouteExecutorAdapter.sol";

contract CurveRouteExecutorAdapter is IRouteExecutorAdapter {
    using SafeERC20 for IERC20;

    function validate(
        RouteTypesV2.RouteLeg calldata leg
    ) external pure override returns (bool ok, string memory reason) {
        if (leg.provider != RouteTypesV2.Provider.Curve) {
            return (false, "CurveRouteExecutorAdapter: invalid provider");
        }
        if (leg.target == address(0)) {
            return (false, "CurveRouteExecutorAdapter: zero target");
        }
        if (leg.providerData.length == 0) {
            return (false, "CurveRouteExecutorAdapter: missing providerData");
        }
        return (true, "");
    }

    function quote(
        RouteTypesV2.RouteLeg calldata leg,
        uint256 amountIn
    ) external view override returns (uint256 amountOut, uint256 gasEstimate) {
        (int128 i, int128 j,) = abi.decode(leg.providerData, (int128, int128, bool));
        amountOut = ICurvePool(leg.target).get_dy(i, j, amountIn);
        gasEstimate = 190000;
    }

    function execute(
        RouteTypesV2.RouteLeg calldata leg,
        uint256 amountIn
    ) external override returns (uint256 amountOut) {
        (int128 i, int128 j, bool useUnderlying) = abi.decode(leg.providerData, (int128, int128, bool));
        IERC20(leg.tokenIn).forceApprove(leg.target, 0);
        IERC20(leg.tokenIn).forceApprove(leg.target, amountIn);

        if (useUnderlying) {
            amountOut = ICurvePool(leg.target).exchange_underlying(i, j, amountIn, leg.minAmountOut);
        } else {
            amountOut = ICurvePool(leg.target).exchange(i, j, amountIn, leg.minAmountOut);
        }

        IERC20(leg.tokenOut).safeTransfer(msg.sender, amountOut);
    }
}
