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

import {Test, console} from "forge-std/Test.sol";
import {TreasuryVault} from "../../contracts/treasury/TreasuryVault.sol";
import {CcipBridgeAdapter138} from "../../contracts/treasury/CcipBridgeAdapter138.sol";
import {StrategyExecutor138} from "../../contracts/treasury/StrategyExecutor138.sol";

contract MockWETH {
    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, uint256) external pure returns (bool) { return true; }
}

contract MockBridge {
    function sendCrossChain(uint64, address, uint256) external payable returns (bytes32) {
        return keccak256("ok");
    }
}

contract StrategyExecutor138Test is Test {
    TreasuryVault public vault;
    MockBridge public bridge;
    CcipBridgeAdapter138 public adapter;
    StrategyExecutor138 public executor;
    MockWETH public weth9;
    address public admin;
    address public keeper;
    address public receiverMainnet = address(0x99);

    function setUp() public {
        admin = address(1);
        keeper = address(2);
        weth9 = new MockWETH();
        vault = new TreasuryVault(admin);
        bridge = new MockBridge();
        adapter = new CcipBridgeAdapter138(
            address(weth9),
            address(bridge),
            receiverMainnet,
            admin
        );
        executor = new StrategyExecutor138(address(vault), address(adapter), admin);
        vm.startPrank(admin);
        adapter.setStrategyExecutor(address(executor));
        vault.setModule(address(executor), true);
        vault.setModule(address(adapter), true);
        vault.setToken(address(weth9), true);
        executor.setToken(address(weth9), true);
        executor.grantRole(executor.KEEPER_ROLE(), keeper);
        adapter.setExportsEnabled(true);
        vm.stopPrank();

        weth9.mint(address(vault), 100e18);
    }

    function test_exportToMainnet_revert_tokenNotApproved() public {
        MockWETH other = new MockWETH();
        other.mint(address(vault), 50e18);
        vm.prank(admin);
        vault.setToken(address(other), true);
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.TokenNotApproved.selector);
        executor.exportToMainnet(address(other), 50e18, block.timestamp + 3600);
    }

    function test_exportToMainnet_revert_cooldown() public {
        vm.prank(admin);
        executor.setCooldownBlocks(1);
        vm.prank(keeper);
        executor.exportToMainnet(address(weth9), 10e18, block.timestamp + 3600);
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.CooldownNotElapsed.selector);
        executor.exportToMainnet(address(weth9), 10e18, block.timestamp + 3600);
    }

    function test_setExportPolicy_success() public {
        StrategyExecutor138.ExportPolicy memory policy = StrategyExecutor138.ExportPolicy({
            mode: StrategyExecutor138.ExportMode.Threshold,
            minExportUsd: 10_000e6,
            maxPerTxUsd: 50_000e6,
            maxDailyUsd: 100_000e6,
            rateLimitPerHour: 5,
            cooldownBlocks: 10,
            exportAsset: address(weth9),
            destinationSelector: 5009297550715157269,
            destinationReceiver: receiverMainnet
        });
        vm.prank(admin);
        executor.setExportPolicy(policy);
        (StrategyExecutor138.ExportMode mode, uint256 minExportUsd,,,, uint256 cooldownBlocks,,,) = executor.exportPolicy();
        assertEq(uint8(mode), uint8(StrategyExecutor138.ExportMode.Threshold));
        assertEq(minExportUsd, 10_000e6);
        assertEq(cooldownBlocks, 10);
        assertEq(executor.cooldownBlocks(), 10);
    }

    function test_recordExportIntent_success() public {
        vm.prank(admin);
        adapter.setExportsEnabled(false);
        vm.prank(keeper);
        executor.recordExportIntent(address(weth9), 5e18);
        assertEq(executor.pendingIntentToken(), address(weth9));
        assertEq(executor.pendingIntentAmount(), 5e18);
    }

    function test_recordExportIntent_revert_exportsEnabled() public {
        vm.prank(admin);
        adapter.setExportsEnabled(true);
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.ExportsNotEnabled.selector);
        executor.recordExportIntent(address(weth9), 5e18);
    }

    function test_recordExportIntent_revert_tokenNotApproved() public {
        MockWETH other = new MockWETH();
        vm.prank(admin);
        adapter.setExportsEnabled(false);
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.TokenNotApproved.selector);
        executor.recordExportIntent(address(other), 1e18);
    }

    function test_processPendingIntent_revert_noIntent() public {
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.NoPendingIntent.selector);
        executor.processPendingIntent{value: 0}(block.timestamp + 3600);
    }

    function test_processPendingIntent_revert_exportsDisabled() public {
        vm.prank(admin);
        adapter.setExportsEnabled(false);
        vm.prank(keeper);
        executor.recordExportIntent(address(weth9), 5e18);
        vm.prank(admin);
        adapter.setExportsEnabled(false);
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.ExportsNotEnabled.selector);
        executor.processPendingIntent{value: 0}(block.timestamp + 3600);
    }

    function test_processPendingIntent_success() public {
        vm.prank(admin);
        adapter.setExportsEnabled(false);
        vm.prank(keeper);
        executor.recordExportIntent(address(weth9), 5e18);
        vm.prank(admin);
        adapter.setExportsEnabled(true);
        uint256 vaultBefore = weth9.balanceOf(address(vault));
        vm.prank(keeper);
        executor.processPendingIntent{value: 0}(block.timestamp + 3600);
        assertEq(executor.pendingIntentToken(), address(0));
        assertEq(executor.pendingIntentAmount(), 0);
        assertEq(weth9.balanceOf(address(vault)), vaultBefore - 5e18);
    }

    function test_harvestFees_revert_notImplemented() public {
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.NotImplemented.selector);
        executor.harvestFees();
    }

    function test_rebalanceLp_revert_notImplemented() public {
        vm.prank(keeper);
        vm.expectRevert(StrategyExecutor138.NotImplemented.selector);
        executor.rebalanceLp();
    }
}
