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

import {Script, console} from "forge-std/Script.sol";
import {Aggregator} from "../../contracts/oracle/Aggregator.sol";

interface IOraclePriceFeedWire {
    function aggregators(address asset) external view returns (address);
    function setAggregator(address asset, address aggregator, uint256 multiplier) external;
    function updatePriceFeed(address asset) external;
    function setUpdateInterval(uint256 interval) external;
    function updateInterval() external view returns (uint256);
}

interface IPriceFeedKeeperWire {
    function isTracked(address asset) external view returns (bool);
    function trackAsset(address asset) external;
    function maxUpdatesPerCall() external view returns (uint256);
    function setMaxUpdatesPerCall(uint256 max) external;
}

interface IDODOPMMIntegrationWire {
    function setReserveSystem(address reserveSystem_) external;
    function reserveSystem() external view returns (address);
}

interface IReserveSystemWire {
    function updatePriceFeed(address asset, uint256 price, uint256 timestamp) external;
}

interface IAggregatorWire {
    function addTransmitter(address transmitter) external;
    function updateAnswer(uint256 answer) external;
    function isTransmitter(address transmitter) external view returns (bool);
    function latestRoundData()
        external
        view
        returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}

/// @notice Wire canonical Chain 138 assets to Chainlink-mirrored repo aggregators + keeper + reserve.
contract WireChain138OraclePegs138 is Script {
    uint256 internal constant MULT = 1e10;

    address internal constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address internal constant WETH10 = 0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9F;
    address internal constant LINK = 0xb7721dD53A8c629d9f1Ba31a5819AFe250002b03;
    address internal constant CUSDT = 0x93E66202A11B1772E55407B32B44e5Cd8eda7f22;
    address internal constant CUSDC = 0xf22258f57794CC8E06237084b353Ab30fFfa640b;
    address internal constant CEURC = 0x8085961F9cF02b4d800A3c6d386D31da4B34266a;
    address internal constant CEURT = 0xdf4b71c61E5912712C1Bdd451416B9aC26949d72;
    address internal constant CGBPC = 0x003960f16D9d34F2e98d62723B6721Fb92074aD2;
    address internal constant CGBPT = 0x350f54e4D23795f86A9c03988c7135357CCaD97c;
    address internal constant CAUDC = 0xD51482e567c03899eecE3CAe8a058161FD56069D;
    address internal constant CJPYC = 0xEe269e1226a334182aace90056EE4ee5Cc8A6770;
    address internal constant CCHFC = 0x873990849DDa5117d7C644f0aF24370797C03885;
    address internal constant CCADC = 0x54dBd40cF05e15906A2C21f600937e96787f5679;
    address internal constant CXAUC = 0x290E52a8819A4fbD0714E517225429aA2B70EC6b;
    address internal constant CXAUT = 0x94e408E26c6FD8F4ee00b54dF19082FDA07dC96E;
    address internal constant CBTC = 0xe94260c555aC1d9D3CC9E1632883452ebDf0082E;

    function run() external {
        require(block.chainid == 138, "ChainID 138 only");

        uint256 pk = vm.envUint("PRIVATE_KEY");
        address deployer = vm.addr(pk);

        address opf = vm.envAddress("ORACLE_PRICE_FEED");
        address keeperAddr = vm.envAddress("PRICE_FEED_KEEPER_ADDRESS");
        address rs = vm.envAddress("RESERVE_SYSTEM");
        address ethAgg = vm.envAddress("AGGREGATOR_ADDRESS");
        address dodo = vm.envAddress("CHAIN_138_DODO_PMM_INTEGRATION");
        uint256 ethSeed8 = vm.envOr("FEED_ETH_USD_8", uint256(0));

        IOraclePriceFeedWire op = IOraclePriceFeedWire(opf);
        IPriceFeedKeeperWire keeper = IPriceFeedKeeperWire(keeperAddr);

        console.log("=== Wire Chain 138 oracle pegs ===");
        console.log("Deployer:", deployer);

        vm.startBroadcast(pk);

        if (op.updateInterval() != 86400) {
            try op.setUpdateInterval(86400) {} catch {}
        }

        if (ethSeed8 > 0) {
            IAggregatorWire eth = IAggregatorWire(ethAgg);
            if (!eth.isTransmitter(deployer)) {
                try eth.addTransmitter(deployer) {} catch {}
            }
            try eth.updateAnswer(ethSeed8) {} catch {}
        }

        if (keeper.maxUpdatesPerCall() < 20) {
            keeper.setMaxUpdatesPerCall(20);
        }

        address usdAgg = _deployMirror("USD/USD Chainlink mirror", vm.envUint("FEED_USD_USD_8"));
        address linkAgg = _deployMirror("LINK/USD Chainlink mirror", vm.envUint("FEED_LINK_USD_8"));
        address btcAgg = _deployMirror("BTC/USD Chainlink mirror", vm.envUint("FEED_BTC_USD_8"));
        address xauAgg = _deployMirror("XAU/USD Chainlink mirror", vm.envUint("FEED_XAU_USD_8"));
        address eurAgg = _deployMirror("EUR/USD Chainlink mirror", vm.envUint("FEED_EUR_USD_8"));
        address gbpAgg = _deployMirror("GBP/USD Chainlink mirror", vm.envUint("FEED_GBP_USD_8"));
        address audAgg = _deployMirror("AUD/USD Chainlink mirror", vm.envUint("FEED_AUD_USD_8"));
        address jpyAgg = _deployMirror("JPY/USD Chainlink mirror", vm.envUint("FEED_JPY_USD_8"));
        address chfAgg = _deployMirror("CHF/USD Chainlink mirror", vm.envUint("FEED_CHF_USD_8"));
        address cadAgg = _deployMirror("CAD/USD Chainlink mirror", vm.envUint("FEED_CAD_USD_8"));

        _wire(op, keeper, rs, WETH9, ethAgg);
        _wire(op, keeper, rs, WETH10, ethAgg);
        _wire(op, keeper, rs, LINK, linkAgg);
        _wire(op, keeper, rs, CUSDT, usdAgg);
        _wire(op, keeper, rs, CUSDC, usdAgg);
        _wire(op, keeper, rs, CEURC, eurAgg);
        _wire(op, keeper, rs, CEURT, eurAgg);
        _wire(op, keeper, rs, CGBPC, gbpAgg);
        _wire(op, keeper, rs, CGBPT, gbpAgg);
        _wire(op, keeper, rs, CAUDC, audAgg);
        _wire(op, keeper, rs, CJPYC, jpyAgg);
        _wire(op, keeper, rs, CCHFC, chfAgg);
        _wire(op, keeper, rs, CCADC, cadAgg);
        _wire(op, keeper, rs, CXAUC, xauAgg);
        _wire(op, keeper, rs, CXAUT, xauAgg);
        _wire(op, keeper, rs, CBTC, btcAgg);

        IDODOPMMIntegrationWire dodoInt = IDODOPMMIntegrationWire(dodo);
        if (dodoInt.reserveSystem() != rs) {
            dodoInt.setReserveSystem(rs);
            console.log("DODO reserveSystem wired:", rs);
        }

        vm.stopBroadcast();

        console.log("=== Done ===");
    }

    function _deployMirror(string memory description, uint256 price8) internal returns (address) {
        require(price8 > 0, "missing seed price");
        Aggregator agg = new Aggregator(description, msg.sender, 3600, 50);
        agg.addTransmitter(msg.sender);
        agg.updateAnswer(price8);
        console.log("Deployed feed:", description);
        console.log("  aggregator:", address(agg));
        return address(agg);
    }

    function _wire(
        IOraclePriceFeedWire op,
        IPriceFeedKeeperWire keeper,
        address rs,
        address token,
        address agg
    ) internal {
        require(agg != address(0), "zero aggregator");
        if (op.aggregators(token) == address(0)) {
            op.setAggregator(token, agg, MULT);
        }
        if (!keeper.isTracked(token)) {
            keeper.trackAsset(token);
        }
        try op.updatePriceFeed(token) {} catch {
            (, int256 ans,, uint256 updatedAt,) = IAggregatorWire(agg).latestRoundData();
            require(ans > 0, "bad agg price");
            IReserveSystemWire(rs).updatePriceFeed(token, uint256(ans) * MULT, updatedAt);
        }
    }
}
