contract AlphaStakingTier is Initializable, Governable {
  using SafeMath for uint;

  event SetAlphaTier(uint index, uint upperLimit);
  event DeleteAlphaTier(uint index);

  IAlphaStaking public staking;
  mapping(uint => uint) public tiers;
  uint public tierCount;

  function initialize(address _staking) external initializer {
    __Governable__init();
    staking = IAlphaStaking(_staking);
  }

  /// @dev Get user's staking tier
  /// @param user user address to get tier of
  function getAlphaTier(address user) external view returns (uint index) {
    uint stakeAmount = staking.getStakeValue(user);
    uint _tierCount = tierCount;
    for (uint i = 0; i < _tierCount; i++) {
      if (stakeAmount < tiers[i]) {
        return i;
      }
    }
    // user that staked more than the last tier upper limit is treated as the last tier
    // note: this should technically not be reachable.
    return _tierCount.sub(1);
  }

  /// @dev Set staking tiers
  /// @param upperLimits array of tier upper limits
  function setAlphaTiers(uint[] calldata upperLimits) external onlyGov {
    for (uint lIndex = 0; lIndex < upperLimits.length; lIndex++) {
      if (lIndex > 0) {
        require(
          upperLimits[lIndex] > upperLimits[lIndex - 1],
          'setAlphaTiers: upperLimits should be strictly increasing'
        );
      } else {
        require(upperLimits[lIndex] > 0, 'setAlphaTiers: first tier upper limit should be > 0');
      }
      tiers[lIndex] = upperLimits[lIndex];
      emit SetAlphaTier(lIndex, upperLimits[lIndex]);
    }

    uint _tierCount = tierCount; // gas opt
    // Resetting previous values
    for (uint eIndex = upperLimits.length; eIndex < _tierCount; eIndex++) {
      delete tiers[eIndex];
      emit DeleteAlphaTier(eIndex);
    }

    tierCount = upperLimits.length;
  }

  /// @dev Update existing staking tier
  /// @param index index of tier to update
  /// @param upperLimit new upper limit of tier to update
  function updateAlphaTier(uint index, uint upperLimit) external onlyGov {
    require(index < tierCount, 'updateAlphaTier: index out of range');
    require(upperLimit != 0, 'updateAlphaTier: upper limit cannot be 0');
    if (0 < index) {
      require(
        tiers[index - 1] < upperLimit,
        'updateAlphaTier: new upper limit must be more than the previous tier'
      );
    }
    if (index < tierCount.sub(1)) {
      require(
        upperLimit < tiers[index + 1],
        'updateAlphaTier: new upper limit must be less than the next tier'
      );
    }
    tiers[index] = upperLimit;
    emit SetAlphaTier(index, upperLimit);
  }
}
function setAlphaTiers(uint[] calldata upperLimits) external onlyGov {
  for (uint lIndex = 0; lIndex < upperLimits.length; lIndex++) {
    if (lIndex > 0) {
      require(
        upperLimits[lIndex] > upperLimits[lIndex - 1],
        'setAlphaTiers: upperLimits should be strictly increasing'
      );
    } else {
      require(upperLimits[lIndex] > 0, 'setAlphaTiers: first tier upper limit should be > 0');
    }
    tiers[lIndex] = upperLimits[lIndex];
    emit SetAlphaTier(lIndex, upperLimits[lIndex]);
  }

  uint _tierCount = tierCount; // gas opt
  // Resetting previous values
  for (uint eIndex = upperLimits.length; eIndex < _tierCount; eIndex++) {
    delete tiers[eIndex];
    emit DeleteAlphaTier(eIndex);
  }

  tierCount = upperLimits.length;
}

/// @dev Set staking tiers
/// @param upperLimits array of tier upper limits