// 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 "./ChallengeManager.sol";

/**
 * @title BridgeSwapCoordinator
 * @notice Coordinates bridge release + swap in single transaction
 * @dev Verifies claim finalization, releases from liquidity pool, executes swap, transfers stablecoin
 */
contract BridgeSwapCoordinator is ReentrancyGuard {
    using SafeERC20 for IERC20;

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

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

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

    /**
     * @notice Constructor
     * @param _inbox InboxETH contract address
     * @param _liquidityPool LiquidityPoolETH contract address
     * @param _swapRouter SwapRouter contract address
     * @param _challengeManager ChallengeManager contract address
     */
    constructor(
        address _inbox,
        address _liquidityPool,
        address _swapRouter,
        address _challengeManager
    ) {
        require(_inbox != address(0), "BridgeSwapCoordinator: zero inbox");
        require(_liquidityPool != address(0), "BridgeSwapCoordinator: zero liquidity pool");
        require(_swapRouter != address(0), "BridgeSwapCoordinator: zero swap router");
        require(_challengeManager != address(0), "BridgeSwapCoordinator: zero challenge manager");

        inbox = InboxETH(payable(_inbox));
        liquidityPool = LiquidityPoolETH(payable(_liquidityPool));
        swapRouter = SwapRouter(payable(_swapRouter));
        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 swap (for 1inch)
     * @return stablecoinAmount Amount of stablecoin received
     */
    function bridgeAndSwap(
        uint256 depositId,
        address recipient,
        LiquidityPoolETH.AssetType outputAsset,
        address stablecoinToken,
        uint256 amountOutMin,
        bytes calldata routeData
    ) external nonReentrant returns (uint256 stablecoinAmount) {
        if (depositId == 0) revert ZeroDepositId();
        if (recipient == address(0)) revert ZeroRecipient();

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

        // Use amount from claim (ChallengeManager has the claim data)
        uint256 bridgeAmount = claim.amount;

        // Add pending claim (this should have been done during claim submission, but check anyway)
        // Note: In production, you'd want to track whether funds have already been released

        // Release funds from liquidity pool to this contract
        liquidityPool.releaseToRecipient(depositId, address(this), bridgeAmount, outputAsset);

        // Execute swap
        if (outputAsset == LiquidityPoolETH.AssetType.ETH) {
            // Swap ETH to stablecoin via SwapRouter
            stablecoinAmount = swapRouter.swapToStablecoin{value: bridgeAmount}(
                outputAsset,
                stablecoinToken,
                bridgeAmount,
                amountOutMin,
                routeData
            );
        } else {
            // WETH case: approve and swap
            // Get WETH address from liquidity pool
            address wethAddress = liquidityPool.getWeth();
            IERC20 wethToken = IERC20(wethAddress);
            wethToken.approve(address(swapRouter), bridgeAmount);
            
            stablecoinAmount = swapRouter.swapToStablecoin(
                outputAsset,
                stablecoinToken,
                bridgeAmount,
                amountOutMin,
                routeData
            );
        }

        if (stablecoinAmount < amountOutMin) revert InsufficientOutput();

        // Transfer stablecoin to recipient
        IERC20(stablecoinToken).safeTransfer(recipient, stablecoinAmount);

        // Note: Bond release should be handled separately after finalization
        // This coordinator only handles bridge release + swap

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

        return stablecoinAmount;
    }

    /**
     * @notice Check if claim can be swapped
     * @param depositId Deposit ID
     * @return canSwap_ True if claim can be swapped
     * @return reason Reason if cannot swap
     */
    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, "");
    }

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