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

import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/// @dev Matches `DODOPMMIntegration.swapExactIn` surface (any registered pool).
interface IDODOStyleSwapExactIn {
    function swapExactIn(address pool, address tokenIn, uint256 amountIn, uint256 minAmountOut)
        external
        returns (uint256 amountOut);
}

/**
 * @title SwapFlashWorkflowBorrower
 * @notice ERC-3156 borrower: flash `token` → swap to `midToken` → swap back to `token` → repay `amount + fee`.
 * @dev `data` must be `abi.encode(SwapFlashParams)`. The caller chooses `integration` — use only trusted DODO/PMM routers.
 *      `pool` is typically the same for both legs (e.g. cUSDT/USDT round-trip). Set mins from off-chain quotes.
 */
contract SwapFlashWorkflowBorrower is IERC3156FlashBorrower {
    using SafeERC20 for IERC20;

    bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");

    address public immutable trustedLender;

    struct SwapFlashParams {
        address integration;
        address pool;
        address midToken;
        uint256 minOutFirst;
        uint256 minOutSecond;
    }

    error UntrustedLender();
    error BadParams();
    error InsufficientToRepay();

    constructor(address trustedLender_) {
        trustedLender = trustedLender_;
    }

    function onFlashLoan(
        address,
        address token,
        uint256 amount,
        uint256 fee,
        bytes calldata data
    ) external override returns (bytes32) {
        if (msg.sender != trustedLender) revert UntrustedLender();
        SwapFlashParams memory p = abi.decode(data, (SwapFlashParams));
        if (p.integration == address(0) || p.pool == address(0) || p.midToken == address(0)) revert BadParams();
        if (p.midToken == token) revert BadParams();

        IERC20 borrowed = IERC20(token);
        IERC20 mid = IERC20(p.midToken);

        borrowed.forceApprove(p.integration, amount);
        IDODOStyleSwapExactIn(p.integration).swapExactIn(p.pool, token, amount, p.minOutFirst);

        uint256 midBal = mid.balanceOf(address(this));
        mid.forceApprove(p.integration, midBal);
        IDODOStyleSwapExactIn(p.integration).swapExactIn(p.pool, p.midToken, midBal, p.minOutSecond);

        uint256 need = amount + fee;
        if (borrowed.balanceOf(address(this)) < need) revert InsufficientToRepay();
        borrowed.safeTransfer(msg.sender, need);

        return _RETURN_VALUE;
    }
}
