// 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 "./InboxETH.sol";
import "./LiquidityPoolETH.sol";
import "./SwapRouter.sol";
import "./EnhancedSwapRouter.sol";
import "./ChallengeManager.sol";

/**
 * @title DualRouterBridgeSwapCoordinator
 * @notice Coordinates bridge release + swap using either basic SwapRouter or EnhancedSwapRouter
 * @dev Verifies claim finalization, releases from liquidity pool, then swaps via chosen router
 */
contract DualRouterBridgeSwapCoordinator is ReentrancyGuard {
    using SafeERC20 for IERC20;

    InboxETH public immutable inbox;
    LiquidityPoolETH public immutable liquidityPool;
    SwapRouter public immutable swapRouter;
    EnhancedSwapRouter public immutable enhancedSwapRouter;
    ChallengeManager public immutable challengeManager;

    event BridgeSwapExecuted(
        uint256 indexed depositId,
        address indexed recipient,
        LiquidityPoolETH.AssetType inputAsset,
        uint256 bridgeAmount,
        address stablecoinToken,
        uint256 stablecoinAmount,
        bool usedEnhancedRouter
    );

    error ZeroDepositId();
    error ZeroRecipient();
    error ClaimNotFinalized();
    error ClaimChallenged();
    error InsufficientOutput();

    constructor(
        address _inbox,
        address _liquidityPool,
        address _swapRouter,
        address _enhancedSwapRouter,
        address _challengeManager
    ) {
        require(_inbox != address(0), "DualRouterBridgeSwapCoordinator: zero inbox");
        require(_liquidityPool != address(0), "DualRouterBridgeSwapCoordinator: zero liquidity pool");
        require(_swapRouter != address(0), "DualRouterBridgeSwapCoordinator: zero swap router");
        require(_enhancedSwapRouter != address(0), "DualRouterBridgeSwapCoordinator: zero enhanced swap router");
        require(_challengeManager != address(0), "DualRouterBridgeSwapCoordinator: zero challenge manager");

        inbox = InboxETH(payable(_inbox));
        liquidityPool = LiquidityPoolETH(payable(_liquidityPool));
        swapRouter = SwapRouter(payable(_swapRouter));
        enhancedSwapRouter = EnhancedSwapRouter(payable(_enhancedSwapRouter));
        challengeManager = ChallengeManager(payable(_challengeManager));
    }

    /**
     * @notice Execute bridge release + swap to stablecoin
     * @param depositId Deposit ID
     * @param recipient Recipient address (should match claim recipient)
     * @param outputAsset Asset type from bridge (ETH or WETH)
     * @param stablecoinToken Target stablecoin address (USDT, USDC, or DAI)
     * @param amountOutMin Minimum stablecoin output (slippage protection)
     * @param routeData Optional route data for basic SwapRouter (ignored when useEnhancedRouter is true)
     * @param useEnhancedRouter If true, use EnhancedSwapRouter; otherwise use basic SwapRouter
     * @return stablecoinAmount Amount of stablecoin received
     */
    function bridgeAndSwap(
        uint256 depositId,
        address recipient,
        LiquidityPoolETH.AssetType outputAsset,
        address stablecoinToken,
        uint256 amountOutMin,
        bytes calldata routeData,
        bool useEnhancedRouter
    ) external nonReentrant returns (uint256 stablecoinAmount) {
        if (depositId == 0) revert ZeroDepositId();
        if (recipient == address(0)) revert ZeroRecipient();

        ChallengeManager.Claim memory claim = challengeManager.getClaim(depositId);
        if (claim.depositId == 0) revert("DualRouterBridgeSwapCoordinator: claim not found");
        if (!claim.finalized) revert ClaimNotFinalized();
        if (claim.challenged) revert ClaimChallenged();
        if (claim.recipient != recipient) revert("DualRouterBridgeSwapCoordinator: recipient mismatch");

        uint256 bridgeAmount = claim.amount;

        liquidityPool.releaseToRecipient(depositId, address(this), bridgeAmount, outputAsset);

        if (useEnhancedRouter) {
            (stablecoinAmount, ) = _swapViaEnhancedRouter(outputAsset, stablecoinToken, bridgeAmount, amountOutMin);
        } else {
            stablecoinAmount = _swapViaBasicRouter(outputAsset, stablecoinToken, bridgeAmount, amountOutMin, routeData);
        }

        if (stablecoinAmount < amountOutMin) revert InsufficientOutput();

        IERC20(stablecoinToken).safeTransfer(recipient, stablecoinAmount);

        emit BridgeSwapExecuted(
            depositId,
            recipient,
            outputAsset,
            bridgeAmount,
            stablecoinToken,
            stablecoinAmount,
            useEnhancedRouter
        );

        return stablecoinAmount;
    }

    function _swapViaBasicRouter(
        LiquidityPoolETH.AssetType outputAsset,
        address stablecoinToken,
        uint256 bridgeAmount,
        uint256 amountOutMin,
        bytes calldata routeData
    ) internal returns (uint256) {
        if (outputAsset == LiquidityPoolETH.AssetType.ETH) {
            return swapRouter.swapToStablecoin{value: bridgeAmount}(
                outputAsset,
                stablecoinToken,
                bridgeAmount,
                amountOutMin,
                routeData
            );
        }
        address wethAddress = liquidityPool.getWeth();
        IERC20(wethAddress).approve(address(swapRouter), bridgeAmount);
        return swapRouter.swapToStablecoin(
            outputAsset,
            stablecoinToken,
            bridgeAmount,
            amountOutMin,
            routeData
        );
    }

    function _swapViaEnhancedRouter(
        LiquidityPoolETH.AssetType outputAsset,
        address stablecoinToken,
        uint256 bridgeAmount,
        uint256 amountOutMin
    ) internal returns (uint256, EnhancedSwapRouter.SwapProvider) {
        // SwapProvider(0) = UniswapV3; used as "auto" / first in default routing
        EnhancedSwapRouter.SwapProvider preferred = EnhancedSwapRouter.SwapProvider.UniswapV3;
        if (outputAsset == LiquidityPoolETH.AssetType.ETH) {
            return enhancedSwapRouter.swapToStablecoin{value: bridgeAmount}(
                outputAsset,
                stablecoinToken,
                bridgeAmount,
                amountOutMin,
                preferred
            );
        }
        address wethAddress = liquidityPool.getWeth();
        IERC20(wethAddress).approve(address(enhancedSwapRouter), bridgeAmount);
        return enhancedSwapRouter.swapToStablecoin(
            outputAsset,
            stablecoinToken,
            bridgeAmount,
            amountOutMin,
            preferred
        );
    }

    /**
     * @notice Check if claim can be swapped
     */
    function canSwap(uint256 depositId) external view returns (bool canSwap_, string memory reason) {
        ChallengeManager.Claim memory claim = challengeManager.getClaim(depositId);
        if (claim.depositId == 0) return (false, "Claim not found");
        if (!claim.finalized) return (false, "Claim not finalized");
        if (claim.challenged) return (false, "Claim was challenged");
        return (true, "");
    }

    receive() external payable {}
}
