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

import { ABDKMath64x64 } from "@abdk/ABDKMath64x64.sol";
import "@aperture/uni-v3-lib/TickMath.sol";
import { Math } from "@openzeppelin/utils/math/Math.sol";

/**
 * @title UniswapMath
 * @notice Abstract contract providing mathematical utilities for Uniswap V3 price and tick calculations
 * @dev Contains pure mathematical functions for price/tick conversions and validations
 */
abstract contract UniswapMath {
    using Math for uint256;

    /// @notice Calculates the Uniswap V3 tick corresponding to a given price ratio between Kraiken and ETH
    /// @param t0isWeth Boolean flag indicating if token0 is WETH
    /// @param tokenAmount Amount of the Kraiken token
    /// @param ethAmount Amount of Ethereum
    /// @return tick_ The calculated tick for the given price ratio
    function _tickAtPrice(bool t0isWeth, uint256 tokenAmount, uint256 ethAmount) internal pure returns (int24 tick_) {
        require(ethAmount > 0, "ETH amount cannot be zero");
        if (tokenAmount == 0) {
            // KRAIKEN/ETH
            tick_ = TickMath.MAX_TICK;
        } else {
            // Use ABDKMath64x64 for precise division and square root calculation
            int128 priceRatioX64 = ABDKMath64x64.div(int128(int256(tokenAmount)), int128(int256(ethAmount)));
            // KRAIKEN/ETH
            tick_ = _tickAtPriceRatio(priceRatioX64);
        }
        // convert to tick in a pool
        tick_ = t0isWeth ? tick_ : -tick_;
    }

    /// @notice Converts a price ratio to a Uniswap V3 tick
    /// @param priceRatioX64 The price ratio in ABDKMath64x64 format
    /// @return tick_ The corresponding tick
    function _tickAtPriceRatio(int128 priceRatioX64) internal pure returns (int24 tick_) {
        // Convert the price ratio into a sqrt price in the format expected by Uniswap's TickMath
        uint160 sqrtPriceX96 = uint160(int160(ABDKMath64x64.sqrt(priceRatioX64) << 32));
        tick_ = TickMath.getTickAtSqrtRatio(sqrtPriceX96);
    }

    /// @notice Calculates the price ratio from a given Uniswap V3 tick
    /// @dev Returns price (token1/token0) in Q96 format: price * 2^96
    ///      Computed as sqrtRatioX96² / 2^96 = (sqrt(price) * 2^96)² / 2^96 = price * 2^96
    /// @param tick The tick for which to calculate the price ratio
    /// @return priceRatioX96 The price ratio in Q96 format
    function _priceAtTick(int24 tick) internal pure returns (uint256 priceRatioX96) {
        uint256 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick);
        priceRatioX96 = sqrtRatioX96.mulDiv(sqrtRatioX96, (1 << 96));
    }

    /// @notice Clamps tick to valid range and aligns to tick spacing
    /// @param tick The tick to clamp
    /// @param tickSpacing The tick spacing to align to
    /// @return clampedTick The clamped and aligned tick
    function _clampToTickSpacing(int24 tick, int24 tickSpacing) internal pure returns (int24 clampedTick) {
        // Align to tick spacing first
        clampedTick = tick / tickSpacing * tickSpacing;

        // Ensure tick is within valid bounds
        if (clampedTick < TickMath.MIN_TICK) clampedTick = TickMath.MIN_TICK;
        if (clampedTick > TickMath.MAX_TICK) clampedTick = TickMath.MAX_TICK;
    }
}
