// 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";

/**
 * @title LiquidityPoolETH
 * @notice Liquidity pool for ETH and WETH with fee model and minimum liquidity ratio enforcement
 * @dev Supports separate pools for native ETH and WETH (ERC-20)
 */
contract LiquidityPoolETH is ReentrancyGuard {
    using SafeERC20 for IERC20;

    enum AssetType {
        ETH,   // Native ETH
        WETH   // Wrapped ETH (ERC-20)
    }

    // Pool configuration
    uint256 public immutable lpFeeBps; // Liquidity provider fee in basis points (default: 5 = 0.05%)
    uint256 public immutable minLiquidityRatioBps; // Minimum liquidity ratio in basis points (default: 11000 = 110%)
    address public immutable weth; // WETH token address
    
    // WETH getter for external access
    function getWeth() external view returns (address) {
        return weth;
    }

    // Pool state
    struct PoolState {
        uint256 totalLiquidity;
        uint256 pendingClaims; // Total amount of pending claims to be released
        mapping(address => uint256) lpShares; // LP address => amount provided
    }

    mapping(AssetType => PoolState) public pools;
    mapping(address => bool) public authorizedRelease; // Contracts authorized to release funds

    event LiquidityProvided(
        AssetType indexed assetType,
        address indexed provider,
        uint256 amount
    );

    event LiquidityWithdrawn(
        AssetType indexed assetType,
        address indexed provider,
        uint256 amount
    );

    event FundsReleased(
        AssetType indexed assetType,
        uint256 indexed depositId,
        address indexed recipient,
        uint256 amount,
        uint256 feeAmount
    );

    event PendingClaimAdded(
        AssetType indexed assetType,
        uint256 amount
    );

    event PendingClaimRemoved(
        AssetType indexed assetType,
        uint256 amount
    );

    error ZeroAmount();
    error ZeroAddress();
    error InsufficientLiquidity();
    error WithdrawalBlockedByLiquidityRatio();
    error UnauthorizedRelease();
    error InvalidAssetType();

    /**
     * @notice Constructor
     * @param _weth WETH token address
     * @param _lpFeeBps LP fee in basis points (5 = 0.05%)
     * @param _minLiquidityRatioBps Minimum liquidity ratio in basis points (11000 = 110%)
     */
    constructor(
        address _weth,
        uint256 _lpFeeBps,
        uint256 _minLiquidityRatioBps
    ) {
        require(_weth != address(0), "LiquidityPoolETH: zero WETH address");
        require(_lpFeeBps <= 10000, "LiquidityPoolETH: fee exceeds 100%");
        require(_minLiquidityRatioBps >= 10000, "LiquidityPoolETH: min ratio must be >= 100%");
        
        weth = _weth;
        lpFeeBps = _lpFeeBps;
        minLiquidityRatioBps = _minLiquidityRatioBps;
    }

    /**
     * @notice Authorize a contract to release funds (called during deployment)
     * @param releaser Address authorized to release funds
     */
    function authorizeRelease(address releaser) external {
        require(releaser != address(0), "LiquidityPoolETH: zero address");
        authorizedRelease[releaser] = true;
    }

    /**
     * @notice Provide liquidity to the pool
     * @param assetType Type of asset (ETH or WETH)
     */
    function provideLiquidity(AssetType assetType) external payable nonReentrant {
        uint256 amount;
        
        if (assetType == AssetType.ETH) {
            if (msg.value == 0) revert ZeroAmount();
            amount = msg.value;
        } else if (assetType == AssetType.WETH) {
            if (msg.value != 0) revert("LiquidityPoolETH: WETH deposits must use depositWETH()");
            revert("LiquidityPoolETH: use depositWETH() for WETH deposits");
        } else {
            revert InvalidAssetType();
        }

        pools[assetType].totalLiquidity += amount;
        pools[assetType].lpShares[msg.sender] += amount;

        emit LiquidityProvided(assetType, msg.sender, amount);
    }

    /**
     * @notice Provide WETH liquidity to the pool
     * @param amount Amount of WETH to deposit
     */
    function depositWETH(uint256 amount) external nonReentrant {
        if (amount == 0) revert ZeroAmount();

        IERC20(weth).safeTransferFrom(msg.sender, address(this), amount);

        pools[AssetType.WETH].totalLiquidity += amount;
        pools[AssetType.WETH].lpShares[msg.sender] += amount;

        emit LiquidityProvided(AssetType.WETH, msg.sender, amount);
    }

    /**
     * @notice Withdraw liquidity from the pool
     * @param amount Amount to withdraw
     * @param assetType Type of asset (ETH or WETH)
     */
    function withdrawLiquidity(
        uint256 amount,
        AssetType assetType
    ) external nonReentrant {
        if (amount == 0) revert ZeroAmount();
        if (pools[assetType].lpShares[msg.sender] < amount) revert InsufficientLiquidity();

        // Check minimum liquidity ratio
        uint256 availableLiquidity = pools[assetType].totalLiquidity - pools[assetType].pendingClaims;
        uint256 newAvailableLiquidity = availableLiquidity - amount;
        uint256 minRequired = (pools[assetType].pendingClaims * minLiquidityRatioBps) / 10000;

        if (newAvailableLiquidity < minRequired) {
            revert WithdrawalBlockedByLiquidityRatio();
        }

        pools[assetType].totalLiquidity -= amount;
        pools[assetType].lpShares[msg.sender] -= amount;

        if (assetType == AssetType.ETH) {
            (bool success, ) = payable(msg.sender).call{value: amount}("");
            require(success, "LiquidityPoolETH: ETH transfer failed");
        } else {
            IERC20(weth).safeTransfer(msg.sender, amount);
        }

        emit LiquidityWithdrawn(assetType, msg.sender, amount);
    }

    /**
     * @notice Release funds to recipient (only authorized contracts)
     * @param depositId Deposit ID (for event tracking)
     * @param recipient Recipient address
     * @param amount Amount to release (before fees)
     * @param assetType Type of asset (ETH or WETH)
     */
    function releaseToRecipient(
        uint256 depositId,
        address recipient,
        uint256 amount,
        AssetType assetType
    ) external nonReentrant {
        if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
        if (amount == 0) revert ZeroAmount();
        if (recipient == address(0)) revert ZeroAddress();

        // Calculate fee
        uint256 feeAmount = (amount * lpFeeBps) / 10000;
        uint256 releaseAmount = amount - feeAmount;

        // Check available liquidity
        PoolState storage pool = pools[assetType];
        uint256 availableLiquidity = pool.totalLiquidity - pool.pendingClaims;
        
        if (availableLiquidity < releaseAmount) {
            revert InsufficientLiquidity();
        }

        // Reduce pending claims
        pool.pendingClaims -= amount;

        // Release funds to recipient
        if (assetType == AssetType.ETH) {
            (bool success, ) = payable(recipient).call{value: releaseAmount}("");
            require(success, "LiquidityPoolETH: ETH transfer failed");
        } else {
            IERC20(weth).safeTransfer(recipient, releaseAmount);
        }

        // Fee remains in pool (increases totalLiquidity effectively by reducing pendingClaims)

        emit FundsReleased(assetType, depositId, recipient, releaseAmount, feeAmount);
    }

    /**
     * @notice Add pending claim (called when claim is submitted)
     * @param amount Amount of pending claim
     * @param assetType Type of asset
     */
    function addPendingClaim(uint256 amount, AssetType assetType) external {
        if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
        pools[assetType].pendingClaims += amount;
        emit PendingClaimAdded(assetType, amount);
    }

    /**
     * @notice Remove pending claim (called when claim is challenged/slashed)
     * @param amount Amount of pending claim to remove
     * @param assetType Type of asset
     */
    function removePendingClaim(uint256 amount, AssetType assetType) external {
        if (!authorizedRelease[msg.sender]) revert UnauthorizedRelease();
        pools[assetType].pendingClaims -= amount;
        emit PendingClaimRemoved(assetType, amount);
    }

    /**
     * @notice Get available liquidity for an asset type
     * @param assetType Type of asset
     * @return Available liquidity (total - pending claims)
     */
    function getAvailableLiquidity(AssetType assetType) external view returns (uint256) {
        PoolState storage pool = pools[assetType];
        uint256 pending = pool.pendingClaims;
        if (pool.totalLiquidity <= pending) {
            return 0;
        }
        return pool.totalLiquidity - pending;
    }

    /**
     * @notice Get LP share for a provider
     * @param provider LP provider address
     * @param assetType Type of asset
     * @return LP share amount
     */
    function getLpShare(address provider, AssetType assetType) external view returns (uint256) {
        return pools[assetType].lpShares[provider];
    }

    /**
     * @notice Get pool statistics
     * @param assetType Type of asset
     * @return totalLiquidity Total liquidity in pool
     * @return pendingClaims Total pending claims
     * @return availableLiquidity Available liquidity (total - pending)
     */
    function getPoolStats(
        AssetType assetType
    ) external view returns (
        uint256 totalLiquidity,
        uint256 pendingClaims,
        uint256 availableLiquidity
    ) {
        PoolState storage pool = pools[assetType];
        totalLiquidity = pool.totalLiquidity;
        pendingClaims = pool.pendingClaims;
        if (totalLiquidity > pendingClaims) {
            availableLiquidity = totalLiquidity - pendingClaims;
        } else {
            availableLiquidity = 0;
        }
    }

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