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

import {Test, console} from "forge-std/Test.sol";
import {CCIPWETH10Bridge} from "../contracts/ccip/CCIPWETH10Bridge.sol";
import {WETH10} from "../contracts/tokens/WETH10.sol";
import {IRouterClient} from "../contracts/ccip/IRouterClient.sol";

interface IERC20 {
    function approve(address spender, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

contract MockLinkToken {
    mapping(address => uint256) public balanceOf;
    
    function mint(address to, uint256 amount) external {
        balanceOf[to] += amount;
    }
    
    function transfer(address to, uint256 amount) external returns (bool) {
        balanceOf[msg.sender] -= amount;
        balanceOf[to] += amount;
        return true;
    }
    
    function transferFrom(address from, address to, uint256 amount) external returns (bool) {
        balanceOf[from] -= amount;
        balanceOf[to] += amount;
        return true;
    }
    
    function approve(address spender, uint256 amount) external returns (bool) {
        return true;
    }
}

contract MockCCIPRouter10 is IRouterClient {
    mapping(bytes32 => bool) public messages;
    uint256 public fee = 0.001 ether;
    
    function ccipSend(
        uint64 destinationChainSelector,
        EVM2AnyMessage memory message
    ) external payable override returns (bytes32 messageId, uint256 fees) {
        messageId = keccak256(abi.encode(block.timestamp, msg.sender, message));
        messages[messageId] = true;
        fees = fee;
        emit MessageSent(
            messageId,
            destinationChainSelector,
            msg.sender,
            message.receiver,
            message.data,
            message.tokenAmounts,
            message.feeToken,
            message.extraArgs
        );
    }
    
    function getFee(
        uint64 destinationChainSelector,
        EVM2AnyMessage memory message
    ) external view override returns (uint256) {
        return fee;
    }
    
    function getSupportedTokens(
        uint64 destinationChainSelector
    ) external pure override returns (address[] memory) {
        return new address[](0);
    }
    
    // Note: In real CCIP, tokens are automatically transferred by the router
    // This mock is simplified for testing
}

contract CCIPWETH10BridgeTest is Test {
    CCIPWETH10Bridge public bridge;
    WETH10 public weth10;
    MockCCIPRouter10 public mockRouter;
    MockLinkToken public feeToken;
    address public user = address(1);
    address public recipient = address(2);
    uint64 public destinationChainSelector = 1;
    
    function setUp() public {
        // Deploy WETH10
        weth10 = new WETH10();
        
        // Deploy Mock LINK token
        feeToken = new MockLinkToken();
        
        // Deploy Mock CCIP Router
        mockRouter = new MockCCIPRouter10();
        
        // Deploy Bridge
        bridge = new CCIPWETH10Bridge(
            address(mockRouter),
            address(weth10),
            address(feeToken)
        );
        
        // Setup user
        vm.deal(user, 10 ether);
        vm.prank(user);
        weth10.deposit{value: 5 ether}();
        
        // Fund user with LINK
        feeToken.mint(user, 10 ether);
    }
    
    function testAddDestination() public {
        address receiverBridge = address(0x456);
        
        vm.prank(bridge.admin());
        bridge.addDestination(destinationChainSelector, receiverBridge);
        
        (uint64 chainSelector, address receiverBridge_, bool enabled) = 
            bridge.destinations(destinationChainSelector);
        
        assertEq(chainSelector, destinationChainSelector);
        assertEq(receiverBridge_, receiverBridge);
        assertTrue(enabled);
    }
    
    function testSendCrossChain() public {
        address receiverBridge = address(0x456);
        uint256 amount = 1 ether;
        
        // Add destination
        vm.prank(bridge.admin());
        bridge.addDestination(destinationChainSelector, receiverBridge);
        
        // Approve bridge
        vm.prank(user);
        weth10.approve(address(bridge), amount);
        
        // Approve fee token
        vm.prank(user);
        feeToken.approve(address(bridge), 1 ether);
        
        // Send cross-chain
        vm.prank(user);
        bytes32 messageId = bridge.sendCrossChain(
            destinationChainSelector,
            recipient,
            amount
        );
        
        assertTrue(messageId != bytes32(0));
        assertEq(weth10.balanceOf(user), 4 ether);
        assertEq(weth10.balanceOf(address(bridge)), amount);
    }
    
    function testReceiveCrossChain() public {
        uint256 amount = 1 ether;
        address sourceSender = address(0x789);
        uint64 sourceChainSelector = 2;
        
        // Deposit WETH10 to bridge for testing (simulating CCIP token transfer)
        vm.deal(address(this), amount);
        weth10.deposit{value: amount}();
        weth10.transfer(address(bridge), amount);
        
        // Prepare message
        bytes32 messageId = keccak256("test-message");
        bytes memory data = abi.encode(recipient, amount, sourceSender, 1);
        
        IRouterClient.TokenAmount[] memory tokenAmounts = new IRouterClient.TokenAmount[](1);
        tokenAmounts[0] = IRouterClient.TokenAmount({
            token: address(weth10),
            amount: amount,
            amountType: IRouterClient.TokenAmountType.Fiat
        });
        
        // Simulate receive (mock router calls bridge - tokens already transferred)
        vm.prank(address(mockRouter));
        bridge.ccipReceive(
            IRouterClient.Any2EVMMessage({
                messageId: messageId,
                sourceChainSelector: sourceChainSelector,
                sender: abi.encode(sourceSender),
                data: data,
                tokenAmounts: tokenAmounts
            })
        );
        
        assertEq(weth10.balanceOf(recipient), amount);
        assertTrue(bridge.processedTransfers(messageId));
    }
    
    function testCalculateFee() public {
        address receiverBridge = address(0x456);
        uint256 amount = 1 ether;
        
        // Add destination
        vm.prank(bridge.admin());
        bridge.addDestination(destinationChainSelector, receiverBridge);
        
        // Calculate fee
        uint256 fee = bridge.calculateFee(destinationChainSelector, amount);
        assertEq(fee, mockRouter.fee());
    }
}

