contract TestFinalizer is
    TestStakingNoWETH
{
    event DepositStakingPoolRewards(
        bytes32 poolId,
        uint256 reward,
        uint256 membersStake
    );

    struct UnfinalizedPoolReward {
        uint256 totalReward;
        uint256 membersStake;
    }

    struct FinalizedPoolRewards {
        uint256 operatorReward;
        uint256 membersReward;
        uint256 membersStake;
    }

    address payable private _operatorRewardsReceiver;
    address payable private _membersRewardsReceiver;
    mapping (bytes32 => uint32) private _operatorSharesByPool;

    /// @param operatorRewardsReceiver The address to transfer rewards into when
    ///        a pool is finalized.
    constructor(
        address payable operatorRewardsReceiver,
        address payable membersRewardsReceiver
    )
        public
    {
        _addAuthorizedAddress(msg.sender);
        init();
        _operatorRewardsReceiver = operatorRewardsReceiver;
        _membersRewardsReceiver = membersRewardsReceiver;
        _removeAuthorizedAddressAtIndex(msg.sender, 0);
    }

    // this contract can receive ETH
    // solhint-disable no-empty-blocks
    function ()
        external
        payable
    {}

    /// @dev Activate a pool in the current epoch.
    function addActivePool(
        bytes32 poolId,
        uint32 operatorShare,
        uint256 feesCollected,
        uint256 membersStake,
        uint256 weightedStake
    )
        external
    {
        require(feesCollected > 0, "FEES_MUST_BE_NONZERO");
        uint256 currentEpoch_ = currentEpoch;
        IStructs.PoolStats memory poolStats = poolStatsByEpoch[poolId][currentEpoch_];
        require(poolStats.feesCollected == 0, "POOL_ALREADY_ADDED");
        _operatorSharesByPool[poolId] = operatorShare;
        poolStatsByEpoch[poolId][currentEpoch_] = IStructs.PoolStats({
            feesCollected: feesCollected,
            membersStake: membersStake,
            weightedStake: weightedStake
        });

        aggregatedStatsByEpoch[currentEpoch_].totalFeesCollected += feesCollected;
        aggregatedStatsByEpoch[currentEpoch_].totalWeightedStake += weightedStake;
        aggregatedStatsByEpoch[currentEpoch_].numPoolsToFinalize += 1;
    }

    /// @dev Drain the balance of this contract.
    function drainBalance()
        external
    {
        address(0).transfer(address(this).balance);
    }

    /// @dev Compute Cobb-Douglas.
    function cobbDouglas(
        uint256 totalRewards,
        uint256 ownerFees,
        uint256 totalFees,
        uint256 ownerStake,
        uint256 totalStake
    )
        external
        view
        returns (uint256 ownerRewards)
    {
        ownerRewards = LibCobbDouglas.cobbDouglas(
            totalRewards,
            ownerFees,
            totalFees,
            ownerStake,
            totalStake,
            cobbDouglasAlphaNumerator,
            cobbDouglasAlphaDenominator
        );
    }

    /// @dev Expose `_getUnfinalizedPoolReward()`
    function getUnfinalizedPoolRewards(bytes32 poolId)
        external
        view
        returns (UnfinalizedPoolReward memory reward)
    {
        (reward.totalReward, reward.membersStake) = _getUnfinalizedPoolRewards(
            poolId
        );
    }

    /// @dev Expose pool stats for the input epoch.
    function getPoolStatsFromEpoch(uint256 epoch, bytes32 poolId)
        external
        view
        returns (IStructs.PoolStats memory)
    {
        return poolStatsByEpoch[poolId][epoch];
    }

    function getAggregatedStatsForPreviousEpoch()
        external
        view
        returns (IStructs.AggregatedStats memory)
    {
        return aggregatedStatsByEpoch[currentEpoch - 1];
    }

    /// @dev Overridden to log and transfer to receivers.
    function _syncPoolRewards(
        bytes32 poolId,
        uint256 reward,
        uint256 membersStake
    )
        internal
        returns (uint256 operatorReward, uint256 membersReward)
    {
        uint32 operatorShare = _operatorSharesByPool[poolId];
        (operatorReward, membersReward) = _computePoolRewardsSplit(
            operatorShare,
            reward,
            membersStake
        );
        address(_operatorRewardsReceiver).transfer(operatorReward);
        address(_membersRewardsReceiver).transfer(membersReward);
        emit DepositStakingPoolRewards(poolId, reward, membersStake);
    }

    /// @dev Overriden to just increase the epoch counter.
    function _goToNextEpoch() internal {
        currentEpoch += 1;
    }
}
function getUnfinalizedPoolRewards(bytes32 poolId)
    external
    view
    returns (UnfinalizedPoolReward memory reward)
{
    (reward.totalReward, reward.membersStake) = _getUnfinalizedPoolRewards(
        poolId
    );
}

/// @dev Expose `_getUnfinalizedPoolReward()`