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

import { Kraiken } from "../../src/Kraiken.sol";

import { ThreePositionStrategy } from "../../src/abstracts/ThreePositionStrategy.sol";
import { IWETH9 } from "../../src/interfaces/IWETH9.sol";
import { LiquidityBoundaryHelper } from "../../test/helpers/LiquidityBoundaryHelper.sol";
import { TickMath } from "@aperture/uni-v3-lib/TickMath.sol";
import { IUniswapV3Pool } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";

/**
 * @title SwapExecutor
 * @notice Helper contract to execute swaps on Uniswap V3 pools for analysis scripts.
 * @dev Supports two modes:
 *      - Capped (default): trade sizes limited by LiquidityBoundaryHelper
 *      - Uncapped: trades go directly to pool with no size caps, matching production behavior
 *      Set uncapped=true via constructor to allow trades through position boundaries.
 */
contract SwapExecutor {
    IUniswapV3Pool public pool;
    IWETH9 public weth;
    Kraiken public harberg;
    bool public token0isWeth;
    ThreePositionStrategy public liquidityManager;
    bool public uncapped;

    constructor(IUniswapV3Pool _pool, IWETH9 _weth, Kraiken _harberg, bool _token0isWeth, ThreePositionStrategy _liquidityManager, bool _uncapped) {
        pool = _pool;
        weth = _weth;
        harberg = _harberg;
        token0isWeth = _token0isWeth;
        liquidityManager = _liquidityManager;
        uncapped = _uncapped;
    }

    function executeBuy(uint256 amount, address recipient) external returns (uint256) {
        uint256 safeAmount = amount;

        if (!uncapped) {
            uint256 maxBuyAmount = LiquidityBoundaryHelper.calculateBuyLimit(pool, liquidityManager, token0isWeth);
            safeAmount = amount > maxBuyAmount ? maxBuyAmount : amount;
        }

        if (safeAmount == 0) return 0;

        bool zeroForOne = token0isWeth;
        uint160 sqrtPriceLimitX96 = zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1;

        pool.swap(recipient, zeroForOne, int256(safeAmount), sqrtPriceLimitX96, "");
        return safeAmount;
    }

    function executeSell(uint256 amount, address recipient) external returns (uint256) {
        uint256 safeAmount = amount;

        if (!uncapped) {
            uint256 maxSellAmount = LiquidityBoundaryHelper.calculateSellLimit(pool, liquidityManager, token0isWeth);
            safeAmount = amount > maxSellAmount ? maxSellAmount : amount;
        }

        if (safeAmount == 0) return 0;

        bool zeroForOne = !token0isWeth;
        uint160 sqrtPriceLimitX96 = zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1;

        pool.swap(recipient, zeroForOne, int256(safeAmount), sqrtPriceLimitX96, "");
        return safeAmount;
    }

    // Callback required for Uniswap V3 swaps
    function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata) external {
        require(msg.sender == address(pool), "Unauthorized callback");

        if (amount0Delta > 0) {
            IWETH9(pool.token0()).transfer(address(pool), uint256(amount0Delta));
        }
        if (amount1Delta > 0) {
            IWETH9(pool.token1()).transfer(address(pool), uint256(amount1Delta));
        }
    }
}
