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

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../../../reserve/IReserveSystem.sol";
import "./IStablecoinPegManager.sol";

/**
 * @title StablecoinPegManager
 * @notice Maintains USD peg for USDT/USDC, ETH peg for WETH, and monitors deviations
 * @dev Monitors peg status and triggers rebalancing if deviation exceeds thresholds
 */
contract StablecoinPegManager is IStablecoinPegManager, Ownable, ReentrancyGuard {
    IReserveSystem public immutable reserveSystem;

    // Peg thresholds (basis points: 10000 = 100%)
    uint256 public usdPegThresholdBps = 50; // ±0.5% for USD stablecoins
    uint256 public ethPegThresholdBps = 10; // ±0.1% for ETH/WETH
    uint256 public constant MAX_PEG_THRESHOLD_BPS = 500; // 5% max

    // Target prices (in 18 decimals)
    uint256 public constant USD_TARGET_PRICE = 1e18; // $1.00
    uint256 public constant ETH_TARGET_PRICE = 1e18; // 1:1 with ETH

    // Supported assets
    mapping(address => bool) public isUSDStablecoin; // USDT, USDC, DAI
    mapping(address => bool) public isWETH; // WETH
    address[] public supportedAssets;

    struct AssetPeg {
        address asset;
        uint256 targetPrice;
        uint256 thresholdBps;
        bool isActive;
    }

    mapping(address => AssetPeg) public assetPegs;

    event PegChecked(
        address indexed asset,
        uint256 currentPrice,
        uint256 targetPrice,
        int256 deviationBps,
        bool isMaintained
    );

    event RebalancingTriggered(
        address indexed asset,
        int256 deviationBps,
        uint256 requiredAdjustment
    );

    event AssetRegistered(address indexed asset, uint256 targetPrice, uint256 thresholdBps);
    event PegThresholdUpdated(address indexed asset, uint256 oldThreshold, uint256 newThreshold);

    error ZeroAddress();
    error AssetNotRegistered();
    error InvalidThreshold();
    error InvalidTargetPrice();

    /**
     * @notice Constructor
     * @param _reserveSystem ReserveSystem contract address
     */
    constructor(address _reserveSystem) Ownable(msg.sender) {
        if (_reserveSystem == address(0)) revert ZeroAddress();
        reserveSystem = IReserveSystem(_reserveSystem);
    }

    /**
     * @notice Register a USD stablecoin
     * @param asset Asset address (USDT, USDC, DAI)
     */
    function registerUSDStablecoin(address asset) external onlyOwner {
        if (asset == address(0)) revert ZeroAddress();
        
        isUSDStablecoin[asset] = true;
        assetPegs[asset] = AssetPeg({
            asset: asset,
            targetPrice: USD_TARGET_PRICE,
            thresholdBps: usdPegThresholdBps,
            isActive: true
        });

        // Add to supported assets if not already present
        bool alreadyAdded = false;
        for (uint256 i = 0; i < supportedAssets.length; i++) {
            if (supportedAssets[i] == asset) {
                alreadyAdded = true;
                break;
            }
        }
        if (!alreadyAdded) {
            supportedAssets.push(asset);
        }

        emit AssetRegistered(asset, USD_TARGET_PRICE, usdPegThresholdBps);
    }

    /**
     * @notice Register WETH
     * @param weth WETH token address
     */
    function registerWETH(address weth) external onlyOwner {
        if (weth == address(0)) revert ZeroAddress();
        
        isWETH[weth] = true;
        assetPegs[weth] = AssetPeg({
            asset: weth,
            targetPrice: ETH_TARGET_PRICE,
            thresholdBps: ethPegThresholdBps,
            isActive: true
        });

        // Add to supported assets if not already present
        bool alreadyAdded = false;
        for (uint256 i = 0; i < supportedAssets.length; i++) {
            if (supportedAssets[i] == weth) {
                alreadyAdded = true;
                break;
            }
        }
        if (!alreadyAdded) {
            supportedAssets.push(weth);
        }

        emit AssetRegistered(weth, ETH_TARGET_PRICE, ethPegThresholdBps);
    }

    /**
     * @notice Check USD peg for a stablecoin
     * @param stablecoin Stablecoin address
     * @return isMaintained Whether peg is maintained
     * @return deviationBps Deviation in basis points
     */
    function checkUSDpeg(address stablecoin) external view override returns (bool isMaintained, int256 deviationBps) {
        if (!isUSDStablecoin[stablecoin]) revert AssetNotRegistered();
        
        AssetPeg memory peg = assetPegs[stablecoin];
        (uint256 currentPrice, ) = reserveSystem.getPrice(stablecoin);
        
        deviationBps = calculateDeviation(stablecoin, currentPrice, peg.targetPrice);
        isMaintained = _abs(deviationBps) <= peg.thresholdBps;

        return (isMaintained, deviationBps);
    }

    /**
     * @notice Check ETH peg for WETH
     * @param weth WETH address
     * @return isMaintained Whether peg is maintained
     * @return deviationBps Deviation in basis points
     */
    function checkETHpeg(address weth) external view override returns (bool isMaintained, int256 deviationBps) {
        if (!isWETH[weth]) revert AssetNotRegistered();
        
        AssetPeg memory peg = assetPegs[weth];
        (uint256 currentPrice, ) = reserveSystem.getPrice(weth);
        
        deviationBps = calculateDeviation(weth, currentPrice, peg.targetPrice);
        isMaintained = _abs(deviationBps) <= peg.thresholdBps;

        return (isMaintained, deviationBps);
    }

    /**
     * @notice Calculate deviation from target price
     * @param asset Asset address
     * @param currentPrice Current price
     * @param targetPrice Target price
     * @return deviationBps Deviation in basis points (can be negative)
     */
    function calculateDeviation(
        address asset,
        uint256 currentPrice,
        uint256 targetPrice
    ) public pure override returns (int256 deviationBps) {
        if (targetPrice == 0) revert InvalidTargetPrice();
        
        // Calculate deviation: ((currentPrice - targetPrice) / targetPrice) * 10000
        if (currentPrice >= targetPrice) {
            uint256 diff = currentPrice - targetPrice;
            deviationBps = int256((diff * 10000) / targetPrice);
        } else {
            uint256 diff = targetPrice - currentPrice;
            deviationBps = -int256((diff * 10000) / targetPrice);
        }

        return deviationBps;
    }

    /**
     * @notice Get peg status for an asset
     * @param asset Asset address
     * @return currentPrice Current price
     * @return targetPrice Target price
     * @return deviationBps Deviation in basis points
     * @return isMaintained Whether peg is maintained
     */
    function getPegStatus(
        address asset
    ) external view override returns (uint256 currentPrice, uint256 targetPrice, int256 deviationBps, bool isMaintained) {
        AssetPeg memory peg = assetPegs[asset];
        if (peg.asset == address(0)) revert AssetNotRegistered();
        
        (currentPrice, ) = reserveSystem.getPrice(asset);
        targetPrice = peg.targetPrice;
        deviationBps = calculateDeviation(asset, currentPrice, targetPrice);
        isMaintained = _abs(deviationBps) <= peg.thresholdBps;

        // Note: Cannot emit in view function, events should be emitted by caller if needed

        return (currentPrice, targetPrice, deviationBps, isMaintained);
    }

    /**
     * @notice Get all supported assets
     * @return Array of supported asset addresses
     */
    function getSupportedAssets() external view override returns (address[] memory) {
        return supportedAssets;
    }

    /**
     * @notice Set USD peg threshold
     * @param newThreshold New threshold in basis points
     */
    function setUSDPegThreshold(uint256 newThreshold) external onlyOwner {
        if (newThreshold > MAX_PEG_THRESHOLD_BPS) revert InvalidThreshold();
        
        uint256 oldThreshold = usdPegThresholdBps;
        usdPegThresholdBps = newThreshold;

        // Update all USD stablecoin thresholds
        for (uint256 i = 0; i < supportedAssets.length; i++) {
            if (isUSDStablecoin[supportedAssets[i]]) {
                uint256 oldAssetThreshold = assetPegs[supportedAssets[i]].thresholdBps;
                assetPegs[supportedAssets[i]].thresholdBps = newThreshold;
                emit PegThresholdUpdated(supportedAssets[i], oldAssetThreshold, newThreshold);
            }
        }

        emit PegThresholdUpdated(address(0), oldThreshold, newThreshold);
    }

    /**
     * @notice Set ETH peg threshold
     * @param newThreshold New threshold in basis points
     */
    function setETHPegThreshold(uint256 newThreshold) external onlyOwner {
        if (newThreshold > MAX_PEG_THRESHOLD_BPS) revert InvalidThreshold();
        
        uint256 oldThreshold = ethPegThresholdBps;
        ethPegThresholdBps = newThreshold;

        // Update all WETH thresholds
        for (uint256 i = 0; i < supportedAssets.length; i++) {
            if (isWETH[supportedAssets[i]]) {
                uint256 oldAssetThreshold = assetPegs[supportedAssets[i]].thresholdBps;
                assetPegs[supportedAssets[i]].thresholdBps = newThreshold;
                emit PegThresholdUpdated(supportedAssets[i], oldAssetThreshold, newThreshold);
            }
        }

        emit PegThresholdUpdated(address(0), oldThreshold, newThreshold);
    }

    /**
     * @notice Trigger rebalancing if deviation exceeds threshold
     * @param asset Asset address
     */
    function triggerRebalancing(address asset) external onlyOwner nonReentrant {
        AssetPeg memory peg = assetPegs[asset];
        if (peg.asset == address(0)) revert AssetNotRegistered();

        (uint256 currentPrice, ) = reserveSystem.getPrice(asset);
        int256 deviationBps = calculateDeviation(asset, currentPrice, peg.targetPrice);

        if (_abs(deviationBps) > peg.thresholdBps) {
            // Calculate required adjustment
            uint256 adjustment = currentPrice > peg.targetPrice
                ? currentPrice - peg.targetPrice
                : peg.targetPrice - currentPrice;

            emit RebalancingTriggered(asset, deviationBps, adjustment);
        }
    }

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

    /**
     * @notice Get absolute value of int256
     * @param value Input value
     * @return Absolute value
     */
    function _abs(int256 value) internal pure returns (uint256) {
        return value < 0 ? uint256(-value) : uint256(value);
    }
}

