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

/**
 * @title CommodityPegManager
 * @notice Manages commodity pegging (gold XAU, silver, oil, etc.) via XAU triangulation
 * @dev All commodities are pegged through XAU (gold) as the base anchor
 */
contract CommodityPegManager is ICommodityPegManager, Ownable, ReentrancyGuard {
    IReserveSystem public immutable reserveSystem;

    // XAU address (gold) - base anchor
    address public xauAddress;

    // Commodity peg threshold (basis points: 10000 = 100%)
    uint256 public commodityPegThresholdBps = 100; // ±1.0% for commodities
    uint256 public constant MAX_PEG_THRESHOLD_BPS = 1000; // 10% max

    struct Commodity {
        address commodityAddress;
        string symbol;
        uint256 xauRate; // Rate per 1 oz XAU (in 18 decimals)
        bool isActive;
    }

    mapping(address => Commodity) public commodities;
    address[] public supportedCommodities;

    // XAU rates: 1 oz XAU = xauRate units of commodity
    // Example: 1 oz XAU = 75 oz XAG (silver), so xauRate = 75e18

    event CommodityRegistered(
        address indexed commodity,
        string symbol,
        uint256 xauRate
    );

    event CommodityPegChecked(
        address indexed commodity,
        uint256 currentPrice,
        uint256 targetPrice,
        int256 deviationBps,
        bool isMaintained
    );

    event RebalancingTriggered(
        address indexed commodity,
        int256 deviationBps
    );

    error ZeroAddress();
    error CommodityNotRegistered();
    error InvalidXauRate();
    error InvalidThreshold();
    error XauNotSet();

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

    /**
     * @notice Set XAU (gold) address
     * @param _xauAddress XAU token address
     */
    function setXAUAddress(address _xauAddress) external onlyOwner {
        if (_xauAddress == address(0)) revert ZeroAddress();
        xauAddress = _xauAddress;
    }

    /**
     * @notice Register a commodity for pegging
     * @param commodity Commodity token address
     * @param symbol Commodity symbol (XAG, XPT, XPD, etc.)
     * @param xauRate Rate: 1 oz XAU = xauRate units of commodity (in 18 decimals)
     * @return success Whether registration was successful
     */
    function registerCommodity(
        address commodity,
        string memory symbol,
        uint256 xauRate
    ) external override onlyOwner returns (bool) {
        if (commodity == address(0)) revert ZeroAddress();
        if (xauRate == 0) revert InvalidXauRate();
        if (xauAddress == address(0)) revert XauNotSet();

        commodities[commodity] = Commodity({
            commodityAddress: commodity,
            symbol: symbol,
            xauRate: xauRate,
            isActive: true
        });

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

        emit CommodityRegistered(commodity, symbol, xauRate);
        return true;
    }

    /**
     * @notice Check commodity peg via XAU
     * @param commodity Commodity address
     * @return isMaintained Whether peg is maintained
     * @return deviationBps Deviation in basis points
     */
    function checkCommodityPeg(
        address commodity
    ) external view override returns (bool isMaintained, int256 deviationBps) {
        Commodity memory comm = commodities[commodity];
        if (comm.commodityAddress == address(0)) revert CommodityNotRegistered();

        // Get XAU price in target currency (USD)
        (uint256 xauPrice, ) = reserveSystem.getPrice(xauAddress);
        
        // Calculate target price: xauPrice / xauRate
        uint256 targetPrice = (xauPrice * 1e18) / comm.xauRate;
        
        // Get current commodity price
        (uint256 currentPrice, ) = reserveSystem.getPrice(commodity);
        
        // Calculate deviation
        if (targetPrice == 0) {
            return (false, type(int256).max);
        }

        if (currentPrice >= targetPrice) {
            uint256 diff = currentPrice - targetPrice;
            deviationBps = int256((diff * 10000) / targetPrice);
        } else {
            uint256 diff = targetPrice - currentPrice;
            deviationBps = -int256((diff * 10000) / targetPrice);
        }

        isMaintained = _abs(deviationBps) <= commodityPegThresholdBps;

        return (isMaintained, deviationBps);
    }

    /**
     * @notice Triangulate commodity value through XAU to target currency
     * @param commodity Commodity address
     * @param amount Amount of commodity
     * @param targetCurrency Target currency address (e.g., USDT for USD)
     * @return targetAmount Amount in target currency
     */
    function triangulateViaXAU(
        address commodity,
        uint256 amount,
        address targetCurrency
    ) external view override returns (uint256 targetAmount) {
        Commodity memory comm = commodities[commodity];
        if (comm.commodityAddress == address(0)) revert CommodityNotRegistered();
        if (xauAddress == address(0)) revert XauNotSet();

        // Convert commodity to XAU: amount / xauRate
        uint256 xauAmount = (amount * 1e18) / comm.xauRate;

        // Get XAU price in target currency
        (uint256 xauPrice, ) = reserveSystem.getPrice(xauAddress);
        
        // Get target currency price (should be 1e18 for stablecoins)
        (uint256 targetPrice, ) = reserveSystem.getPrice(targetCurrency);

        // Calculate: xauAmount * xauPrice / targetPrice
        targetAmount = (xauAmount * xauPrice) / (targetPrice * 1e18);

        return targetAmount;
    }

    /**
     * @notice Get commodity price in target currency
     * @param commodity Commodity address
     * @param targetCurrency Target currency address
     * @return price Price in target currency
     */
    function getCommodityPrice(
        address commodity,
        address targetCurrency
    ) external view override returns (uint256 price) {
        Commodity memory comm = commodities[commodity];
        if (comm.commodityAddress == address(0)) revert CommodityNotRegistered();
        if (xauAddress == address(0)) revert XauNotSet();

        // Get XAU price in target currency
        (uint256 xauPrice, ) = reserveSystem.getPrice(xauAddress);
        
        // Calculate commodity price: xauPrice / xauRate
        price = (xauPrice * 1e18) / comm.xauRate;

        return price;
    }

    /**
     * @notice Get commodity peg status
     * @param commodity Commodity address
     * @return currentPrice Current price
     * @return targetPrice Target price (via XAU)
     * @return deviationBps Deviation in basis points
     * @return isMaintained Whether peg is maintained
     */
    function getCommodityPegStatus(
        address commodity
    ) external view override returns (uint256 currentPrice, uint256 targetPrice, int256 deviationBps, bool isMaintained) {
        Commodity memory comm = commodities[commodity];
        if (comm.commodityAddress == address(0)) revert CommodityNotRegistered();
        if (xauAddress == address(0)) revert XauNotSet();

        // Get XAU price
        (uint256 xauPrice, ) = reserveSystem.getPrice(xauAddress);
        
        // Calculate target price
        targetPrice = (xauPrice * 1e18) / comm.xauRate;
        
        // Get current price
        (currentPrice, ) = reserveSystem.getPrice(commodity);
        
        // Calculate deviation
        if (targetPrice == 0) {
            return (currentPrice, targetPrice, type(int256).max, false);
        }

        if (currentPrice >= targetPrice) {
            uint256 diff = currentPrice - targetPrice;
            deviationBps = int256((diff * 10000) / targetPrice);
        } else {
            uint256 diff = targetPrice - currentPrice;
            deviationBps = -int256((diff * 10000) / targetPrice);
        }

        isMaintained = _abs(deviationBps) <= commodityPegThresholdBps;

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

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

    /**
     * @notice Get all supported commodities
     * @return Array of commodity addresses
     */
    function getSupportedCommodities() external view override returns (address[] memory) {
        return supportedCommodities;
    }

    /**
     * @notice Set commodity peg threshold
     * @param newThreshold New threshold in basis points
     */
    function setCommodityPegThreshold(uint256 newThreshold) external onlyOwner {
        if (newThreshold > MAX_PEG_THRESHOLD_BPS) revert InvalidThreshold();
        commodityPegThresholdBps = newThreshold;
    }

    /**
     * @notice Update XAU rate for a commodity
     * @param commodity Commodity address
     * @param newXauRate New XAU rate
     */
    function updateXauRate(address commodity, uint256 newXauRate) external onlyOwner {
        if (commodities[commodity].commodityAddress == address(0)) revert CommodityNotRegistered();
        if (newXauRate == 0) revert InvalidXauRate();
        
        commodities[commodity].xauRate = newXauRate;
    }

    // ============ 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);
    }
}

