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

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

contract NetworkResilienceTest is Test {
    Aggregator public aggregator;
    address public transmitter1;
    address public transmitter2;
    address public transmitter3;
    
    function setUp() public {
        aggregator = new Aggregator("ETH/USD", address(this), 60, 50);
        
        transmitter1 = address(0x111);
        transmitter2 = address(0x222);
        transmitter3 = address(0x333);
        
        aggregator.addTransmitter(transmitter1);
        aggregator.addTransmitter(transmitter2);
        aggregator.addTransmitter(transmitter3);
    }
    
    function testOracleContinuesWithOneTransmitterFailure() public {
        uint256 price1 = 25000000000;
        uint256 price2 = 25100000000;
        
        // Transmitter 1 updates
        vm.prank(transmitter1);
        aggregator.updateAnswer(price1);
        
        // Fast forward past heartbeat to create new round
        vm.warp(block.timestamp + 61);
        
        // Transmitter 1 fails, transmitter 2 continues with new round
        vm.prank(transmitter2);
        aggregator.updateAnswer(price2);
        
        (uint256 roundId, int256 answer, , , ) = aggregator.latestRoundData();
        assertEq(uint256(answer), price2, "Oracle should continue with remaining transmitters");
        assertGt(roundId, 1, "Should create new round");
    }
    
    function testOracleHandlesMultipleTransmitterFailures() public {
        uint256 price1 = 25000000000;
        uint256 price2 = 25100000000;
        uint256 price3 = 25200000000;
        
        // All transmitters update in round 1
        vm.prank(transmitter1);
        aggregator.updateAnswer(price1);
        
        vm.prank(transmitter2);
        aggregator.updateAnswer(price1);
        
        vm.prank(transmitter3);
        aggregator.updateAnswer(price1);
        
        // Fast forward past heartbeat to create new round
        vm.warp(block.timestamp + 61);
        
        // Two transmitters fail, one continues with new round
        vm.prank(transmitter3);
        aggregator.updateAnswer(price3);
        
        (uint256 roundId, int256 answer, , , ) = aggregator.latestRoundData();
        assertEq(uint256(answer), price3, "Oracle should continue with remaining transmitter");
        assertGt(roundId, 1, "Should create new round");
    }
    
    function testOracleRecoversFromPause() public {
        uint256 price1 = 25000000000;
        uint256 price2 = 25100000000;
        
        // Update before pause
        vm.prank(transmitter1);
        aggregator.updateAnswer(price1);
        
        // Verify initial state
        (uint256 roundId1, int256 answer1, , , ) = aggregator.latestRoundData();
        assertEq(uint256(answer1), price1, "Initial price should be set");
        
        // Pause oracle (must be called by admin)
        aggregator.pause();
        
        // Try to update (should fail)
        vm.prank(transmitter1);
        vm.expectRevert("Aggregator: paused");
        aggregator.updateAnswer(price2);
        
        // Unpause (must be called by admin)
        aggregator.unpause();
        
        // Fast forward past heartbeat to ensure new round is created
        vm.warp(block.timestamp + 61);
        
        // Update should work again
        vm.prank(transmitter1);
        aggregator.updateAnswer(price2);
        
        (uint256 roundId2, int256 answer2, , , ) = aggregator.latestRoundData();
        assertEq(uint256(answer2), price2, "Oracle should recover after unpause");
        assertGt(roundId2, roundId1, "Should create new round after unpause");
    }
    
    function testOracleHandlesStaleData() public {
        uint256 price1 = 25000000000;
        
        // Initial update
        vm.prank(transmitter1);
        aggregator.updateAnswer(price1);
        
        // Fast forward past heartbeat
        vm.warp(block.timestamp + 61);
        
        // New update should create new round
        vm.prank(transmitter1);
        aggregator.updateAnswer(price1);
        
        (uint256 roundId, , , , ) = aggregator.latestRoundData();
        assertGt(roundId, 1, "Should create new round after heartbeat");
    }
    
    function testOracleHandlesPriceDeviation() public {
        uint256 price1 = 25000000000; // $250.00
        uint256 price2 = 25125000000; // $251.25 (0.5% deviation)
        
        // Initial update
        vm.prank(transmitter1);
        aggregator.updateAnswer(price1);
        
        // Update with deviation
        vm.prank(transmitter1);
        aggregator.updateAnswer(price2);
        
        (uint256 roundId, int256 answer, , , ) = aggregator.latestRoundData();
        assertEq(uint256(answer), price2, "Oracle should accept price deviation");
        assertGt(roundId, 1, "Should create new round on deviation");
    }
}

