// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.19;

import { SwapExecutor } from "../analysis/helpers/SwapExecutor.sol";
import { Kraiken } from "../src/Kraiken.sol";
import { LiquidityManager } from "../src/LiquidityManager.sol";
import { ThreePositionStrategy } from "../src/abstracts/ThreePositionStrategy.sol";
import { UniswapHelpers } from "../src/helpers/UniswapHelpers.sol";
import { IWETH9 } from "../src/interfaces/IWETH9.sol";
import { TestEnvironment } from "./helpers/TestBase.sol";
import { ConfigurableOptimizer } from "./mocks/ConfigurableOptimizer.sol";
import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "forge-std/Test.sol";
import "forge-std/console2.sol";

/// @title FuzzingAnalyzerBugs
/// @notice Tests verifying that the fuzzing analyzer measurements are correct
contract FuzzingAnalyzerBugs is Test {
    TestEnvironment testEnv;
    IUniswapV3Factory factory;
    IUniswapV3Pool pool;
    IWETH9 weth;
    Kraiken kraiken;
    LiquidityManager lm;
    SwapExecutor swapExecutor;
    ConfigurableOptimizer optimizer;
    bool token0isWeth;

    address trader = makeAddr("trader");
    address fees = makeAddr("fees");

    function setUp() public {
        testEnv = new TestEnvironment(fees);
        factory = UniswapHelpers.deployUniswapFactory();

        // Bear market params: CI=0.8e18, AS=0.1e18, AW=40, DD=0.2e18
        optimizer = new ConfigurableOptimizer(8e17, 1e17, 40, 2e17);

        (factory, pool, weth, kraiken,, lm,, token0isWeth) = testEnv.setupEnvironmentWithExistingFactory(factory, true, address(optimizer));

        swapExecutor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm, true);

        // Fund LM
        vm.deal(address(lm), 200 ether);
        vm.prank(address(lm));
        weth.deposit{ value: 100 ether }();

        // Initial recenter
        vm.prank(fees);
        lm.recenter();
    }

    /// @notice Simple round trip: buy → recenter → sell. Trader should NOT profit.
    function test_simpleRoundTrip_bearMarket() public {
        // Fund trader
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        uint256 traderInitEth = weth.balanceOf(trader);
        console2.log("Trader initial WETH:", traderInitEth / 1e18);

        // Buy 30 ETH of KRAIKEN
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 30 ether);
        vm.stopPrank();
        uint256 bought = swapExecutor.executeBuy(30 ether, trader);
        console2.log("WETH consumed by buy:", bought / 1e18);

        // Recover any stuck WETH
        uint256 stuckWeth = weth.balanceOf(address(swapExecutor));
        if (stuckWeth > 0) {
            vm.prank(address(swapExecutor));
            weth.transfer(trader, stuckWeth);
        }

        uint256 krkBalance = kraiken.balanceOf(trader);
        console2.log("KRAIKEN received:", krkBalance / 1e18);

        // Recenter
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        lm.recenter();

        // Sell all KRAIKEN
        vm.startPrank(trader);
        kraiken.transfer(address(swapExecutor), krkBalance);
        vm.stopPrank();
        uint256 sold = swapExecutor.executeSell(krkBalance, trader);
        console2.log("KRAIKEN consumed by sell:", sold / 1e18);

        // Recover stuck tokens
        uint256 stuckKrk = kraiken.balanceOf(address(swapExecutor));
        if (stuckKrk > 0) {
            vm.prank(address(swapExecutor));
            kraiken.transfer(trader, stuckKrk);
        }
        stuckWeth = weth.balanceOf(address(swapExecutor));
        if (stuckWeth > 0) {
            vm.prank(address(swapExecutor));
            weth.transfer(trader, stuckWeth);
        }

        uint256 traderFinalEth = weth.balanceOf(trader);
        uint256 traderFinalKrk = kraiken.balanceOf(trader);
        int256 pnl = int256(traderFinalEth) - int256(traderInitEth);

        console2.log("Trader final WETH:", traderFinalEth / 1e18);
        console2.log("Trader unsold KRAIKEN:", traderFinalKrk / 1e18);
        console2.log("Trader PnL (wei):", pnl);

        // In a simple round trip, trader should lose (slippage + fees)
        assertLe(pnl, 0, "Trader should not profit from simple round trip");
    }

    /// @notice Demonstrates the PnL leakage bug: leftover KRAIKEN inflates next run's profit
    function test_pnlLeakage_withoutCleanup() public {
        // === RUN 0: Buy a lot, partial liquidation ===
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        // Buy aggressively
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 80 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(80 ether, trader);

        // Recover stuck WETH from partial buy
        uint256 stuckW = weth.balanceOf(address(swapExecutor));
        if (stuckW > 0) {
            vm.prank(address(swapExecutor));
            weth.transfer(trader, stuckW);
        }

        uint256 krkAfterBuy = kraiken.balanceOf(trader);
        console2.log("Run 0 - KRAIKEN after buy:", krkAfterBuy / 1e18);

        // Recenter
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        // Partial liquidation: sell half
        uint256 halfKrk = krkAfterBuy / 2;
        if (halfKrk > 0) {
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), halfKrk);
            vm.stopPrank();
            swapExecutor.executeSell(halfKrk, trader);
        }
        // Recover stuck tokens
        stuckW = weth.balanceOf(address(swapExecutor));
        if (stuckW > 0) {
            vm.prank(address(swapExecutor));
            weth.transfer(trader, stuckW);
        }
        uint256 stuckK = kraiken.balanceOf(address(swapExecutor));
        if (stuckK > 0) {
            vm.prank(address(swapExecutor));
            kraiken.transfer(trader, stuckK);
        }

        uint256 run0FinalWeth = weth.balanceOf(trader);
        uint256 run0LeftoverKrk = kraiken.balanceOf(trader);
        int256 run0Pnl = int256(run0FinalWeth) - 200 ether;
        console2.log("Run 0 - Final WETH:", run0FinalWeth / 1e18);
        console2.log("Run 0 - Leftover KRK:", run0LeftoverKrk / 1e18);
        console2.log("Run 0 - PnL (WETH only):", run0Pnl);

        // === RUN 1 (WITHOUT CLEANUP - the old buggy behavior) ===
        // Don't clean up! Just add more WETH on top.
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();
        uint256 run1InitEth = weth.balanceOf(trader); // Includes old balance!

        console2.log("Run 1 - Init WETH (includes old):", run1InitEth / 1e18);
        console2.log("Run 1 - Carried KRK:", kraiken.balanceOf(trader) / 1e18);

        // Recenter to build new liquidity
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        // Now sell the leftover KRAIKEN from run 0
        uint256 carryKrk = kraiken.balanceOf(trader);
        if (carryKrk > 0) {
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), carryKrk);
            vm.stopPrank();
            swapExecutor.executeSell(carryKrk, trader);

            stuckW = weth.balanceOf(address(swapExecutor));
            if (stuckW > 0) {
                vm.prank(address(swapExecutor));
                weth.transfer(trader, stuckW);
            }
            stuckK = kraiken.balanceOf(address(swapExecutor));
            if (stuckK > 0) {
                vm.prank(address(swapExecutor));
                kraiken.transfer(trader, stuckK);
            }
        }

        uint256 run1FinalEth = weth.balanceOf(trader);
        int256 run1Pnl = int256(run1FinalEth) - int256(run1InitEth);
        console2.log("Run 1 - Final WETH:", run1FinalEth / 1e18);
        console2.log("Run 1 - PnL (without cleanup):", run1Pnl);

        // The key insight: run1Pnl can be POSITIVE because it includes WETH from
        // selling run 0's leftover KRAIKEN, which was never accounted for.
        // This is the false positive that made 251/252 combos appear "unsafe".
        if (run1Pnl > 0) {
            console2.log("BUG CONFIRMED: Run 1 shows false profit from leftover KRAIKEN");
        }

        // True combined PnL across both runs should be negative
        int256 totalPnl = run0Pnl + run1Pnl;
        console2.log("Total PnL across both runs:", totalPnl);
    }

    /// @notice 10 cycles of buy→recenter→sell — does IL accumulation create profit?
    function test_multiCycle_bearMarket() public {
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        uint256 traderInitEth = weth.balanceOf(trader);
        console2.log("=== Multi-cycle test (10 cycles) ===");
        console2.log("Trader initial WETH:", traderInitEth / 1e18);

        for (uint256 i = 0; i < 10; i++) {
            uint256 wethBefore = weth.balanceOf(trader);
            uint256 buyAmount = 20 ether;
            if (wethBefore < buyAmount) {
                console2.log("Cycle", i, "- skipped (insufficient WETH)");
                break;
            }

            // Buy
            vm.startPrank(trader);
            weth.transfer(address(swapExecutor), buyAmount);
            vm.stopPrank();
            swapExecutor.executeBuy(buyAmount, trader);

            // Recover stuck WETH from partial buy
            uint256 sw = weth.balanceOf(address(swapExecutor));
            if (sw > 0) {
                vm.prank(address(swapExecutor));
                weth.transfer(trader, sw);
            }

            uint256 krkBal = kraiken.balanceOf(trader);

            // Recenter
            vm.warp(block.timestamp + 1 hours);
            vm.roll(block.number + 1);
            vm.prank(fees);
            try lm.recenter() { } catch { }

            // Sell all KRAIKEN
            if (krkBal > 0) {
                vm.startPrank(trader);
                kraiken.transfer(address(swapExecutor), krkBal);
                vm.stopPrank();
                try swapExecutor.executeSell(krkBal, trader) { } catch { }
            }

            // Recover stuck tokens
            uint256 sk = kraiken.balanceOf(address(swapExecutor));
            if (sk > 0) {
                vm.prank(address(swapExecutor));
                kraiken.transfer(trader, sk);
            }
            sw = weth.balanceOf(address(swapExecutor));
            if (sw > 0) {
                vm.prank(address(swapExecutor));
                weth.transfer(trader, sw);
            }

            uint256 wethAfter = weth.balanceOf(trader);
            uint256 krkLeft = kraiken.balanceOf(trader);
            int256 cyclePnl = int256(wethAfter) - int256(wethBefore);
            console2.log("Cycle", i);
            console2.log("  PnL (wei):", cyclePnl);
            console2.log("  KRK left:", krkLeft / 1e18);
        }

        // Final cleanup: sell any remaining KRAIKEN
        uint256 finalKrk = kraiken.balanceOf(trader);
        if (finalKrk > 0) {
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), finalKrk);
            vm.stopPrank();
            try swapExecutor.executeSell(finalKrk, trader) { } catch { }
            uint256 sk = kraiken.balanceOf(address(swapExecutor));
            if (sk > 0) {
                vm.prank(address(swapExecutor));
                kraiken.transfer(trader, sk);
            }
            uint256 sw = weth.balanceOf(address(swapExecutor));
            if (sw > 0) {
                vm.prank(address(swapExecutor));
                weth.transfer(trader, sw);
            }
        }

        uint256 traderFinalEth = weth.balanceOf(trader);
        uint256 traderFinalKrk = kraiken.balanceOf(trader);
        int256 totalPnl = int256(traderFinalEth) - int256(traderInitEth);
        console2.log("=== Final ===");
        console2.log("Final WETH:", traderFinalEth / 1e18);
        console2.log("Unsold KRK:", traderFinalKrk / 1e18);
        console2.log("Total PnL:", totalPnl);

        // Multi-cycle should also show loss with bear params
        assertLe(totalPnl, 0, "Trader should not profit even with multiple cycles");
    }

    /// @notice Same as sweep-like test but with CAPPED swaps (production behavior)
    function test_sweepLikeRun_bearMarket_capped() public {
        // Redeploy SwapExecutor with capped=false (uses LiquidityBoundaryHelper)
        swapExecutor = new SwapExecutor(pool, weth, kraiken, token0isWeth, lm, false);

        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        uint256 traderInitEth = weth.balanceOf(trader);
        console2.log("=== Sweep-like run CAPPED (30 trades, 80% buy bias) ===");

        for (uint256 i = 0; i < 30; i++) {
            if (uint256(keccak256(abi.encodePacked(uint256(0), i, "recenter"))) % 3 == 0) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }

            uint256 rand = uint256(keccak256(abi.encodePacked(uint256(0), i))) % 100;
            if (rand < 80) {
                uint256 amount = (20 * 1 ether) + (uint256(keccak256(abi.encodePacked(uint256(0), i, "buy"))) % (60 * 1 ether));
                if (weth.balanceOf(trader) < amount) continue;
                vm.startPrank(trader);
                weth.transfer(address(swapExecutor), amount);
                try swapExecutor.executeBuy(amount, trader) { } catch { }
                vm.stopPrank();
                _recoverStuck();
            } else {
                uint256 krkBal = kraiken.balanceOf(trader);
                if (krkBal == 0) continue;
                uint256 pct = 20 + (uint256(keccak256(abi.encodePacked(uint256(0), i, "sell"))) % 60);
                uint256 amount = krkBal * pct / 100;
                if (amount == 0) continue;
                vm.startPrank(trader);
                kraiken.transfer(address(swapExecutor), amount);
                try swapExecutor.executeSell(amount, trader) { } catch { }
                vm.stopPrank();
                _recoverStuck();
            }
        }

        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        // Liquidate
        uint256 remaining = kraiken.balanceOf(trader);
        uint256 attempts;
        while (remaining > 0 && attempts < 20) {
            uint256 prev = remaining;
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), remaining);
            vm.stopPrank();
            try swapExecutor.executeSell(remaining, trader) { } catch { }
            _recoverStuck();
            if (attempts % 3 == 2) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }
            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
            unchecked {
                attempts++;
            }
        }
        _recoverStuck();

        uint256 traderFinalEth = weth.balanceOf(trader);
        int256 pnl = int256(traderFinalEth) - int256(traderInitEth);
        console2.log("Final WETH:", traderFinalEth / 1e18);
        console2.log("Unsold KRK:", kraiken.balanceOf(trader) / 1e18);
        console2.log("PnL (wei):", pnl);

        // With capped swaps + bear params, trader should NOT profit
        assertLe(pnl, 0, "Trader should not profit with capped swaps + bear params");
    }

    /// @notice Conservation check: total system WETH is consistent
    function test_systemConservation() public {
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        // Track WETH total supply (all holders)
        uint256 wethSupplyBefore = weth.balanceOf(trader) + weth.balanceOf(address(lm)) + weth.balanceOf(address(pool)) + weth.balanceOf(address(swapExecutor))
            + weth.balanceOf(address(0xdead));
        uint256 lmEthBefore = address(lm).balance;
        console2.log("WETH supply before:", wethSupplyBefore / 1e18);
        console2.log("LM native ETH:", lmEthBefore / 1e18);

        // Do 10 trades with buys and recenters
        for (uint256 i = 0; i < 10; i++) {
            if (i % 3 == 0) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }

            uint256 amount = 20 ether;
            if (weth.balanceOf(trader) >= amount) {
                vm.startPrank(trader);
                weth.transfer(address(swapExecutor), amount);
                try swapExecutor.executeBuy(amount, trader) { } catch { }
                vm.stopPrank();
            }
        }

        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        uint256 wethSupplyAfter = weth.balanceOf(trader) + weth.balanceOf(address(lm)) + weth.balanceOf(address(pool)) + weth.balanceOf(address(swapExecutor))
            + weth.balanceOf(address(0xdead)) + weth.balanceOf(fees);
        uint256 lmEthAfter = address(lm).balance;
        console2.log("WETH supply after:", wethSupplyAfter / 1e18);
        console2.log("LM native ETH after:", lmEthAfter / 1e18);

        // Total WETH should equal initial + any ETH that was converted
        uint256 totalEthConverted = lmEthBefore - lmEthAfter;
        console2.log("ETH converted to WETH:", totalEthConverted / 1e18);
        console2.log("Expected WETH supply:", (wethSupplyBefore + totalEthConverted) / 1e18);

        assertEq(wethSupplyAfter, wethSupplyBefore + totalEthConverted, "WETH total supply should be conserved (accounting for ETH->WETH conversion)");
    }

    /// @notice Mimics sweep run: 30 trades with 80% buy bias, bear params
    function test_sweepLikeRun_bearMarket() public {
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        uint256 traderInitEth = weth.balanceOf(trader);
        console2.log("=== Sweep-like run (30 trades, 80% buy bias) ===");

        uint256 recenters;
        uint256 buysFailed;
        uint256 sellsFailed;

        for (uint256 i = 0; i < 30; i++) {
            // 1/3 chance of recenter
            if (uint256(keccak256(abi.encodePacked(uint256(0), i, "recenter"))) % 3 == 0) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() {
                    recenters++;
                } catch { }
            }

            uint256 rand = uint256(keccak256(abi.encodePacked(uint256(0), i))) % 100;
            if (rand < 80) {
                // Buy
                uint256 amount = (20 * 1 ether) + (uint256(keccak256(abi.encodePacked(uint256(0), i, "buy"))) % (60 * 1 ether));
                if (weth.balanceOf(trader) < amount) {
                    buysFailed++;
                    continue;
                }

                vm.startPrank(trader);
                weth.transfer(address(swapExecutor), amount);
                try swapExecutor.executeBuy(amount, trader) returns (uint256 a) {
                    vm.stopPrank();
                    if (a == 0) buysFailed++;
                } catch {
                    vm.stopPrank();
                    buysFailed++;
                }
            } else {
                // Sell
                uint256 krkBal = kraiken.balanceOf(trader);
                if (krkBal == 0) {
                    sellsFailed++;
                    continue;
                }
                uint256 pct = 20 + (uint256(keccak256(abi.encodePacked(uint256(0), i, "sell"))) % 60);
                uint256 amount = krkBal * pct / 100;
                if (amount == 0) {
                    sellsFailed++;
                    continue;
                }

                vm.startPrank(trader);
                kraiken.transfer(address(swapExecutor), amount);
                try swapExecutor.executeSell(amount, trader) returns (uint256 a) {
                    vm.stopPrank();
                    if (a == 0) sellsFailed++;
                } catch {
                    vm.stopPrank();
                    sellsFailed++;
                }
            }
        }

        // Final recenter
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() {
            recenters++;
        } catch { }

        console2.log("Trades done. Recenters:", recenters);
        console2.log("Buys failed:", buysFailed);
        console2.log("Sells failed:", sellsFailed);

        // Liquidate holdings (same as sweep)
        uint256 remaining = kraiken.balanceOf(trader);
        uint256 attempts;
        while (remaining > 0 && attempts < 20) {
            uint256 prev = remaining;
            uint256 wBefore = weth.balanceOf(trader);

            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), remaining);
            vm.stopPrank();

            try swapExecutor.executeSell(remaining, trader) {
                if (weth.balanceOf(trader) <= wBefore) {
                    _recoverStuck();
                    break;
                }
            } catch {
                _recoverStuck();
                break;
            }

            _recoverStuck();

            if (attempts % 3 == 2) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }

            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
            unchecked {
                attempts++;
            }
        }

        // Also recover any stuck WETH from buys
        _recoverStuck();

        uint256 traderFinalEth = weth.balanceOf(trader);
        uint256 traderFinalKrk = kraiken.balanceOf(trader);
        int256 pnl = int256(traderFinalEth) - int256(traderInitEth);

        console2.log("=== Results ===");
        console2.log("Final WETH:", traderFinalEth / 1e18);
        console2.log("Unsold KRK:", traderFinalKrk / 1e18);
        console2.log("PnL (wei):", pnl);
        console2.log("SwapExec WETH:", weth.balanceOf(address(swapExecutor)) / 1e18);
        console2.log("SwapExec KRK:", kraiken.balanceOf(address(swapExecutor)) / 1e18);

        // Without ratchet, trader may profit from IL extraction via buy->recenter->sell cycles
        // This test documents the baseline behavior
        console2.log("Baseline sweep PnL (no ratchet):", pnl);
    }

    function _recoverStuck() internal {
        uint256 sk = kraiken.balanceOf(address(swapExecutor));
        if (sk > 0) {
            vm.prank(address(swapExecutor));
            kraiken.transfer(trader, sk);
        }
        uint256 sw = weth.balanceOf(address(swapExecutor));
        if (sw > 0) {
            vm.prank(address(swapExecutor));
            weth.transfer(trader, sw);
        }
    }

    /// @notice Total system WETH = all holders + LM native ETH (before wrapping)
    function _systemWeth() internal view returns (uint256) {
        return weth.balanceOf(trader) + weth.balanceOf(address(lm)) + weth.balanceOf(address(pool)) + weth.balanceOf(address(swapExecutor))
            + weth.balanceOf(address(0xdead)) + weth.balanceOf(fees) + address(lm).balance;
    }

    /// @notice Trade-by-trade diagnostic: replicate sweep run 0 with verbose logging
    /// Uses exact same seeds and pattern as ParameterSweepFuzzing._executeRun(0)
    /// Bear params: CI=0.8e18, AS=0.1e18, AW=40, DD=0.2e18, buyBias=80
    function test_tradeByTrade_diagnostic() public {
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        uint256 traderInitEth = weth.balanceOf(trader);
        uint256 systemStart = _systemWeth();

        console2.log("=== TRADE-BY-TRADE DIAGNOSTIC ===");
        console2.log("traderInitWETH:", traderInitEth);
        console2.log("systemWETH:", systemStart);
        console2.log("LM native ETH:", address(lm).balance);
        console2.log("LM WETH:", weth.balanceOf(address(lm)));
        console2.log("pool WETH:", weth.balanceOf(address(pool)));

        (, int24 startTick,,,,,) = pool.slot0();
        console2.log("startTick:", startTick);

        bool crossedAbove200 = false;
        uint256 runIndex = 0;

        for (uint256 i = 0; i < 30; i++) {
            // 1/3 chance of recenter (same hash as sweep)
            if (uint256(keccak256(abi.encodePacked(runIndex, i, "recenter"))) % 3 == 0) {
                uint256 sysBeforeRec = _systemWeth();
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() {
                    uint256 sysAfterRec = _systemWeth();
                    (, int24 tick,,,,,) = pool.slot0();
                    console2.log("---");
                    console2.log("RECENTER ok  tick:", tick);
                    console2.log("  tW:", weth.balanceOf(trader));
                    console2.log("  tK:", kraiken.balanceOf(trader));
                    if (sysAfterRec != sysBeforeRec) {
                        int256 leak = int256(sysAfterRec) - int256(sysBeforeRec);
                        console2.log("  !! SYS WETH CHANGED by:", leak);
                    }
                } catch {
                    console2.log("--- RECENTER failed at trade", i);
                }
            }

            uint256 rand = uint256(keccak256(abi.encodePacked(runIndex, i))) % 100;
            if (rand < 80) {
                // Buy (same amount calc as sweep)
                uint256 amount = (20 * 1 ether) + (uint256(keccak256(abi.encodePacked(runIndex, i, "buy"))) % (60 * 1 ether));
                uint256 traderWethBefore = weth.balanceOf(trader);
                uint256 traderKrkBefore = kraiken.balanceOf(trader);

                if (traderWethBefore < amount) {
                    console2.log("T", i);
                    console2.log("  BUY_SKIP need:", amount / 1e18);
                    console2.log("  have:", traderWethBefore / 1e18);
                    continue;
                }

                uint256 sysBefore = _systemWeth();

                vm.startPrank(trader);
                weth.transfer(address(swapExecutor), amount);
                try swapExecutor.executeBuy(amount, trader) returns (uint256 consumed) {
                    vm.stopPrank();
                    _recoverStuck();

                    uint256 traderWethAfter = weth.balanceOf(trader);
                    uint256 traderKrkAfter = kraiken.balanceOf(trader);
                    uint256 sysAfter = _systemWeth();
                    (, int24 tick,,,,,) = pool.slot0();

                    console2.log("T", i);
                    console2.log("  BUY amt:", amount / 1e18);
                    console2.log("  consumed:", consumed / 1e18);
                    console2.log("  tW:", traderWethAfter);
                    console2.log("  tK:", traderKrkAfter);
                    console2.log("  tick:", tick);

                    int256 wethDelta = int256(traderWethAfter) - int256(traderWethBefore);
                    int256 krkDelta = int256(traderKrkAfter) - int256(traderKrkBefore);
                    console2.log("  dW:", wethDelta);
                    console2.log("  dK:", krkDelta);

                    if (sysAfter != sysBefore) {
                        int256 leak = int256(sysAfter) - int256(sysBefore);
                        console2.log("  !! SYS LEAK:", leak);
                    }

                    if (!crossedAbove200 && traderWethAfter > 200 ether) {
                        crossedAbove200 = true;
                        console2.log("  *** CROSSED 200 WETH ***");
                    }
                } catch {
                    vm.stopPrank();
                    _recoverStuck();
                    console2.log("T", i);
                    console2.log("  BUY_FAIL amt:", amount / 1e18);
                }
            } else {
                // Sell (same calc as sweep)
                uint256 krkBal = kraiken.balanceOf(trader);
                if (krkBal == 0) {
                    console2.log("T", i);
                    console2.log("  SELL_SKIP (0 KRK)");
                    continue;
                }
                uint256 pct = 20 + (uint256(keccak256(abi.encodePacked(runIndex, i, "sell"))) % 60);
                uint256 amount = krkBal * pct / 100;
                if (amount == 0) {
                    console2.log("T", i);
                    console2.log("  SELL_SKIP (0 amt)");
                    continue;
                }

                uint256 traderWethBefore = weth.balanceOf(trader);
                uint256 traderKrkBefore = kraiken.balanceOf(trader);
                uint256 sysBefore = _systemWeth();

                vm.startPrank(trader);
                kraiken.transfer(address(swapExecutor), amount);
                try swapExecutor.executeSell(amount, trader) returns (uint256 consumed) {
                    vm.stopPrank();
                    _recoverStuck();

                    uint256 traderWethAfter = weth.balanceOf(trader);
                    uint256 traderKrkAfter = kraiken.balanceOf(trader);
                    uint256 sysAfter = _systemWeth();
                    (, int24 tick,,,,,) = pool.slot0();

                    console2.log("T", i);
                    console2.log("  SELL pct:", pct);
                    console2.log("  amt:", amount / 1e18);
                    console2.log("  consumed:", consumed / 1e18);
                    console2.log("  tW:", traderWethAfter);
                    console2.log("  tK:", traderKrkAfter);
                    console2.log("  tick:", tick);

                    int256 wethDelta = int256(traderWethAfter) - int256(traderWethBefore);
                    console2.log("  dW:", wethDelta);

                    if (sysAfter != sysBefore) {
                        int256 leak = int256(sysAfter) - int256(sysBefore);
                        console2.log("  !! SYS LEAK:", leak);
                    }

                    if (!crossedAbove200 && traderWethAfter > 200 ether) {
                        crossedAbove200 = true;
                        console2.log("  *** CROSSED 200 WETH ***");
                    }
                } catch {
                    vm.stopPrank();
                    _recoverStuck();
                    console2.log("T", i);
                    console2.log("  SELL_FAIL amt:", amount / 1e18);
                }
            }
        }

        console2.log("=== PRE-LIQUIDATION ===");
        console2.log("tW:", weth.balanceOf(trader));
        console2.log("tK:", kraiken.balanceOf(trader));
        console2.log("seW:", weth.balanceOf(address(swapExecutor)));
        console2.log("seK:", kraiken.balanceOf(address(swapExecutor)));

        // Final recenter
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        // Liquidate (same as sweep)
        uint256 remaining = kraiken.balanceOf(trader);
        uint256 attempts;
        while (remaining > 0 && attempts < 20) {
            uint256 prev = remaining;
            uint256 wBefore = weth.balanceOf(trader);

            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), remaining);
            vm.stopPrank();

            try swapExecutor.executeSell(remaining, trader) {
                if (weth.balanceOf(trader) <= wBefore) {
                    _recoverStuck();
                    break;
                }
            } catch {
                _recoverStuck();
                break;
            }

            _recoverStuck();

            if (attempts % 3 == 2) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }

            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
            unchecked {
                attempts++;
            }
        }
        _recoverStuck();

        uint256 traderFinalEth = weth.balanceOf(trader);
        uint256 traderFinalKrk = kraiken.balanceOf(trader);
        int256 pnl = int256(traderFinalEth) - int256(traderInitEth);
        uint256 systemEnd = _systemWeth();

        console2.log("=== FINAL ===");
        console2.log("traderWETH:", traderFinalEth);
        console2.log("traderKRK:", traderFinalKrk);
        console2.log("PnL:", pnl);
        console2.log("systemStart:", systemStart);
        console2.log("systemEnd:", systemEnd);
        int256 sysChange = int256(systemEnd) - int256(systemStart);
        console2.log("sysChange:", sysChange);

        // System WETH should be conserved (no ETH created from nothing)
        // If sysChange != 0, ETH is leaking in or out
        if (sysChange != 0) {
            console2.log("!! SYSTEM WETH NOT CONSERVED !!");
        }
    }

    /// @notice Compare: single big buy vs multi-buy with recenters
    /// If single big buy also profits, the issue is trade size.
    /// If only multi-buy profits, recenters are creating the opportunity.
    function test_singleBigBuy_vs_multiBuy() public {
        // === SCENARIO A: Single big buy (185 ETH) → recenter → sell all ===
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        uint256 initA = weth.balanceOf(trader);

        // Buy 185 ETH in one shot
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 185 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(185 ether, trader);
        _recoverStuck();

        uint256 krkA = kraiken.balanceOf(trader);
        (, int24 tickAfterBuy,,,,,) = pool.slot0();
        console2.log("=== SCENARIO A: Single 185 ETH buy ===");
        console2.log("KRK received:", krkA);
        console2.log("tick after buy:", tickAfterBuy);
        console2.log("tW after buy:", weth.balanceOf(trader));

        // Recenter
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        (, int24 tickAfterRec,,,,,) = pool.slot0();
        console2.log("tick after recenter:", tickAfterRec);

        // Sell all KRK
        uint256 remaining = kraiken.balanceOf(trader);
        uint256 attempts;
        while (remaining > 0 && attempts < 20) {
            uint256 prev = remaining;
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), remaining);
            vm.stopPrank();
            try swapExecutor.executeSell(remaining, trader) { } catch { }
            _recoverStuck();
            if (attempts % 3 == 2) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }
            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
            unchecked {
                attempts++;
            }
        }
        _recoverStuck();

        int256 pnlA = int256(weth.balanceOf(trader)) - int256(initA);
        console2.log("PnL A (single buy):", pnlA);
        console2.log("Unsold KRK A:", kraiken.balanceOf(trader));
    }

    /// @notice Isolate: 2 buys with recenter between them
    function test_twoBuys_withRecenter() public {
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();
        uint256 init = weth.balanceOf(trader);

        // Buy #1: 78 ETH
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 78 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(78 ether, trader);
        _recoverStuck();

        (, int24 t1,,,,,) = pool.slot0();
        console2.log("After buy1: tick", t1);
        console2.log("  tW:", weth.balanceOf(trader));
        console2.log("  tK:", kraiken.balanceOf(trader));

        // Recenter
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { }
        catch {
            console2.log("  recenter1 FAILED");
        }

        // Buy #2: 47 ETH
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 47 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(47 ether, trader);
        _recoverStuck();

        (, int24 t2,,,,,) = pool.slot0();
        console2.log("After buy2: tick", t2);
        console2.log("  tW:", weth.balanceOf(trader));
        console2.log("  tK:", kraiken.balanceOf(trader));

        // Recenter again
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { }
        catch {
            console2.log("  recenter2 FAILED");
        }

        // Sell ALL
        uint256 remaining = kraiken.balanceOf(trader);
        uint256 attempts;
        while (remaining > 0 && attempts < 20) {
            uint256 prev = remaining;
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), remaining);
            vm.stopPrank();
            try swapExecutor.executeSell(remaining, trader) { } catch { }
            _recoverStuck();
            if (attempts % 3 == 2) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }
            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
            unchecked {
                attempts++;
            }
        }
        _recoverStuck();

        int256 pnl = int256(weth.balanceOf(trader)) - int256(init);
        console2.log("=== 2-buy result ===");
        console2.log("PnL:", pnl);
        console2.log("Unsold KRK:", kraiken.balanceOf(trader));
    }

    /// @notice 4 buys matching sweep T0-T5 exactly, with recenters
    function test_fourBuys_withRecenters() public {
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();
        uint256 init = weth.balanceOf(trader);
        uint256 sysStart = _systemWeth();

        // Replicate the sweep's first 4 buys exactly
        uint256[4] memory buyAmounts = [uint256(78 ether), 47 ether, 40 ether, 20 ether];
        // Recenters happen after T0 and T1 (from the diagnostic output)
        bool[4] memory recenterAfter = [true, true, false, false];

        for (uint256 i = 0; i < 4; i++) {
            vm.startPrank(trader);
            weth.transfer(address(swapExecutor), buyAmounts[i]);
            vm.stopPrank();
            swapExecutor.executeBuy(buyAmounts[i], trader);
            _recoverStuck();

            (, int24 tick,,,,,) = pool.slot0();
            console2.log("Buy", i);
            console2.log("  amt:", buyAmounts[i] / 1e18);
            console2.log("  tick:", tick);
            console2.log("  tW:", weth.balanceOf(trader));
            console2.log("  tK:", kraiken.balanceOf(trader));

            if (recenterAfter[i]) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() {
                    console2.log("  recenter OK");
                } catch {
                    console2.log("  recenter FAILED");
                }
            }
        }

        // Final recenter before selling
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        // Sell ALL
        uint256 remaining = kraiken.balanceOf(trader);
        uint256 attempts;
        while (remaining > 0 && attempts < 20) {
            uint256 prev = remaining;
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), remaining);
            vm.stopPrank();
            try swapExecutor.executeSell(remaining, trader) { } catch { }
            _recoverStuck();
            if (attempts % 3 == 2) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }
            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
            unchecked {
                attempts++;
            }
        }
        _recoverStuck();

        int256 pnl = int256(weth.balanceOf(trader)) - int256(init);
        uint256 sysEnd = _systemWeth();

        console2.log("=== 4-buy result ===");
        console2.log("PnL:", pnl);
        console2.log("Unsold KRK:", kraiken.balanceOf(trader));
        console2.log("sysStart:", sysStart);
        console2.log("sysEnd:", sysEnd);
    }

    /// @notice Deep diagnostic: track position layout, VWAP tick, and ETH distribution
    /// around each recenter to understand WHY recenters between buys create profit.
    function test_recenterMechanism_diagnostic() public {
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        console2.log("=== INITIAL STATE (after setUp recenter) ===");
        _logPositions("INIT");

        // --- Buy #1: 78 ETH ---
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 78 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(78 ether, trader);
        _recoverStuck();

        console2.log("=== AFTER BUY1 (78 ETH) ===");
        _logPositions("BUY1");
        console2.log("  tW:", weth.balanceOf(trader));
        console2.log("  tK:", kraiken.balanceOf(trader) / 1e18);

        // --- Recenter #1 ---
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.recordLogs();
        vm.prank(fees);
        lm.recenter();
        _logRecenterEvents();

        console2.log("=== AFTER RECENTER1 ===");
        _logPositions("REC1");

        // --- Buy #2: 47 ETH ---
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 47 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(47 ether, trader);
        _recoverStuck();

        console2.log("=== AFTER BUY2 (47 ETH) ===");
        _logPositions("BUY2");
        console2.log("  tW:", weth.balanceOf(trader));
        console2.log("  tK:", kraiken.balanceOf(trader) / 1e18);

        // --- Recenter #2 ---
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.recordLogs();
        vm.prank(fees);
        lm.recenter();
        _logRecenterEvents();

        console2.log("=== AFTER RECENTER2 ===");
        _logPositions("REC2");

        // --- Buy #3: 40 ETH ---
        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 40 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(40 ether, trader);
        _recoverStuck();

        console2.log("=== AFTER BUY3 (40 ETH) ===");
        _logPositions("BUY3");
        console2.log("  tW:", weth.balanceOf(trader));
        console2.log("  tK:", kraiken.balanceOf(trader) / 1e18);

        // --- Recenter #3 ---
        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.recordLogs();
        vm.prank(fees);
        lm.recenter();
        _logRecenterEvents();

        console2.log("=== AFTER RECENTER3 ===");
        _logPositions("REC3");

        // --- Now sell everything ---
        console2.log("=== SELLING ALL KRK ===");
        uint256 totalKrk = kraiken.balanceOf(trader);
        console2.log("  KRK to sell:", totalKrk / 1e18);
        console2.log("  WETH before sell:", weth.balanceOf(trader));

        uint256 remaining = totalKrk;
        uint256 attempts;
        while (remaining > 0 && attempts < 20) {
            uint256 prev = remaining;
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), remaining);
            vm.stopPrank();
            try swapExecutor.executeSell(remaining, trader) { } catch { }
            _recoverStuck();
            if (attempts % 3 == 2) {
                vm.warp(block.timestamp + 1 hours);
                vm.roll(block.number + 1);
                vm.prank(fees);
                try lm.recenter() { } catch { }
            }
            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
            unchecked {
                attempts++;
            }
        }
        _recoverStuck();

        console2.log("=== AFTER SELL ALL ===");
        _logPositions("SOLD");
        int256 pnl = int256(weth.balanceOf(trader)) - 200 ether;
        console2.log("  PnL:", pnl);
        console2.log("  tW:", weth.balanceOf(trader));
        console2.log("  unsold KRK:", kraiken.balanceOf(trader));
        console2.log("  sysWeth:", _systemWeth());
    }

    function _logPositions(string memory label) internal view {
        (, int24 currentTick,,,,,) = pool.slot0();
        console2.log("  currentTick:", currentTick);

        // Floor position
        (uint128 floorLiq, int24 floorLower, int24 floorUpper) = lm.positions(ThreePositionStrategy.Stage.FLOOR);
        console2.log("  FLOOR: [", floorLower);
        console2.log("         ,", floorUpper);
        console2.log("         ] liq:", uint256(floorLiq));

        // Anchor position
        (uint128 anchorLiq, int24 anchorLower, int24 anchorUpper) = lm.positions(ThreePositionStrategy.Stage.ANCHOR);
        console2.log("  ANCHOR: [", anchorLower);
        console2.log("          ,", anchorUpper);
        console2.log("          ] liq:", uint256(anchorLiq));

        // Discovery position
        (uint128 discLiq, int24 discLower, int24 discUpper) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY);
        console2.log("  DISC: [", discLower);
        console2.log("        ,", discUpper);
        console2.log("        ] liq:", uint256(discLiq));

        // ETH distribution
        uint256 lmEth = address(lm).balance + weth.balanceOf(address(lm));
        uint256 poolWeth = weth.balanceOf(address(pool));
        console2.log("  lmEth:", lmEth);
        console2.log("  poolWeth:", poolWeth);

        // VWAP
        uint256 vwapX96 = lm.getVWAP();
        uint256 adjVwapX96 = lm.getAdjustedVWAP(8e17); // CI=0.8e18
        console2.log("  vwapX96:", vwapX96);
        console2.log("  adjVwapX96:", adjVwapX96);
        console2.log("  cumVolume:", lm.cumulativeVolume());
    }

    function _logRecenterEvents() internal {
        Vm.Log[] memory logs = vm.getRecordedLogs();
        bytes32 scarcitySig = keccak256("EthScarcity(int24,uint256,uint256,uint256,int24)");
        bytes32 abundanceSig = keccak256("EthAbundance(int24,uint256,uint256,uint256,int24)");

        for (uint256 i = 0; i < logs.length; i++) {
            if (logs[i].topics.length > 0) {
                if (logs[i].topics[0] == scarcitySig) {
                    (int24 tick, uint256 ethBal, uint256 supply, uint256 vwap, int24 vwapTick) =
                        abi.decode(logs[i].data, (int24, uint256, uint256, uint256, int24));
                    console2.log("  EVENT EthScarcity:");
                    console2.log("    tick:", tick);
                    console2.log("    ethBal:", ethBal);
                    console2.log("    supply:", supply);
                    console2.log("    vwapTick:", vwapTick);
                } else if (logs[i].topics[0] == abundanceSig) {
                    (int24 tick, uint256 ethBal, uint256 supply, uint256 vwap, int24 vwapTick) =
                        abi.decode(logs[i].data, (int24, uint256, uint256, uint256, int24));
                    console2.log("  EVENT EthAbundance:");
                    console2.log("    tick:", tick);
                    console2.log("    ethBal:", ethBal);
                    console2.log("    supply:", supply);
                    console2.log("    vwapTick:", vwapTick);
                }
            }
        }
    }

    /// @notice Verifies that with proper cleanup, no false profits appear
    function test_noFalseProfit_withCleanup() public {
        // === RUN 0 ===
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();

        vm.startPrank(trader);
        weth.transfer(address(swapExecutor), 80 ether);
        vm.stopPrank();
        swapExecutor.executeBuy(80 ether, trader);

        uint256 stuckW = weth.balanceOf(address(swapExecutor));
        if (stuckW > 0) {
            vm.prank(address(swapExecutor));
            weth.transfer(trader, stuckW);
        }

        vm.warp(block.timestamp + 1 hours);
        vm.roll(block.number + 1);
        vm.prank(fees);
        try lm.recenter() { } catch { }

        // Partial liquidation
        uint256 halfKrk = kraiken.balanceOf(trader) / 2;
        if (halfKrk > 0) {
            vm.startPrank(trader);
            kraiken.transfer(address(swapExecutor), halfKrk);
            vm.stopPrank();
            swapExecutor.executeSell(halfKrk, trader);
        }
        stuckW = weth.balanceOf(address(swapExecutor));
        if (stuckW > 0) {
            vm.prank(address(swapExecutor));
            weth.transfer(trader, stuckW);
        }
        uint256 stuckK = kraiken.balanceOf(address(swapExecutor));
        if (stuckK > 0) {
            vm.prank(address(swapExecutor));
            kraiken.transfer(trader, stuckK);
        }

        // === CLEANUP (the fix) ===
        uint256 leftoverWeth = weth.balanceOf(trader);
        if (leftoverWeth > 0) {
            vm.prank(trader);
            weth.transfer(address(0xdead), leftoverWeth);
        }
        uint256 leftoverKraiken = kraiken.balanceOf(trader);
        if (leftoverKraiken > 0) {
            vm.prank(trader);
            kraiken.transfer(address(0xdead), leftoverKraiken);
        }

        // === RUN 1 (WITH CLEANUP) ===
        vm.deal(trader, 200 ether);
        vm.prank(trader);
        weth.deposit{ value: 200 ether }();
        uint256 run1InitEth = weth.balanceOf(trader);
        uint256 run1InitKrk = kraiken.balanceOf(trader);

        assertEq(run1InitEth, 200 ether, "Should start with exactly 200 WETH");
        assertEq(run1InitKrk, 0, "Should start with 0 KRAIKEN");

        console2.log("Run 1 (clean) - Init WETH:", run1InitEth / 1e18);
        console2.log("Run 1 (clean) - Init KRK:", run1InitKrk);

        // No trades in run 1 — just check starting state is clean
        int256 pnl = int256(weth.balanceOf(trader)) - int256(run1InitEth);
        assertEq(pnl, 0, "PnL should be 0 with no trades and clean state");
    }
}
