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

import {Test} from "forge-std/Test.sol";

import {IRouterClient} from "../../contracts/ccip/IRouterClient.sol";
import {OfficialStableMirrorToken} from "../../contracts/tokens/OfficialStableMirrorToken.sol";
import {CompliantUSDT} from "../../contracts/tokens/CompliantUSDT.sol";
import {CompliantUSDC} from "../../contracts/tokens/CompliantUSDC.sol";
import {StablecoinReserveVault} from "../../contracts/reserve/StablecoinReserveVault.sol";
import {CWMultiTokenBridgeL1} from "../../contracts/bridge/CWMultiTokenBridgeL1.sol";
import {CWReserveVerifier} from "../../contracts/bridge/integration/CWReserveVerifier.sol";

contract MockRouterVaultVerifier is IRouterClient {
    uint256 public fee;
    bytes32 public nextMessageId = keccak256("cw-reserve-vault-message");

    function ccipSend(
        uint64,
        EVM2AnyMessage memory
    ) external payable returns (bytes32 messageId, uint256 fees) {
        return (nextMessageId, fee);
    }

    function getFee(uint64, EVM2AnyMessage memory) external view returns (uint256) {
        return fee;
    }

    function getSupportedTokens(uint64) external pure returns (address[] memory tokens) {
        tokens = new address[](0);
    }
}

contract CWReserveVerifierVaultIntegrationTest is Test {
    uint64 internal constant AVALANCHE_SELECTOR = 6433500567565415381;

    address internal user = address(0xBEEF);
    address internal receiveRouter = address(0x138138);
    address internal peerBridge = address(0x4311443114);

    MockRouterVaultVerifier internal router;
    OfficialStableMirrorToken internal officialUsdt;
    OfficialStableMirrorToken internal officialUsdc;
    CompliantUSDT internal compliantUsdt;
    CompliantUSDC internal compliantUsdc;
    StablecoinReserveVault internal vault;
    CWMultiTokenBridgeL1 internal l1Bridge;
    CWReserveVerifier internal verifier;

    function setUp() public {
        router = new MockRouterVaultVerifier();
        officialUsdt = new OfficialStableMirrorToken("Tether USD (Chain 138)", "USDT", 6, address(this), 0);
        officialUsdc = new OfficialStableMirrorToken("USD Coin (Chain 138)", "USDC", 6, address(this), 0);
        compliantUsdt = new CompliantUSDT(address(this), address(this));
        compliantUsdc = new CompliantUSDC(address(this), address(this));
        vault = new StablecoinReserveVault(
            address(this),
            address(officialUsdt),
            address(officialUsdc),
            address(compliantUsdt),
            address(compliantUsdc)
        );
        l1Bridge = new CWMultiTokenBridgeL1(address(router), receiveRouter, address(0));
        verifier = new CWReserveVerifier(address(this), address(l1Bridge), address(vault), address(0));

        uint256 canonicalSupply = compliantUsdc.totalSupply();
        officialUsdc.mint(address(this), canonicalSupply);
        officialUsdc.approve(address(vault), canonicalSupply);
        vault.seedUSDCReserve(canonicalSupply);
        compliantUsdc.transferOwnership(address(vault));
        compliantUsdc.transfer(user, 250e6);

        l1Bridge.configureSupportedCanonicalToken(address(compliantUsdc), true);
        l1Bridge.configureDestination(address(compliantUsdc), AVALANCHE_SELECTOR, peerBridge, true);
        l1Bridge.setReserveVerifier(address(verifier));

        verifier.configureToken(
            address(compliantUsdc),
            address(0),
            true,
            false,
            true
        );
    }

    function testVerifierAllowsLockWhenRealVaultBacksExistingSupply() public {
        uint256 amount = 25e6;

        vm.startPrank(user);
        compliantUsdc.approve(address(l1Bridge), amount);
        l1Bridge.lockAndSend(address(compliantUsdc), AVALANCHE_SELECTOR, user, amount);
        vm.stopPrank();

        assertEq(l1Bridge.lockedBalance(address(compliantUsdc)), amount);
        assertEq(l1Bridge.totalOutstanding(address(compliantUsdc)), amount);
        assertEq(l1Bridge.outstandingMinted(address(compliantUsdc), AVALANCHE_SELECTOR), amount);
    }
}
