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

import "forge-std/Test.sol";
import "../src/ExchangeRateOracle.sol";
import "../src/interfaces/IChainlinkPriceFeed.sol";

contract MockPriceFeed is IChainlinkPriceFeed {
    int256 private _price;
    uint256 private _updatedAt;
    uint8 private _decimals;

    constructor(int256 price, uint8 dec) {
        _price = price;
        _updatedAt = block.timestamp;
        _decimals = dec;
    }

    function setPrice(int256 price) external {
        _price = price;
        _updatedAt = block.timestamp;
    }

    function setUpdatedAt(uint256 ts) external {
        _updatedAt = ts;
    }

    function latestRoundData()
        external
        view
        override
        returns (uint80, int256, uint256, uint256, uint80)
    {
        return (1, _price, block.timestamp, _updatedAt, 1);
    }

    function decimals() external view override returns (uint8) {
        return _decimals;
    }

    function description() external pure override returns (string memory) {
        return "Mock Price Feed";
    }
}

contract ExchangeRateOracleTest is Test {
    ExchangeRateOracle oracle;
    MockPriceFeed cnyFeed;
    MockPriceFeed jpyFeed;

    address admin = address(1);

    function setUp() public {
        vm.startPrank(admin);

        oracle = new ExchangeRateOracle();
        oracle.initialize(900, admin); // 15 min staleness

        cnyFeed = new MockPriceFeed(7_2500_0000, 8); // 7.25 CNY/USD
        jpyFeed = new MockPriceFeed(150_0000_0000, 8); // 150 JPY/USD

        oracle.setPriceFeed("CNY", address(cnyFeed));
        oracle.setPriceFeed("JPY", address(jpyFeed));

        vm.stopPrank();
    }

    function test_GetRateCNY() public view {
        uint256 rate = oracle.getRate("CNY");
        assertEq(rate, 7_2500_0000);
    }

    function test_GetRateJPY() public view {
        uint256 rate = oracle.getRate("JPY");
        assertEq(rate, 150_0000_0000);
    }

    function test_UnsupportedCurrencyReverts() public {
        vm.expectRevert("Oracle: unsupported currency");
        oracle.getRate("EUR");
    }

    function test_StaleDataReverts() public {
        // Ensure block.timestamp is large enough to avoid underflow
        vm.warp(2000);
        // 设置 CNY feed 过期（20分钟前更新）
        cnyFeed.setUpdatedAt(block.timestamp - 1200);

        vm.expectRevert("Oracle: stale price data");
        oracle.getRate("CNY");
    }

    function test_GetRateDetails() public view {
        (uint256 rate, uint256 updatedAt, uint8 decimals) = oracle.getRateDetails("CNY");
        assertEq(rate, 7_2500_0000);
        assertGt(updatedAt, 0);
        assertEq(decimals, 8);
    }

    function test_SetMaxStaleness() public {
        vm.prank(admin);
        oracle.setMaxStaleness(600); // 10 min
        assertEq(oracle.maxStaleness(), 600);
    }

    function test_RemovePriceFeed() public {
        vm.prank(admin);
        oracle.removePriceFeed("CNY");

        vm.expectRevert("Oracle: unsupported currency");
        oracle.getRate("CNY");
    }

    function test_SupportedCurrencyCount() public view {
        assertEq(oracle.supportedCurrencyCount(), 2);
    }

    function test_InvalidPriceReverts() public {
        MockPriceFeed badFeed = new MockPriceFeed(0, 8);
        vm.prank(admin);
        oracle.setPriceFeed("BAD", address(badFeed));

        vm.expectRevert("Oracle: invalid price");
        oracle.getRate("BAD");
    }
}
