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

import "@openzeppelin/contracts/access/Ownable.sol";
import "./AerePyth.sol";

/// Existing AereOracle interface (median multi-reporter feed, deployed 2026-05-07).
interface IAereOracleLegacy {
    function getPrice(bytes32 symbol) external view returns (uint128 price, uint64 timestamp, uint256 contributors);
}

/**
 * @title AereOracleAdapter
 * @notice Unified price-query interface for AERE Network.
 *
 *   Bridges between:
 *     - Legacy AereOracle (single multi-reporter median feed for BTC/USD + ETH/USD)
 *     - AerePyth (Pyth-compatible pull oracle; 700+ feeds when public Pyth network onboards us)
 *
 *   Routing logic (per symbol):
 *     - If a Pyth feed ID is registered for the symbol, prefer Pyth (newer, more
 *       trustworthy, signed by publisher set).
 *     - Else fall back to legacy AereOracle.
 *     - If neither has the symbol, revert.
 *
 *   This adapter lets every AERE dApp keep using a single oracle address and one
 *   `getPrice(symbol)` signature, while the underlying data source migrates
 *   from AereOracle → AerePyth → canonical Pyth as the network matures.
 *
 *   Symbol convention (bytes32):
 *     keccak256("BTC/USD"), keccak256("ETH/USD"), keccak256("AERE/USD"), etc.
 */
contract AereOracleAdapter is Ownable {
    IAereOracleLegacy public legacy;
    AerePyth public pyth;

    /// symbol (keccak256) → Pyth price feed ID. If unset, fall back to legacy.
    mapping(bytes32 => bytes32) public pythFeedId;

    /// Maximum age (seconds) accepted for legacy oracle reads.
    uint256 public legacyMaxAge = 600;

    event PythRegistered(bytes32 indexed symbol, bytes32 indexed pythFeedId);
    event SourceUpdated(string source, address indexed addr);

    constructor(IAereOracleLegacy _legacy, AerePyth _pyth) {
        legacy = _legacy;
        pyth = _pyth;
    }

    // ───────────────────── Admin ─────────────────────

    function setLegacy(IAereOracleLegacy _legacy) external onlyOwner {
        legacy = _legacy;
        emit SourceUpdated("legacy", address(_legacy));
    }

    function setPyth(AerePyth _pyth) external onlyOwner {
        pyth = _pyth;
        emit SourceUpdated("pyth", address(_pyth));
    }

    function registerPythFeed(bytes32 symbol, bytes32 feedId) external onlyOwner {
        pythFeedId[symbol] = feedId;
        emit PythRegistered(symbol, feedId);
    }

    function setLegacyMaxAge(uint256 _age) external onlyOwner {
        legacyMaxAge = _age;
    }

    // ───────────────────── Unified read ─────────────────────

    /**
     * @notice Universal price read.
     * @return price1e8 Price scaled to 1e8 (same as AereOracle's format).
     * @return updatedAt unix seconds.
     * @return source "pyth" or "legacy".
     */
    function quote(bytes32 symbol) external view returns (uint256 price1e8, uint256 updatedAt, string memory source) {
        bytes32 feedId = pythFeedId[symbol];
        if (feedId != bytes32(0)) {
            PythPrice memory p = pyth.getPriceUnsafe(feedId);
            require(p.publishTime > 0, "Adapter: no pyth data");
            // Pyth stores price + exponent; normalise to 1e8.
            // expo is negative for fractional prices (e.g. -8 for $30000.12345678).
            uint256 abs;
            if (p.price < 0) {
                // Negative price means market closed or invalid; revert.
                revert("Adapter: negative price");
            }
            abs = uint256(uint64(p.price));
            int32 expo = p.expo;
            if (expo == -8) {
                price1e8 = abs;
            } else if (expo > -8) {
                // expo is e.g. -6 (USDC), -2 (some equities). Scale up.
                price1e8 = abs * (10 ** uint256(int256(-8 - expo)));
            } else {
                // expo is e.g. -12, -18 (some assets). Scale down.
                price1e8 = abs / (10 ** uint256(int256(expo + 8)));
            }
            updatedAt = p.publishTime;
            source = "pyth";
            return (price1e8, updatedAt, source);
        }

        // Fall back to legacy AereOracle.
        (uint128 lp, uint64 lts, ) = legacy.getPrice(symbol);
        require(lp > 0, "Adapter: no legacy data");
        require(block.timestamp - uint256(lts) <= legacyMaxAge, "Adapter: legacy stale");
        price1e8 = uint256(lp);
        updatedAt = uint256(lts);
        source = "legacy";
    }

    /// Convenience: just return the price1e8.
    function priceOf(bytes32 symbol) external view returns (uint256 price1e8) {
        (price1e8, , ) = this.quote(symbol);
    }
}
