// 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/IAggregationRouter.sol";
import "../RouteTypesV2.sol";
import "../interfaces/IRouteExecutorAdapter.sol";

contract OneInchRouteExecutorAdapter 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.OneInch) {
            return (false, "OneInchRouteExecutorAdapter: invalid provider");
        }
        if (leg.target == address(0)) {
            return (false, "OneInchRouteExecutorAdapter: zero target");
        }
        if (leg.providerData.length == 0) {
            return (false, "OneInchRouteExecutorAdapter: missing providerData");
        }
        return (true, "");
    }

    function quote(
        RouteTypesV2.RouteLeg calldata,
        uint256
    ) external pure override returns (uint256 amountOut, uint256 gasEstimate) {
        return (0, 320000);
    }

    function execute(
        RouteTypesV2.RouteLeg calldata leg,
        uint256 amountIn
    ) external override returns (uint256 amountOut) {
        (address executor, address allowanceTarget, bytes memory data) =
            abi.decode(leg.providerData, (address, address, bytes));
        address spender = allowanceTarget == address(0) ? leg.target : allowanceTarget;

        IERC20(leg.tokenIn).forceApprove(spender, 0);
        IERC20(leg.tokenIn).forceApprove(spender, amountIn);

        IAggregationRouter.SwapDescription memory desc = IAggregationRouter.SwapDescription({
            srcToken: leg.tokenIn,
            dstToken: leg.tokenOut,
            srcReceiver: address(this),
            dstReceiver: msg.sender,
            amount: amountIn,
            minReturnAmount: leg.minAmountOut,
            flags: 0,
            permit: ""
        });

        (amountOut,) = IAggregationRouter(leg.target).swap(executor, desc, "", data);
    }
}
