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

import "@uniswap-v3-core/interfaces/IUniswapV3Factory.sol";
import "@uniswap-v3-core/interfaces/IUniswapV3Pool.sol";

library UniswapHelpers {
    /**
     * @notice Computes the integer square root of a given number.
     * @param y The input value for which the square root is calculated.
     * @return z The integer square root of the input.
     */
    function sqrt(uint256 y) internal pure returns (uint256 z) {
        if (y > 3) {
            z = y;
            uint256 x = y / 2 + 1;
            while (x < z) {
                z = x;
                x = (y / x + x) / 2;
            }
        } else if (y != 0) {
            z = 1;
        }
    }

    function calcPriceFor1Cent(bool token0isWeth) public pure returns (uint160 sqrtPriceX96) {
        uint256 price;
        if (token0isWeth) {
            // ETH as token0, so we are setting the price of 1 ETH in terms of token1 (USD cent)
            price = 2400 * 10 ** 20; // 1 ETH = 2400 USD, scaled by 10^18 for precision
        } else {
            // Token (valued at 1 USD cent) as token0, ETH as token1
            // We invert the logic to represent the price of 1 token in terms of ETH
            price = uint256(10 ** 16) / 2400; // Adjust for 18 decimal places
        }

        // Calculate the square root of the price with scaling for Uniswap V3 initialization
        sqrtPriceX96 = uint160(sqrt(price) * 2 ** 96 / 10 ** 9);
    }

    /**
     * @notice Initializes a Uniswap V3 pool with the price of 1 cent per ETH or 1 ETH in terms of token price.
     * @dev Uses the square root of the price to initialize the pool with proper scaling.
     * @param _pool The address of the Uniswap V3 pool to initialize.
     * @param token0isWeth A boolean indicating if WETH is token0 in the pool.
     */
    function initializePoolFor1Cent(IUniswapV3Pool _pool, bool token0isWeth) internal {
        uint160 sqrtPriceX96 = calcPriceFor1Cent(token0isWeth);
        // Initialize the pool with the calculated price
        _pool.initialize(sqrtPriceX96);
    }

    /**
     * @notice Deploys the Uniswap V3 factory using the provided bytecode and returns the factory instance.
     * @return factory The IUniswapV3Factory instance created by the deployment.
     */
    function deployUniswapFactory() internal returns (IUniswapV3Factory factory) {
        bytes memory factoryBytecode =
            hex"[encoded data, 49878 characters]";
        address factoryAddress = deployContract(factoryBytecode, "");
        factory = IUniswapV3Factory(factoryAddress);
    }

    /**
     * @notice Internal function to deploy a contract using its bytecode and constructor arguments.
     * @param bytecode The contract bytecode to be deployed.
     * @param constructorArgs The constructor arguments for the contract.
     * @return addr The address of the deployed contract.
     */
    function deployContract(bytes memory bytecode, bytes memory constructorArgs) internal returns (address addr) {
        bytes memory fullCode = abi.encodePacked(bytecode, constructorArgs);

        // Deploy the contract with CREATE
        assembly {
            addr := create(0, add(fullCode, 0x20), mload(fullCode))
            if iszero(extcodesize(addr)) { revert(0, 0) }
        }
    }
}
