/**
 *Submitted for verification at Etherscan.io on 2023-08-06
 */

pragma solidity ^0.8.4;

// SPDX-License-Identifier: MIT

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);

    function transfer(address recipient, uint256 amount)
        external
        returns (bool);

    function allowance(address owner, address spender)
        external
        view
        returns (uint256);

    function transferFrom(
        address sender,
        address recipient,
        uint256 amount
    ) external returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);
}

contract PinkyExchange {
    IERC20 public pinkyToken;
    IERC20 public usdtToken;
    uint256 public exchangeRateEth;
    uint256 public exchangeRateUsdt;
    address public admin;

    event TokensPurchasedWithEth(address buyer, uint256 amount);
    event TokensPurchasedWithUsdt(address buyer, uint256 amount);
    event ExchangeRateEthUpdated(uint256 oldRate, uint256 newRate);
    event ExchangeRateUsdtUpdated(uint256 oldRate, uint256 newRate);
    event OwnershipTransferred(address oldAdmin, address newAdmin);

    constructor(
        IERC20 _pinkyToken,
        IERC20 _usdtToken,
        uint256 _exchangeRateEth,
        uint256 _exchangeRateUsdt
    ) {
        pinkyToken = _pinkyToken;
        usdtToken = _usdtToken;
        exchangeRateEth = _exchangeRateEth;
        exchangeRateUsdt = _exchangeRateUsdt;
        admin = msg.sender;
    }

    function buyTokensWithETH() public payable {
        uint256 amountToBuy = msg.value * exchangeRateEth;
        require(
            pinkyToken.balanceOf(address(this)) >= amountToBuy,
            "Not enough tokens in the reserve"
        );
        pinkyToken.transfer(msg.sender, amountToBuy);
        emit TokensPurchasedWithEth(msg.sender, amountToBuy);
    }

    function buyTokensWithUSDT(uint256 _usdtAmount) public {
        uint256 amountToBuy = _usdtAmount * exchangeRateUsdt;
        require(
            pinkyToken.balanceOf(address(this)) >= amountToBuy,
            "Not enough tokens in the reserve"
        );
        require(
            usdtToken.allowance(msg.sender, address(this)) >= _usdtAmount,
            "Not enough USDT approved for transfer"
        );
        usdtToken.transferFrom(msg.sender, address(this), _usdtAmount);
        pinkyToken.transfer(msg.sender, amountToBuy);
        emit TokensPurchasedWithUsdt(msg.sender, amountToBuy);
    }

    function getETHExchangeRate() public view returns (uint256) {
        return exchangeRateEth;
    }

    function getUSDTExchangeRate() public view returns (uint256) {
        return exchangeRateUsdt;
    }

    function setETHExchangeRate(uint256 newRate) public {
        require(msg.sender == admin, "Only admin can set exchange rate");
        emit ExchangeRateEthUpdated(exchangeRateEth, newRate);
        exchangeRateEth = newRate;
    }

    function setUSDTExchangeRate(uint256 newRate) public {
        require(msg.sender == admin, "Only admin can set exchange rate");
        emit ExchangeRateUsdtUpdated(exchangeRateUsdt, newRate);
        exchangeRateUsdt = newRate;
    }

    function withdrawETH(uint256 _amount) public {
        require(msg.sender == admin, "Only admin can withdraw ETH");
        require(
            address(this).balance >= _amount,
            "Contract does not have enough balance"
        );
        payable(admin).transfer(_amount);
    }

    function withdrawPinkyTokens(uint256 _amount) public {
        require(msg.sender == admin, "Only admin can withdraw tokens");
        require(
            pinkyToken.balanceOf(address(this)) >= _amount,
            "Not enough tokens in the contract"
        );
        pinkyToken.transfer(admin, _amount);
    }

    function withdrawUsdtTokens(uint256 _amount) public {
        require(msg.sender == admin, "Only admin can withdraw USDT tokens");
        require(
            usdtToken.balanceOf(address(this)) >= _amount,
            "Not enough USDT tokens in the contract"
        );
        usdtToken.transfer(admin, _amount);
    }

    function transferOwnership(address newAdmin) public {
        require(msg.sender == admin, "Only admin can transfer ownership");
        emit OwnershipTransferred(admin, newAdmin);
        admin = newAdmin;
    }
}