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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "../BridgeSwapCoordinator.sol";
import "../LiquidityPoolETH.sol";
import "../../../reserve/IReserveSystem.sol";
import "./IStablecoinPegManager.sol";
import "./ICommodityPegManager.sol";
import "./IISOCurrencyManager.sol";

/**
 * @title BridgeReserveCoordinator
 * @notice Orchestrates bridge operations with ReserveSystem, ensuring peg maintenance and asset backing
 * @dev Connects trustless bridge to ReserveSystem for reserve verification and peg maintenance
 */
contract BridgeReserveCoordinator is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    BridgeSwapCoordinator public immutable bridgeSwapCoordinator;
    IReserveSystem public immutable reserveSystem;
    IStablecoinPegManager public immutable stablecoinPegManager;
    ICommodityPegManager public immutable commodityPegManager;
    IISOCurrencyManager public immutable isoCurrencyManager;

    // Reserve verification threshold (basis points: 10000 = 100%)
    uint256 public reserveVerificationThresholdBps = 10000; // 100% - must have full backing
    uint256 public constant MAX_RESERVE_THRESHOLD_BPS = 15000; // 150% max

    // Rebalancing parameters
    uint256 public rebalancingCooldown = 1 hours;
    mapping(address => uint256) public lastRebalancingTime;

    struct ReserveStatus {
        address asset;
        uint256 bridgeAmount;
        uint256 reserveBalance;
        uint256 reserveRatio; // reserveBalance / bridgeAmount * 10000
        bool isSufficient;
        uint256 lastVerified;
    }

    struct PegStatus {
        address asset;
        uint256 currentPrice;
        uint256 targetPrice;
        int256 deviationBps; // Can be negative
        bool isMaintained;
    }

    event ReserveVerified(
        uint256 indexed depositId,
        address indexed asset,
        uint256 bridgeAmount,
        uint256 reserveBalance,
        bool isSufficient
    );

    event RebalancingTriggered(
        address indexed asset,
        uint256 amount,
        address indexed recipient
    );

    event ReserveThresholdUpdated(uint256 oldThreshold, uint256 newThreshold);

    error ZeroAddress();
    error InsufficientReserve();
    error RebalancingCooldownActive();
    error InvalidReserveThreshold();
    error ReserveVerificationFailed();

    /**
     * @notice Constructor
     * @param _bridgeSwapCoordinator BridgeSwapCoordinator contract address
     * @param _reserveSystem ReserveSystem contract address
     * @param _stablecoinPegManager StablecoinPegManager contract address
     * @param _commodityPegManager CommodityPegManager contract address
     * @param _isoCurrencyManager ISOCurrencyManager contract address
     */
    constructor(
        address _bridgeSwapCoordinator,
        address _reserveSystem,
        address _stablecoinPegManager,
        address _commodityPegManager,
        address _isoCurrencyManager
    ) Ownable(msg.sender) {
        if (_bridgeSwapCoordinator == address(0) || 
            _reserveSystem == address(0) ||
            _stablecoinPegManager == address(0) ||
            _commodityPegManager == address(0) ||
            _isoCurrencyManager == address(0)) {
            revert ZeroAddress();
        }

        bridgeSwapCoordinator = BridgeSwapCoordinator(payable(_bridgeSwapCoordinator));
        reserveSystem = IReserveSystem(_reserveSystem);
        stablecoinPegManager = IStablecoinPegManager(_stablecoinPegManager);
        commodityPegManager = ICommodityPegManager(_commodityPegManager);
        isoCurrencyManager = IISOCurrencyManager(_isoCurrencyManager);
    }

    /**
     * @notice Bridge transfer with automatic reserve verification
     * @param depositId Deposit ID from bridge
     * @param recipient Recipient address
     * @param outputAsset Asset type (ETH or WETH)
     * @param stablecoinToken Target stablecoin
     * @param amountOutMin Minimum output amount
     * @param routeData Optional route data for swap
     * @return stablecoinAmount Amount of stablecoin received
     */
    function bridgeWithReserveBacking(
        uint256 depositId,
        address recipient,
        LiquidityPoolETH.AssetType outputAsset,
        address stablecoinToken,
        uint256 amountOutMin,
        bytes calldata routeData
    ) external nonReentrant returns (uint256 stablecoinAmount) {
        // Get claim amount from bridge
        // Note: We need to get the amount from ChallengeManager via BridgeSwapCoordinator
        // For now, we'll verify reserves after the bridge operation
        
        // Execute bridge and swap
        stablecoinAmount = bridgeSwapCoordinator.bridgeAndSwap(
            depositId,
            recipient,
            outputAsset,
            stablecoinToken,
            amountOutMin,
            routeData
        );

        // Verify reserve backing for the stablecoin
        ReserveStatus memory status = verifyReserveStatus(stablecoinToken, stablecoinAmount);
        
        if (!status.isSufficient) {
            // Trigger rebalancing if reserves insufficient
            _triggerRebalancing(stablecoinToken, stablecoinAmount);
        }

        emit ReserveVerified(
            depositId,
            stablecoinToken,
            stablecoinAmount,
            status.reserveBalance,
            status.isSufficient
        );

        return stablecoinAmount;
    }

    /**
     * @notice Verify peg status for all assets
     * @return pegStatuses Array of peg statuses
     */
    function verifyPegStatus() external view returns (PegStatus[] memory pegStatuses) {
        // Get stablecoin peg statuses
        address[] memory stablecoins = stablecoinPegManager.getSupportedAssets();
        uint256 stablecoinCount = stablecoins.length;
        
        // Get commodity peg statuses
        address[] memory commodities = commodityPegManager.getSupportedCommodities();
        uint256 commodityCount = commodities.length;

        uint256 totalCount = stablecoinCount + commodityCount;
        pegStatuses = new PegStatus[](totalCount);

        uint256 index = 0;

        // Add stablecoin peg statuses
        for (uint256 i = 0; i < stablecoinCount; i++) {
            (uint256 currentPrice, uint256 targetPrice, int256 deviationBps, bool isMaintained) = 
                stablecoinPegManager.getPegStatus(stablecoins[i]);
            
            pegStatuses[index] = PegStatus({
                asset: stablecoins[i],
                currentPrice: currentPrice,
                targetPrice: targetPrice,
                deviationBps: deviationBps,
                isMaintained: isMaintained
            });
            index++;
        }

        // Add commodity peg statuses
        for (uint256 i = 0; i < commodityCount; i++) {
            (uint256 currentPrice, uint256 targetPrice, int256 deviationBps, bool isMaintained) = 
                commodityPegManager.getCommodityPegStatus(commodities[i]);
            
            pegStatuses[index] = PegStatus({
                asset: commodities[i],
                currentPrice: currentPrice,
                targetPrice: targetPrice,
                deviationBps: deviationBps,
                isMaintained: isMaintained
            });
            index++;
        }
    }

    /**
     * @notice Trigger rebalancing if peg deviates
     * @param asset Asset address to rebalance
     * @param amount Amount that needs backing
     */
    function triggerRebalancing(address asset, uint256 amount) external onlyOwner nonReentrant {
        if (block.timestamp < lastRebalancingTime[asset] + rebalancingCooldown) {
            revert RebalancingCooldownActive();
        }

        _triggerRebalancing(asset, amount);
    }

    /**
     * @notice Get reserve status for an asset
     * @param asset Asset address
     * @param bridgeAmount Amount bridged/required
     * @return status Reserve status
     */
    function getReserveStatus(
        address asset,
        uint256 bridgeAmount
    ) external view returns (ReserveStatus memory status) {
        return verifyReserveStatus(asset, bridgeAmount);
    }

    /**
     * @notice Set reserve verification threshold
     * @param newThreshold New threshold in basis points
     */
    function setReserveThreshold(uint256 newThreshold) external onlyOwner {
        if (newThreshold > MAX_RESERVE_THRESHOLD_BPS) {
            revert InvalidReserveThreshold();
        }

        uint256 oldThreshold = reserveVerificationThresholdBps;
        reserveVerificationThresholdBps = newThreshold;

        emit ReserveThresholdUpdated(oldThreshold, newThreshold);
    }

    /**
     * @notice Set rebalancing cooldown period
     * @param newCooldown New cooldown in seconds
     */
    function setRebalancingCooldown(uint256 newCooldown) external onlyOwner {
        rebalancingCooldown = newCooldown;
    }

    // ============ Internal Functions ============

    /**
     * @notice Verify reserve status for an asset
     * @param asset Asset address
     * @param bridgeAmount Amount bridged/required
     * @return status Reserve status
     */
    function verifyReserveStatus(
        address asset,
        uint256 bridgeAmount
    ) internal view returns (ReserveStatus memory status) {
        uint256 reserveBalance = reserveSystem.getReserveBalance(asset);
        uint256 reserveRatio = bridgeAmount > 0 
            ? (reserveBalance * 10000) / bridgeAmount 
            : type(uint256).max;
        
        bool isSufficient = reserveRatio >= reserveVerificationThresholdBps;

        return ReserveStatus({
            asset: asset,
            bridgeAmount: bridgeAmount,
            reserveBalance: reserveBalance,
            reserveRatio: reserveRatio,
            isSufficient: isSufficient,
            lastVerified: block.timestamp
        });
    }

    /**
     * @notice Internal function to trigger rebalancing
     * @param asset Asset address
     * @param amount Amount that needs backing
     */
    function _triggerRebalancing(address asset, uint256 amount) internal {
        lastRebalancingTime[asset] = block.timestamp;

        // Check if we need to deposit reserves
        ReserveStatus memory status = verifyReserveStatus(asset, amount);
        
        if (!status.isSufficient) {
            uint256 shortfall = amount - status.reserveBalance;
            
            // In production, this would trigger reserve deposits or conversions
            // For now, we emit an event for off-chain monitoring
            emit RebalancingTriggered(asset, shortfall, address(this));
        }
    }
}

