// 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 "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./LiquidityPoolETH.sol";
import "./interfaces/ISwapRouter.sol";
import "./interfaces/IWETH.sol";
import "./interfaces/ICurvePool.sol";
import "./interfaces/IAggregationRouter.sol";

/**
 * @title SwapRouter
 * @notice Swaps ETH/WETH to stablecoins via Uniswap V3, Curve, or 1inch
 * @dev Primary: Uniswap V3, Secondary: Curve, Optional: 1inch aggregation
 */
contract SwapRouter is ReentrancyGuard {
    using SafeERC20 for IERC20;

    enum SwapProvider {
        UniswapV3,
        Curve,
        OneInch
    }

    // Contract addresses (Ethereum Mainnet)
    address public immutable uniswapV3Router; // legacy SwapRouter on Ethereum mainnet
    address public immutable curve3Pool; // 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7
    address public immutable oneInchRouter; // 0x1111111254EEB25477B68fb85Ed929f73A960582 (optional)
    
    // Token addresses (Ethereum Mainnet)
    address public immutable weth; // 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
    address public immutable usdt; // 0xdAC17F958D2ee523a2206206994597C13D831ec7
    address public immutable usdc; // 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
    address public immutable dai; // 0x6B175474E89094C44Da98b954EedeAC495271d0F

    // Uniswap V3 fee tiers (0.05% = 500, 0.3% = 3000, 1% = 10000)
    uint24 public constant FEE_TIER_LOW = 500; // 0.05%
    uint24 public constant FEE_TIER_MEDIUM = 3000; // 0.3%
    uint24 public constant FEE_TIER_HIGH = 10000; // 1%

    event SwapExecuted(
        SwapProvider provider,
        LiquidityPoolETH.AssetType inputAsset,
        address inputToken,
        address outputToken,
        uint256 amountIn,
        uint256 amountOut
    );

    error ZeroAmount();
    error ZeroAddress();
    error InsufficientOutput();
    error InvalidAssetType();
    error SwapFailed();

    /**
     * @notice Constructor
     * @param _uniswapV3Router Uniswap V3 SwapRouter address
     * @param _curve3Pool Curve 3pool address
     * @param _oneInchRouter 1inch Router address (can be address(0) if not used)
     * @param _weth WETH address
     * @param _usdt USDT address
     * @param _usdc USDC address
     * @param _dai DAI address
     */
    constructor(
        address _uniswapV3Router,
        address _curve3Pool,
        address _oneInchRouter,
        address _weth,
        address _usdt,
        address _usdc,
        address _dai
    ) {
        require(_uniswapV3Router != address(0), "SwapRouter: zero Uniswap router");
        require(_curve3Pool != address(0), "SwapRouter: zero Curve pool");
        require(_weth != address(0), "SwapRouter: zero WETH");
        require(_usdt != address(0), "SwapRouter: zero USDT");
        require(_usdc != address(0), "SwapRouter: zero USDC");
        require(_dai != address(0), "SwapRouter: zero DAI");

        uniswapV3Router = _uniswapV3Router;
        curve3Pool = _curve3Pool;
        oneInchRouter = _oneInchRouter;
        weth = _weth;
        usdt = _usdt;
        usdc = _usdc;
        dai = _dai;
    }

    /**
     * @notice Swap to stablecoin using best available route
     * @param inputAsset Input asset type (ETH or WETH)
     * @param stablecoinToken Target stablecoin address (USDT, USDC, or DAI)
     * @param amountIn Input amount
     * @param amountOutMin Minimum output amount (slippage protection)
     * @return amountOut Output amount
     */
    function swapToStablecoin(
        LiquidityPoolETH.AssetType inputAsset,
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin,
        bytes calldata
    ) external payable nonReentrant returns (uint256 amountOut) {
        if (amountIn == 0) revert ZeroAmount();
        if (stablecoinToken == address(0)) revert ZeroAddress();
        if (!_isValidStablecoin(stablecoinToken)) revert("SwapRouter: invalid stablecoin");

        // Convert ETH to WETH if needed
        if (inputAsset == LiquidityPoolETH.AssetType.ETH) {
            IWETH(weth).deposit{value: amountIn}();
            inputAsset = LiquidityPoolETH.AssetType.WETH;
        }

        // Approve WETH for swap
        IERC20 wethToken = IERC20(weth);
        // Use forceApprove for OpenZeppelin 5.x (or approve directly)
        wethToken.approve(uniswapV3Router, amountIn);
        if (oneInchRouter != address(0)) {
            wethToken.approve(oneInchRouter, amountIn);
        }

        // Try Uniswap V3 first (primary)
        uint256 outputAmount = _executeUniswapV3Swap(stablecoinToken, amountIn, amountOutMin);
        if (outputAmount >= amountOutMin) {
            // Transfer output to caller
            IERC20(stablecoinToken).safeTransfer(msg.sender, outputAmount);
            emit SwapExecuted(SwapProvider.UniswapV3, inputAsset, weth, stablecoinToken, amountIn, outputAmount);
            return outputAmount;
        }

        // Try Curve for stable/stable swaps (if USDT/USDC/DAI and routeData provided)
        // Note: Curve 3pool doesn't support WETH directly, would need intermediate swap
        // For now, revert if Uniswap fails
        revert SwapFailed();
    }

    /**
     * @notice Execute Uniswap V3 swap (internal)
     * @param stablecoinToken Target stablecoin
     * @param amountIn Input amount
     * @param amountOutMin Minimum output
     * @return amountOut Output amount
     */
    function _executeUniswapV3Swap(
        address stablecoinToken,
        uint256 amountIn,
        uint256 amountOutMin
    ) internal returns (uint256 amountOut) {
        ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
            tokenIn: weth,
            tokenOut: stablecoinToken,
            fee: FEE_TIER_MEDIUM, // 0.3% fee tier
            recipient: address(this),
            deadline: block.timestamp + 300, // 5 minutes
            amountIn: amountIn,
            amountOutMinimum: amountOutMin,
            sqrtPriceLimitX96: 0 // No price limit
        });

        amountOut = ISwapRouter(uniswapV3Router).exactInputSingle(params);
        return amountOut;
    }

    /**
     * @notice Check if token is a valid stablecoin
     * @param token Token address to check
     * @return True if valid stablecoin
     */
    function _isValidStablecoin(address token) internal view returns (bool) {
        return token == usdt || token == usdc || token == dai;
    }

    // Allow contract to receive ETH
    receive() external payable {}
}
