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

import {Test} from "forge-std/Test.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
import {SimpleERC3156FlashVault} from "../../contracts/flash/SimpleERC3156FlashVault.sol";
import {CrossChainFlashBorrower} from "../../contracts/flash/CrossChainFlashBorrower.sol";
import {ICrossChainFlashBridge} from "../../contracts/flash/interfaces/ICrossChainFlashBridge.sol";

contract MockCrossChainBridge is ICrossChainFlashBridge {
    event BridgeCalled(address token, uint256 amount, uint64 dest, address recipient, bytes extra, uint256 value);

    function bridgeTokensFrom(
        address token,
        uint256 amount,
        uint64 destinationChainSelector,
        address recipientOnDestination,
        bytes calldata extraData
    ) external payable override returns (bytes32 messageId) {
        IERC20(token).transferFrom(msg.sender, address(this), amount);
        emit BridgeCalled(token, amount, destinationChainSelector, recipientOnDestination, extraData, msg.value);
        messageId = keccak256(abi.encodePacked(block.number, token, amount));
    }
}

contract MockERC20Mint is ERC20 {
    constructor() ERC20("T", "T") {}

    function mint(address to, uint256 v) external {
        _mint(to, v);
    }
}

contract CrossChainFlashBorrowerTest is Test {
    SimpleERC3156FlashVault internal vault;
    MockERC20Mint internal token;
    MockCrossChainBridge internal bridge;
    CrossChainFlashBorrower internal borrower;

    address internal owner = address(0xA11);
    address internal user = address(0xB22);

    function setUp() public {
        vm.startPrank(owner);
        vault = new SimpleERC3156FlashVault(owner, 5);
        token = new MockERC20Mint();
        token.mint(address(vault), 1_000_000e18);
        vault.setTokenSupported(address(token), true);
        vm.stopPrank();

        bridge = new MockCrossChainBridge();
        borrower = new CrossChainFlashBorrower(address(vault));
    }

    function test_flashBridge_out_repaysFromPrefund() public {
        uint256 amount = 40_000e18;
        uint256 fee = vault.flashFee(address(token), amount);
        uint256 bridgeAmount = amount;

        token.mint(address(borrower), bridgeAmount + fee);

        CrossChainFlashBorrower.CrossChainFlashParams memory p = CrossChainFlashBorrower.CrossChainFlashParams({
            bridge: address(bridge),
            bridgeAmount: bridgeAmount,
            destinationChainSelector: 123,
            recipientOnDestination: address(0xbeef),
            bridgeExtraData: hex"abcd",
            nativeBridgeFee: 0
        });

        vm.prank(user);
        vault.flashLoan(IERC3156FlashBorrower(address(borrower)), address(token), amount, abi.encode(p));

        assertEq(token.balanceOf(address(bridge)), bridgeAmount);
        assertEq(vault.totalFeesCollected(address(token)), fee);
    }
}
