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

import { ThreePositionStrategy } from "../src/abstracts/ThreePositionStrategy.sol";
import { ConfigurableOptimizer } from "../test/mocks/ConfigurableOptimizer.sol";
import { FuzzingBase } from "./helpers/FuzzingBase.sol";
import "forge-std/console2.sol";

/// @title BullBearSweep
/// @notice Measures LM resilience during bull->bear sentiment transitions.
/// @dev For each parameter combo: runs a deterministic bull phase (buys + recenters),
///      then a bear phase (sell all + recenters). Tracks enhanced metrics:
///      - Trader PnL (proxy for LM ETH loss)
///      - Bull retention (how much of trader ETH the LM absorbs during buys)
///      - Bear drawdown (worst LM ETH drop during sell-off)
///      - Sell attempts needed to liquidate (liquidity depth indicator)
contract BullBearSweep is FuzzingBase {
    ConfigurableOptimizer optimizer;

    string summaryFile;

    // Scenario config
    uint256 cfgBullBuys;
    uint256 cfgBuySize;
    uint256 cfgLmFunding;

    struct ComboResult {
        uint256 lmEthStart;
        uint256 lmEthAfterBull;
        uint256 lmEthEnd;
        uint256 lmEthTrough; // lowest LM ETH during bear sell-off
        uint256 actualSpent;
        int256 traderPnl;
        uint256 retentionPct;
        uint256 drawdownBps; // (peak - trough) / peak * 10000
        uint256 sellAttempts; // how many sell rounds needed to liquidate
        int256 lmNetGain; // lmEthEnd - lmEthStart
        bool floorMoved;
        uint256 feeWethBull; // WETH fees accumulated during bull phase
        uint256 feeKrkBull; // KRK fees accumulated during bull phase
        uint256 feeWethTotal; // WETH fees accumulated during full cycle
        uint256 feeKrkTotal; // KRK fees accumulated during full cycle
    }

    function run() public {
        cfgBullBuys = vm.envOr("BULL_BUYS", uint256(10));
        cfgBuySize = vm.envOr("BUY_SIZE_ETH", uint256(15));
        cfgLmFunding = vm.envOr("LM_FUNDING_ETH", uint256(200));
        string memory tag = vm.envOr("SWEEP_TAG", string("BULLBEAR"));

        uint256[] memory ciValues =
            _parseUintArray(vm.envOr("CI_VALUES", string("0,300000000000000000,500000000000000000,800000000000000000,1000000000000000000")));
        uint256[] memory asValues =
            _parseUintArray(vm.envOr("AS_VALUES", string("100000000000000000,300000000000000000,500000000000000000,700000000000000000,1000000000000000000")));
        uint256[] memory awValues = _parseUintArray(vm.envOr("AW_VALUES", string("20,50,80")));
        uint256[] memory ddValues = _parseUintArray(vm.envOr("DD_VALUES", string("200000000000000000,500000000000000000,1000000000000000000")));

        uint256 totalCombos = ciValues.length * asValues.length * awValues.length * ddValues.length;
        console2.log("=== Bull-Bear Sweep ===");
        console2.log("Combinations:", totalCombos);
        console2.log("Bull buys:", cfgBullBuys);
        console2.log("Buy size (ETH):", cfgBuySize);
        console2.log("LM funding (ETH):", cfgLmFunding);

        _initInfrastructure();

        summaryFile = string(abi.encodePacked("analysis/sweep-", tag, "-summary.csv"));
        vm.writeFile(
            summaryFile,
            "ci,anchor_share,anchor_width,discovery_depth,trader_pnl,bull_spent,lm_eth_start,lm_eth_after_bull,lm_eth_end,lm_retention_pct,floor_moved,lm_eth_trough,drawdown_bps,sell_attempts,lm_net_gain,fee_weth_bull,fee_krk_bull,fee_weth_total,fee_krk_total\n"
        );

        uint256 comboIndex;
        for (uint256 ci_i = 0; ci_i < ciValues.length; ci_i++) {
            for (uint256 as_i = 0; as_i < asValues.length; as_i++) {
                for (uint256 aw_i = 0; aw_i < awValues.length; aw_i++) {
                    for (uint256 dd_i = 0; dd_i < ddValues.length; dd_i++) {
                        comboIndex++;
                        _runCombo(ciValues[ci_i], asValues[as_i], uint24(awValues[aw_i] > 100 ? 100 : awValues[aw_i]), ddValues[dd_i], comboIndex);
                    }
                }
            }
        }

        console2.log("=== Sweep Complete ===");
        console2.log("Combos:", comboIndex);
        console2.log("Results:", summaryFile);
    }

    function _runCombo(uint256 ci, uint256 as_, uint24 aw, uint256 dd, uint256 comboIdx) internal {
        optimizer = new ConfigurableOptimizer(ci, as_, aw, dd);
        _setupEnvironment(address(optimizer), false, true, cfgLmFunding * 1 ether);

        ComboResult memory r;
        r.lmEthStart = _getLmSystemEth();

        // Record initial floor position
        (, int24 floorTickInit,) = lm.positions(ThreePositionStrategy.Stage.FLOOR);

        // Fund trader
        uint256 totalBullEth = cfgBullBuys * cfgBuySize * 1 ether;
        vm.deal(trader, totalBullEth);
        vm.prank(trader);
        weth.deposit{ value: totalBullEth }();
        uint256 traderInit = weth.balanceOf(trader);

        // === BULL PHASE: deterministic buys with periodic recenters ===
        for (uint256 i = 0; i < cfgBullBuys; i++) {
            uint256 buyAmt = cfgBuySize * 1 ether;
            if (weth.balanceOf(trader) < buyAmt) break;

            vm.startPrank(trader);
            weth.transfer(address(swapExecutor), buyAmt);
            vm.stopPrank();

            try swapExecutor.executeBuy(buyAmt, trader) {
                r.actualSpent += buyAmt;
            } catch { }
            _recoverStuckTokens();

            if ((i + 1) % LIQUIDATION_RECENTER_INTERVAL == 0) {
                _tryRecenter();
            }
        }

        _tryRecenter();
        r.lmEthAfterBull = _getLmSystemEth();
        r.feeWethBull = weth.balanceOf(fees);
        r.feeKrkBull = kraiken.balanceOf(fees);

        // === BEAR PHASE: sell everything with drawdown tracking ===
        r.lmEthTrough = r.lmEthAfterBull;
        _tryRecenter();

        // Instrumented liquidation: sell in chunks, track trough at each step
        uint256 remaining = kraiken.balanceOf(trader);
        uint256 attempts;

        while (remaining > 0 && attempts < LIQUIDATION_MAX_ATTEMPTS) {
            uint256 prev = remaining;
            uint256 wethBefore = weth.balanceOf(trader);

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

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

            _recoverStuckTokens();
            attempts++;

            // Measure LM ETH after this sell — track trough
            uint256 lmNow = _getLmSystemEth();
            if (lmNow < r.lmEthTrough) {
                r.lmEthTrough = lmNow;
            }

            if (attempts % LIQUIDATION_RECENTER_INTERVAL == 0) {
                _tryRecenter();
                // Re-measure after recenter
                lmNow = _getLmSystemEth();
                if (lmNow < r.lmEthTrough) {
                    r.lmEthTrough = lmNow;
                }
            }

            remaining = kraiken.balanceOf(trader);
            if (remaining >= prev) break;
        }

        _recoverStuckTokens();
        r.sellAttempts = attempts;

        r.lmEthEnd = _getLmSystemEth();
        r.feeWethTotal = weth.balanceOf(fees);
        r.feeKrkTotal = kraiken.balanceOf(fees);
        r.traderPnl = int256(weth.balanceOf(trader)) - int256(traderInit);
        r.lmNetGain = int256(r.lmEthEnd) - int256(r.lmEthStart);

        (, int24 floorTickEnd,) = lm.positions(ThreePositionStrategy.Stage.FLOOR);
        r.floorMoved = (floorTickEnd != floorTickInit);

        if (r.lmEthAfterBull > 0) {
            r.retentionPct = r.lmEthEnd * 10_000 / r.lmEthAfterBull;
            r.drawdownBps = (r.lmEthAfterBull - r.lmEthTrough) * 10_000 / r.lmEthAfterBull;
        }

        _writeRow(ci, as_, aw, dd, r);

        if (comboIdx % 10 == 0) {
            console2.log("Progress:", comboIdx);
            console2.log("  traderPnl:", r.traderPnl);
            console2.log("  drawdown:", r.drawdownBps);
        }
    }

    function _writeRow(uint256 ci, uint256 as_, uint24 aw, uint256 dd, ComboResult memory r) internal {
        string memory row = string(abi.encodePacked(vm.toString(ci), ",", vm.toString(as_), ",", vm.toString(uint256(aw)), ","));
        row = string(abi.encodePacked(row, vm.toString(dd), ",", vm.toString(r.traderPnl), ",", vm.toString(r.actualSpent), ","));
        row = string(abi.encodePacked(row, vm.toString(r.lmEthStart), ",", vm.toString(r.lmEthAfterBull), ","));
        row = string(abi.encodePacked(row, vm.toString(r.lmEthEnd), ",", vm.toString(r.retentionPct), ",", r.floorMoved ? "true" : "false", ","));
        row = string(abi.encodePacked(row, vm.toString(r.lmEthTrough), ",", vm.toString(r.drawdownBps), ","));
        row = string(abi.encodePacked(row, vm.toString(r.sellAttempts), ",", vm.toString(r.lmNetGain), ","));
        row = string(abi.encodePacked(row, vm.toString(r.feeWethBull), ",", vm.toString(r.feeKrkBull), ","));
        row = string(abi.encodePacked(row, vm.toString(r.feeWethTotal), ",", vm.toString(r.feeKrkTotal)));
        vm.writeLine(summaryFile, row);
    }
}
