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

import {Script, console} from "forge-std/Script.sol";
import {AaveQuotePushFlashReceiver} from "../../contracts/flash/AaveQuotePushFlashReceiver.sol";
import {QuotePushTreasuryManager} from "../../contracts/flash/QuotePushTreasuryManager.sol";

interface IDODOPMMPoolQuoteManaged {
    function querySellQuote(address trader, uint256 payQuoteAmount) external view returns (uint256 receiveBaseAmount, uint256 mtFee);
}

/**
 * @title RunManagedMainnetAaveCwusdcUsdcQuotePushCycle
 * @notice Simulate or broadcast a full manager-backed cycle:
 *         flash quote-push -> harvest retained quote into treasury manager -> split to configured recipients.
 *
 * Env:
 *   Same flash envs as RunMainnetAaveCwusdcUsdcQuotePushOnce
 *   QUOTE_PUSH_TREASURY_MANAGER_MAINNET           required
 *   QUOTE_PUSH_TREASURY_HARVEST                   optional; default 1
 *   QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW   optional; default 0
 *
 * Notes:
 *   - Gas holdback target is a quote-denominated cap. The script computes:
 *       gasAmount = min(manager.availableQuote(), gasHoldbackTargetRaw)
 *       recycleAmount = manager.availableQuote() - gasAmount
 *   - This is primarily used by the keeper dry-run so flash and recycle happen in the
 *     same simulated environment and post-flash surplus is visible to the manager.
 */
