// SPDX-License-Identifier: MIT
pragma solidity 0.8.12;

import "@openzeppelin/contracts/access/Ownable.sol";
import "./GovernanceToken.sol";

/**
 * @title  MintManager
 * @notice Set as `owner` of the OP token and responsible for the token inflation schedule.
 *         Contract acts as the token "mint manager" with permission to the `mint` function only.
 *         Currently permitted to mint once per year of up to 2% of the total token supply.
 *         Upgradable to allow changes in the inflation schedule.
 */
contract MintManager is Ownable {
    /**
     * @notice The GovernanceToken that the MintManager can mint tokens
     */
    GovernanceToken public immutable governanceToken;

    /**
     * @notice The amount of tokens that can be minted per year. The value is a fixed
     *          point number with 4 decimals.
     */
    uint256 public constant MINT_CAP = 20; // 2%

    /**
     * @notice The number of decimals for the MINT_CAP.
     */
    uint256 public constant DENOMINATOR = 1000;

    /**
     * @notice The amount of time that must pass before the MINT_CAP number of tokens can
     *         be minted again.
     */
    uint256 public constant MINT_PERIOD = 365 days;

    /**
     * @notice Tracks the time of last mint
     */
    uint256 public mintPermittedAfter;

    /**
     * @param _upgrader        The owner of this contract
     * @param _governanceToken The governance token this contract can mint
     *                         tokens of
     */
    constructor(address _upgrader, address _governanceToken) {
        transferOwnership(_upgrader);
        governanceToken = GovernanceToken(_governanceToken);
    }

    /**
     * @notice Only the token owner is allowed to mint a certain amount of OP per year.
     *
     * @param _account Address to mint new tokens to.
     * @param _amount  Amount of tokens to be minted.
     */
    function mint(address _account, uint256 _amount) public onlyOwner {
        if (mintPermittedAfter > 0) {
            require(mintPermittedAfter <= block.timestamp, "OP: minting not permitted yet");

            require(
                _amount <= (governanceToken.totalSupply() * MINT_CAP) / DENOMINATOR,
                "OP: mint amount exceeds cap"
            );
        }

        mintPermittedAfter = block.timestamp + MINT_PERIOD;

        governanceToken.mint(_account, _amount);
    }

    /**
     * @notice Upgrade the owner of the governance token to a new MintManager.
     *
     * @param _newMintManager The MintManager to upgrade to
     */
    function upgrade(address _newMintManager) public onlyOwner {
        require(_newMintManager != address(0), "OP: Mint manager cannot be empty");

        governanceToken.transferOwnership(_newMintManager);
    }
}
