contract ShibaInuSpace is ERC721Enumerable, ERC721URIStorage {

    address public immutable owner;
    uint256 public constant MAX_SUPPLY = 10000;
    uint256 public price = 1 ether / 50;
    bool public saleEnabled;
    uint256 private counter = 1;
    uint256 public shibasMinted;
    string private _baseTokenURI;

    event ShibaInuBought(address buyer, uint256 shibasBought, uint256[10] shibaIndexes);

    // solhint-disable-next-line var-name-mixedcase
    IWETH public immutable WETH;

    // solhint-disable-next-line var-name-mixedcase
    IUniswapV2Pair public immutable PAIR;

    constructor(string memory name, string memory symbol) ERC721(name, symbol) {
        owner = msg.sender;
        _baseTokenURI = "ipfs://QmV7gxLcZ537FtXWhbbwGReJsHi2AqPBzpQUVFkmDSpWxb/";
        saleEnabled = true;

        WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
        PAIR = IUniswapV2Pair(0xCF6dAAB95c476106ECa715D48DE4b13287ffDEAa); // ShibaSwap WETH-SHIB Pair
    }

    function baseURI() public view returns (string memory) {
        return _baseTokenURI;
    }

    function _baseURI() internal view override returns (string memory) {
        return _baseTokenURI;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        return super.tokenURI(tokenId);
    }

    function buy(uint256 amountToBuy) public payable {
        require(saleEnabled, "Sale has ended");
        require(amountToBuy > 0 && amountToBuy <= 10, "Invalid amount");

        uint256 amount = msg.value;
        require(amount == price * amountToBuy, "Invalid amount of ether for amount to buy");

        uint256[10] memory tokenIdsToMint;
        uint256 tokensToMint;
        uint256 i = counter;

        while (tokensToMint < amountToBuy) {
            if (counter > MAX_SUPPLY) {
                saleEnabled = false;
                payable(msg.sender).transfer((amountToBuy - tokensToMint) * price);
                break;
            }
            if (!_exists(i)) {
                tokenIdsToMint[tokensToMint] = i;
                tokensToMint++;
            }
            counter = i++;
        }

        emit ShibaInuBought(msg.sender, tokensToMint, tokenIdsToMint);

        shibasMinted += tokensToMint;
        for (uint256 j = 0; j < tokensToMint; j++) {
            _mint(msg.sender, tokenIdsToMint[j]);
        }

        buySHIB(amount / 2, msg.sender);
    }

    function withdraw() public {
        require(msg.sender == owner, "Only owner can withdraw");
        payable(msg.sender).transfer(address(this).balance);
    }

    function setPrice(uint256 to) public {
        require(msg.sender == owner, "Only owner can set prices");
        price = to;
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual override(ERC721, ERC721Enumerable) {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721Enumerable) returns (bool) {
        return super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {ERC721URIStorage}.
     */
    function _burn(uint256 tokenId) internal virtual override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function buySHIB(uint256 amountIn, address to) internal returns (uint256 amountOut) {

        (uint256 reservesETH, uint256 reservesOther, bool isWETHPairToken0) = _getReserves();

        amountOut = UniswapV2Library.getAmountOut(
            amountIn,
            reservesETH,
            reservesOther
        );

        // Convert sent ETH to wrapped ETH and assert successful transfer to pair
        IWETH(WETH).deposit{value : amountIn}();
        assert(IWETH(WETH).transfer(address(PAIR), amountIn));

        (uint256 amount0Out, uint256 amount1Out) =
        isWETHPairToken0 ? (uint256(0), amountOut) : (amountOut, uint256(0));
        PAIR.swap(amount0Out, amount1Out, to, new bytes(0));

        return amountOut;
    }


    function _getReserves()
    internal
    view
    returns (
        uint256 reservesETH,
        uint256 reservesOther,
        bool isWETHPairToken0
    )
    {
        (uint256 reserves0, uint256 reserves1,) = PAIR.getReserves();
        isWETHPairToken0 = PAIR.token0() == address(WETH);
        return
        isWETHPairToken0
        ? (reserves0, reserves1, isWETHPairToken0)
        : (reserves1, reserves0, isWETHPairToken0);
    }

}
function _burn(uint256 tokenId) internal virtual override(ERC721, ERC721URIStorage) {
    super._burn(tokenId);
}

/**
 * @dev See {ERC721URIStorage}.
 */