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

import "forge-std/Test.sol";
import "../../contracts/iso4217w/oracle/ReserveOracle.sol";

contract ReserveOracleTest is Test {
    ReserveOracle public oracle;

    address public admin = address(0x1);
    address public oracle1 = address(0x2);
    address public oracle2 = address(0x3);
    address public oracle3 = address(0x4);
    
    string public constant CURRENCY_CODE = "USD";

    uint256 public constant STALENESS_THRESHOLD = 3600; // 1 hour
    uint256 public constant QUORUM_SIZE = 3;

    function setUp() public {
        vm.warp(STALENESS_THRESHOLD + 1); // Avoid underflow: block.timestamp - stalenessThreshold
        vm.startPrank(admin);
        oracle = new ReserveOracle(admin, QUORUM_SIZE, STALENESS_THRESHOLD);

        // Add oracles (also grants ORACLE_ROLE)
        oracle.addOracle(oracle1);
        oracle.addOracle(oracle2);
        oracle.addOracle(oracle3);

        vm.stopPrank();
    }

    function test_SubmitReserveReport() public {
        uint256 reserve = 10000e2;
        bytes32 attestationHash = keccak256("attestation");

        // Submit from all 3 oracles to reach quorum (QUORUM_SIZE=3)
        vm.prank(oracle1);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);
        vm.prank(oracle2);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);
        vm.prank(oracle3);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        (uint256 verifiedReserve, uint256 timestamp) = oracle.getVerifiedReserve(CURRENCY_CODE);
        assertEq(verifiedReserve, reserve);
        assertGt(timestamp, 0);
    }

    function test_GetVerifiedReserve_Quorum() public {
        // Submit reports from all 3 oracles
        uint256 reserve = 10000e2;
        bytes32 attestationHash = keccak256("attestation");

        vm.prank(oracle1);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        vm.prank(oracle2);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        vm.prank(oracle3);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        (uint256 verifiedReserve, ) = oracle.getVerifiedReserve(CURRENCY_CODE);
        
        // Should get consensus (all same)
        assertEq(verifiedReserve, reserve);
    }

    function test_IsQuorumMet() public {
        uint256 reserve = 10000e2;
        bytes32 attestationHash = keccak256("attestation");

        // Submit from 2 oracles (not quorum yet)
        vm.prank(oracle1);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        vm.prank(oracle2);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        (bool quorum1, ) = oracle.isQuorumMet(CURRENCY_CODE);
        assertFalse(quorum1, "Should not have quorum with 2/3");

        // Submit from third oracle
        vm.prank(oracle3);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        (bool quorum2, ) = oracle.isQuorumMet(CURRENCY_CODE);
        assertTrue(quorum2, "Should have quorum with 3/3");
    }

    function test_RemoveStaleReports() public {
        uint256 reserve = 10000e2;
        bytes32 attestationHash = keccak256("attestation");

        vm.prank(oracle1);
        oracle.submitReserveReport(CURRENCY_CODE, reserve, attestationHash);

        // Fast forward time to make reports stale
        vm.warp(block.timestamp + STALENESS_THRESHOLD + 1);

        // Stale reports should not count towards quorum
        (bool quorum, ) = oracle.isQuorumMet(CURRENCY_CODE);
        assertFalse(quorum, "Stale reports should not count");
    }

    function test_GetConsensusReserve() public {
        bytes32 attestationHash = keccak256("attestation");

        // Submit different reports
        vm.prank(oracle1);
        oracle.submitReserveReport(CURRENCY_CODE, 10000e2, attestationHash);

        vm.prank(oracle2);
        oracle.submitReserveReport(CURRENCY_CODE, 10001e2, attestationHash); // 1 USD difference

        vm.prank(oracle3);
        oracle.submitReserveReport(CURRENCY_CODE, 9999e2, attestationHash); // 1 USD difference

        // Consensus should be median or average
        uint256 consensus = oracle.getConsensusReserve(CURRENCY_CODE);
        assertGt(consensus, 0, "Should have consensus value");
    }
}