contract RunManagedMainnetAaveCwusdcUsdcQuotePushCycle is Script {
    address internal constant DEFAULT_POOL = 0x69776fc607e9edA8042e320e7e43f54d06c68f0E;
    address internal constant DEFAULT_CWUSDC = 0x2de5F116bFcE3d0f922d9C8351e0c5Fc24b9284a;
    address internal constant DEFAULT_USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;

    function run() external {
        uint256 pk = vm.envUint("PRIVATE_KEY");
        address receiver = vm.envAddress("AAVE_QUOTE_PUSH_RECEIVER_MAINNET");
        address managerAddr = vm.envAddress("QUOTE_PUSH_TREASURY_MANAGER_MAINNET");
        address pool = vm.envOr("POOL_CWUSDC_USDC_MAINNET", DEFAULT_POOL);
        address integration = vm.envAddress("DODO_PMM_INTEGRATION_MAINNET");
        address baseToken = vm.envOr("CWUSDC_MAINNET", DEFAULT_CWUSDC);
        address usdc = vm.envOr("USDC_MAINNET", DEFAULT_USDC);
        address unwinder = vm.envAddress("QUOTE_PUSH_EXTERNAL_UNWINDER_MAINNET");
        uint256 amount = vm.envUint("FLASH_QUOTE_AMOUNT_RAW");
        bool harvest = vm.envOr("QUOTE_PUSH_TREASURY_HARVEST", uint256(1)) == 1;
        uint256 gasHoldbackTargetRaw = vm.envOr("QUOTE_PUSH_TREASURY_GAS_HOLDBACK_TARGET_RAW", uint256(0));

        QuotePushTreasuryManager manager = QuotePushTreasuryManager(managerAddr);
        AaveQuotePushFlashReceiver.QuotePushParams memory p =
            _loadQuotePushParams(receiver, pool, integration, baseToken, unwinder, amount);

        console.log("receiver", receiver);
        console.log("manager", managerAddr);
        console.log("pool", pool);
        console.log("amount", amount);
        console.log("managerAvailableBefore", manager.availableQuote());
        console.log("receiverSweepableBefore", manager.receiverSweepableQuote());
        console.log("gasHoldbackTargetRaw", gasHoldbackTargetRaw);

        vm.startBroadcast(pk);
        (uint256 harvested, uint256 gasAmount, uint256 recycleAmount) =
            manager.runManagedCycle(usdc, amount, p, harvest, gasHoldbackTargetRaw);
        vm.stopBroadcast();

        console.log("managedCycleHarvestedRaw", harvested);
        console.log("managedCycleGasDistributionRaw", gasAmount);
        console.log("managedCycleRecycleDistributionRaw", recycleAmount);
        console.log("managerQuoteAfter", manager.quoteBalance());
        console.log("managerAvailableAfter", manager.availableQuote());
        console.log("receiverSweepableAfter", manager.receiverSweepableQuote());
    }

    function _loadQuotePushParams(
        address receiver,
        address pool,
        address integration,
        address baseToken,
        address unwinder,
        uint256 amount
    ) internal view returns (AaveQuotePushFlashReceiver.QuotePushParams memory p) {
        uint256 minPmmNum = vm.envOr("MIN_OUT_PMM_NUM", uint256(985));
        uint256 minPmmDen = vm.envOr("MIN_OUT_PMM_DEN", uint256(1000));

        uint256 minOutPmm = vm.envOr("MIN_OUT_PMM", uint256(0));
        if (minOutPmm == 0) {
            (uint256 baseOut,) = IDODOPMMPoolQuoteManaged(pool).querySellQuote(receiver, amount);
            minOutPmm = (baseOut * minPmmNum) / minPmmDen;
        }

        uint256 premiumBps = vm.envOr("AAVE_FLASH_PREMIUM_BPS", uint256(5));
        uint256 buf = vm.envOr("MIN_OUT_UNWIND_BUFFER_RAW", uint256(5000));
        uint256 minOutUnwind = vm.envOr("MIN_OUT_UNWIND", uint256(0));
        if (minOutUnwind == 0) {
            uint256 premium = (amount * premiumBps) / 10000;
            minOutUnwind = amount + premium + buf;
        }

        uint256 unwindMode = vm.envOr("UNWIND_MODE", uint256(0));
        bytes memory unwindData;
        if (unwindMode == 0) {
            uint24 fee = uint24(vm.envUint("UNWIND_V3_FEE_U24"));
            unwindData = abi.encode(fee);
        } else if (unwindMode == 1) {
            address dodoPool = vm.envAddress("UNWIND_DODO_POOL");
            unwindData = abi.encode(dodoPool);
        } else if (unwindMode == 2) {
            string memory pathHex = vm.envString("UNWIND_V3_PATH_HEX");
            bytes memory path = vm.parseBytes(pathHex);
            unwindData = abi.encode(path);
        } else if (unwindMode == 4) {
            address poolA = vm.envAddress("UNWIND_TWO_HOP_POOL_A");
            address poolB = vm.envAddress("UNWIND_TWO_HOP_POOL_B");
            address midToken = vm.envAddress("UNWIND_TWO_HOP_MID_TOKEN");
            uint256 minMidOut = vm.envOr("UNWIND_MIN_MID_OUT_RAW", uint256(1));
            unwindData = abi.encode(poolA, poolB, midToken, minMidOut);
        } else if (unwindMode == 5) {
            address dodoPool = vm.envAddress("UNWIND_DODO_POOL");
            address intermediateToken = vm.envAddress("UNWIND_INTERMEDIATE_TOKEN");
            uint256 minIntermediateOut = vm.envOr("UNWIND_MIN_INTERMEDIATE_OUT_RAW", uint256(1));
            string memory pathHex = vm.envString("UNWIND_V3_PATH_HEX");
            bytes memory path = vm.parseBytes(pathHex);
            unwindData = abi.encode(dodoPool, intermediateToken, minIntermediateOut, path);
        } else if (unwindMode == 6) {
            address poolA = vm.envAddress("UNWIND_TWO_HOP_POOL_A");
            address poolB = vm.envAddress("UNWIND_TWO_HOP_POOL_B");
            address midToken = vm.envAddress("UNWIND_TWO_HOP_MID_TOKEN");
            uint256 minMidOut = vm.envOr("UNWIND_MIN_MID_OUT_RAW", uint256(1));
            address intermediateToken = vm.envAddress("UNWIND_INTERMEDIATE_TOKEN");
            uint256 minIntermediateOut = vm.envOr("UNWIND_MIN_INTERMEDIATE_OUT_RAW", uint256(1));
            string memory pathHex = vm.envString("UNWIND_V3_PATH_HEX");
            bytes memory path = vm.parseBytes(pathHex);
            unwindData = abi.encode(poolA, poolB, midToken, minMidOut, intermediateToken, minIntermediateOut, path);
        } else {
            revert("UNWIND_MODE must be 0, 1, 2, 4, 5, or 6");
        }

        p = AaveQuotePushFlashReceiver.QuotePushParams({
            integration: integration,
            pmmPool: pool,
            baseToken: baseToken,
            externalUnwinder: unwinder,
            minOutPmm: minOutPmm,
            minOutUnwind: minOutUnwind,
            unwindData: unwindData,
            atomicBridge: AaveQuotePushFlashReceiver.AtomicBridgeParams({
                coordinator: address(0),
                sourceChain: 0,
                destinationChain: 0,
                destinationAsset: address(0),
                bridgeAmount: 0,
                minDestinationAmount: 0,
                destinationRecipient: address(0),
                destinationDeadline: 0,
                routeId: bytes32(0),
                settlementMode: bytes32(0),
                submitCommitment: false
            })
        });

        console.log("minOutPmm", minOutPmm);
        console.log("minOutUnwind", minOutUnwind);
    }
}
