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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./interfaces/IERC721Mintable.sol";
import "./interfaces/IERC20Mintable.sol";

contract EventController is AccessControl {
    event PurchaseToken(address indexed buyer, uint256 tokenId);

    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

    IERC20Mintable public immutable PAYMENT_TOKEN;
    IERC721Mintable public EVENT_TOKEN;
    uint256 public EVENT_TOKEN_PRICE;
    uint256 public EVENT_TOKEN_PRICE_ETH;
    uint128 public PRICE_MODIFIER; // price in 0.01%, i.e. 500 = 5%, 5 = 0.05%
    uint256 public ETH_PURCHASE_REWARD;
    address public PAYMENT_RECEIVER;

    constructor(
        IERC20Mintable paymentToken_,
        IERC721Mintable eventToken_,
        uint256 eventTokenPrice_,
        uint256 eventTokenPriceETH_,
        uint128 priceModifier_,
        uint256 ethPurchaseReward_,
        address paymentReceiver_
    ) {
        PAYMENT_TOKEN = paymentToken_;
        EVENT_TOKEN = eventToken_;
        EVENT_TOKEN_PRICE = eventTokenPrice_;
        EVENT_TOKEN_PRICE_ETH = eventTokenPriceETH_;
        PRICE_MODIFIER = priceModifier_;
        ETH_PURCHASE_REWARD = ethPurchaseReward_;
        PAYMENT_RECEIVER = paymentReceiver_;

        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MANAGER_ROLE, msg.sender);
    }

    function _beforePayment() internal view {
        require(address(EVENT_TOKEN) != address(0), "Purchases are stopped");
    }

    function _afterPayment(bool mintReward) internal {
        uint256 reward = ETH_PURCHASE_REWARD;

        // Modifies price and reward for the next purchase
        uint256 kModifier = 10000 + PRICE_MODIFIER;

        EVENT_TOKEN_PRICE = EVENT_TOKEN_PRICE * kModifier / 10000;
        EVENT_TOKEN_PRICE_ETH = EVENT_TOKEN_PRICE_ETH * kModifier / 10000;
        ETH_PURCHASE_REWARD = ETH_PURCHASE_REWARD * kModifier / 10000;

        uint256 tokenId = EVENT_TOKEN.safeMint(msg.sender);
        if (mintReward && reward > 0) {
            PAYMENT_TOKEN.mint(msg.sender, reward);
        }
        emit PurchaseToken(msg.sender, tokenId);
    }

    function purchaseToken() external {
        _beforePayment();

        bool hasEnough = PAYMENT_TOKEN.balanceOf(msg.sender) >= EVENT_TOKEN_PRICE;
        require(hasEnough, "Not enough payment tokens");

        bool success = PAYMENT_TOKEN.transferFrom(msg.sender, PAYMENT_RECEIVER, EVENT_TOKEN_PRICE);
        require(success, "Transfer unsuccessful");

        _afterPayment(false);
    }

    function purchaseTokenETH() external payable {
        _beforePayment();
        bool correctSum = msg.value == EVENT_TOKEN_PRICE_ETH;
        require(correctSum, "Included amount is wrong");

        (bool success, ) = PAYMENT_RECEIVER.call{value: EVENT_TOKEN_PRICE_ETH}("");
        require(success, "Failed to forward ether");

        _afterPayment(true);
    }

    function updatePaymentSettings(
        IERC721Mintable eventToken_,
        uint256 eventTokenPrice_,
        uint256 eventTokenPriceETH_,
        uint128 priceModifier_,
        uint256 ethPurchaseReward_,
        address paymentReceiver_
    )
        external
        onlyRole(MANAGER_ROLE)
    {
        EVENT_TOKEN = eventToken_;
        EVENT_TOKEN_PRICE = eventTokenPrice_;
        EVENT_TOKEN_PRICE_ETH = eventTokenPriceETH_;
        PRICE_MODIFIER = priceModifier_;
        ETH_PURCHASE_REWARD = ethPurchaseReward_;
        PAYMENT_RECEIVER = paymentReceiver_;
    }
}
[{"inputs":[{"internalType":"contract IERC20Mintable","name":"paymentToken_","type":"address"},{"internalType":"contract IERC721Mintable","name":"eventToken_","type":"address"},{"internalType":"uint256","name":"eventTokenPrice_","type":"uint256"},{"internalType":"uint256","name":"eventTokenPriceETH_","type":"uint256"},{"internalType":"uint128","name":"priceModifier_","type":"uint128"},{"internalType":"uint256","name":"ethPurchaseReward_","type":"uint256"},{"internalType":"address","name":"paymentReceiver_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"PurchaseToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ETH_PURCHASE_REWARD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EVENT_TOKEN","outputs":[{"internalType":"contract IERC721Mintable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EVENT_TOKEN_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EVENT_TOKEN_PRICE_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAYMENT_RECEIVER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAYMENT_TOKEN","outputs":[{"internalType":"contract IERC20Mintable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRICE_MODIFIER","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"purchaseToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"purchaseTokenETH","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC721Mintable","name":"eventToken_","type":"address"},{"internalType":"uint256","name":"eventTokenPrice_","type":"uint256"},{"internalType":"uint256","name":"eventTokenPriceETH_","type":"uint256"},{"internalType":"uint128","name":"priceModifier_","type":"uint128"},{"internalType":"uint256","name":"ethPurchaseReward_","type":"uint256"},{"internalType":"address","name":"paymentReceiver_","type":"address"}],"name":"updatePaymentSettings","outputs":[],"stateMutability":"nonpayable","type":"function"}]