import { ERC20Wrapper } from '@0x/contracts-asset-proxy';
import { blockchainTests, describe } from '@0x/contracts-test-utils';
import * as _ from 'lodash';

import { deployAndConfigureContractsAsync, StakingApiWrapper } from './utils/api_wrapper';
import { CumulativeRewardTrackingSimulation, TestAction } from './utils/cumulative_reward_tracking_simulation';

// tslint:disable:no-unnecessary-type-assertion
// tslint:disable:max-file-line-count
blockchainTests.resets('Cumulative Reward Tracking', env => {
    // tokens & addresses
    let accounts: string[];
    let owner: string;
    // wrappers
    let stakingApiWrapper: StakingApiWrapper;
    let simulation: CumulativeRewardTrackingSimulation;
    // let testWrapper: TestRewardBalancesContract;
    let erc20Wrapper: ERC20Wrapper;

    // tests
    before(async () => {
        // create accounts
        accounts = await env.getAccountAddressesAsync();
        owner = accounts[0];
        const actors = accounts.slice(1);
        // set up ERC20Wrapper
        erc20Wrapper = new ERC20Wrapper(env.provider, accounts, owner);
        // deploy staking contracts
        stakingApiWrapper = await deployAndConfigureContractsAsync(env, owner, erc20Wrapper);
        simulation = new CumulativeRewardTrackingSimulation(stakingApiWrapper, actors);
        await simulation.deployAndConfigureTestContractsAsync(env);
    });

    describe('Tracking Cumulative Rewards (CR)', () => {
        it('pool created at epoch 0', async () => {
            await simulation.runTestAsync([], [TestAction.CreatePool], [{ event: 'SetCumulativeReward', epoch: 0 }]);
        });
        it('pool created in epoch >0', async () => {
            await simulation.runTestAsync(
                [TestAction.Finalize],
                [TestAction.CreatePool],
                [{ event: 'SetCumulativeReward', epoch: 1 }],
            );
        });
        it('delegating in the same epoch pool is created', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                ],
                [
                    // Updates CR for epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                ],
                [],
            );
        });
        it('re-delegating in the same epoch', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                ],
                [
                    // Updates CR for epoch 0
                    TestAction.Delegate,
                    // Updates CR for epoch 0
                    TestAction.Delegate,
                ],
                [],
            );
        });
        it('delegating in new epoch', async () => {
            // since there was no delegation in epoch 0 there is no longer a dependency on the CR for epoch 0
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                    // Moves to epoch 1
                    TestAction.Finalize,
                ],
                [
                    // Creates a CR for epoch 1
                    // Sets MRCR to epoch 1
                    // Unsets the CR for epoch 0
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 1 }],
            );
        });
        it('re-delegating in a new epoch', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR in epoch 0
                    TestAction.CreatePool,
                    // Updates CR for epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                    // Moves to epoch 1
                    TestAction.Finalize,
                ],
                [
                    // Updates CR for epoch 1
                    // Sets MRCR to epoch 1
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 1 }],
            );
        });
        it('delegating in epoch 1 then again in epoch 2', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Creates CR for epoch 1
                    // Sets MRCR to epoch 1
                    TestAction.Delegate,
                    // Move to epoch 2
                    TestAction.Finalize,
                ],
                [
                    // Updates CR for epoch 2
                    // Sets MRCR to epoch 2
                    // Creates CR for epoch 3
                    // Clears CR for epoch 1
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 2 }],
            );
        });
        it('delegate in epoch 1 then undelegate in epoch 2', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Creates CR for epoch 1
                    // Sets MRCR to epoch 0
                    // Clears CR for epoch 0
                    // Creates CR for epoch 2
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                ],
                [
                    // Update CR for epoch 2
                    // Set MRCR to epoch 2
                    // Clear CR for epoch 1
                    TestAction.Undelegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 2 }],
            );
        });
        it('delegate in epoch 0 and epoch 1, then undelegate half in epoch 2', async () => {
            await simulation.runTestAsync(
                [
                    // Create CR for epoch 0
                    TestAction.CreatePool,
                    // Updates CR for epoch 0
                    // Sets MRCR to epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Updates CR for epoch 1
                    // Sets MRCR to epoch 1
                    // Creates CR for epoch 2
                    // Clears CR for epoch 0
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                ],
                [
                    // Updates CR for epoch 2
                    // Sets MRCR to epoch 2
                    // Creates CR for epoch 3 (because there will still be stake)
                    // Clears CR for epoch 1
                    TestAction.Undelegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 2 }],
            );
        });
        it('delegate in epoch 1 and 2 then again in 3', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                    // Updates CR for epoch 0
                    // Sets MRCR to epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Updates CR for epoch 1
                    // Sets MRCR to epoch 1
                    // Creates CR for epoch 2
                    // Clears CR for epoch 0
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                ],
                [
                    // Updates CR for epoch 2
                    // Sets MRCR to epoch 2
                    // Creates CR for epoch 3
                    // Clears CR for epoch 1
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 2 }],
            );
        });
        it('delegate in epoch 0, earn reward in epoch 1', async () => {
            await simulation.runTestAsync(
                [
                    // Create CR for epoch 0
                    TestAction.CreatePool,
                    // Updates CR for epoch 0
                    // Sets MRCR to epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Credits pool with rewards
                    TestAction.PayProtocolFee,
                ],
                [
                    // Moves to epoch 2
                    // Creates CR for epoch 2
                    // Sets MRCR to epoch 2
                    TestAction.Finalize,
                ],
                [{ event: 'SetCumulativeReward', epoch: 2 }],
            );
        });
        it('delegate in epoch 0, epoch 2, earn reward in epoch 3, then delegate', async () => {
            await simulation.runTestAsync(
                [
                    // Create CR for epoch 0
                    TestAction.CreatePool,
                    // Updates CR for epoch 0
                    // Sets MRCR to epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Updates CR for epoch 1
                    // Sets MRCR to epoch 1
                    // Creates CR for epoch 2
                    // Clears CR for epoch 0
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                    // Credits pool with rewards
                    TestAction.PayProtocolFee,
                    // Moves to epoch 3
                    // Creates CR for epoch 3
                    // Sets MRCR to epoch 3
                    TestAction.Finalize,
                ],
                [
                    // Updates CR for epoch 3
                    // Creates CR for epoch 4
                    // Clears CR for epoch 1
                    // Clears CR for epoch 2
                    TestAction.Delegate,
                ],
                [],
            );
        });
        it('delegate in epoch 0 and 1, earn reward in epoch 3, then undelegate half', async () => {
            await simulation.runTestAsync(
                [
                    // Create CR for epoch 0
                    TestAction.CreatePool,
                    // Updates CR for epoch 0
                    // Sets MRCR to epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Updates CR for epoch 1
                    // Sets MRCR to epoch 1
                    // Creates CR for epoch 2
                    // Clears CR for epoch 0
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                    // Credits pool with rewards
                    TestAction.PayProtocolFee,
                    // Moves to epoch 3
                    // Creates CR for epoch 3
                    // Sets MRCR to epoch 3
                    TestAction.Finalize,
                ],
                [
                    // Updates CR for epoch 3
                    // Creates CR for epoch 4 (because there is still stake remaming)
                    // Clears CR for epoch 1
                    // Clears CR for epoch 2
                    TestAction.Undelegate,
                ],
                [],
            );
        });
        it('delegate in epoch 1, 2, earn rewards in epoch 3, skip to epoch 4, then delegate', async () => {
            await simulation.runTestAsync(
                [
                    // Create CR for epoch 0
                    TestAction.CreatePool,
                    // Updates CR for epoch 0
                    // Sets MRCR to epoch 0
                    // Creates CR for epoch 1
                    TestAction.Delegate,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Updates CR for epoch 1
                    // Sets MRCR to epoch 1
                    // Creates CR for epoch 2
                    // Clears CR for epoch 0
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                    // Credits pool with rewards
                    TestAction.PayProtocolFee,
                    // Moves to epoch 3
                    // Creates CR for epoch 3
                    // Sets MRCR to epoch 3
                    TestAction.Finalize,
                    // Moves to epoch 4
                    TestAction.Finalize,
                ],
                [
                    // Creates CR for epoch 4
                    // Sets MRCR to epoch 4
                    // Clears CR for epoch 3
                    // Creates CR for epoch 5
                    // Clears CR for epoch 1
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 4 }],
            );
        });
        it('earn reward in epoch 1 with no stake, then delegate', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                    // Credit pool with rewards
                    TestAction.PayProtocolFee,
                    // Moves to epoch 1
                    // That's it, because there's no active pools.
                    TestAction.Finalize,
                ],
                [
                    // Updates CR to epoch 1
                    // Sets MRCR to epoch 1
                    // Clears CR for epoch 0
                    // Creates CR for epoch 2
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 1 }],
            );
        });
        it('delegate in epoch 1, 3, then delegate in epoch 4', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Creates CR for epoch 1
                    // Sets MRCR to epoch 0
                    // Clears CR for epoch 0
                    // Creates CR for epoch 2
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                    // Moves to epoch 3
                    TestAction.Finalize,
                    // Creates CR for epoch 3
                    // Sets MRCR to epoch 3
                    // Clears CR for epoch 1
                    // Creates CR for epoch 4
                    // Clears CR for epoch 2
                    TestAction.Delegate,
                    // Moves to epoch 4
                    TestAction.Finalize,
                ],
                [
                    // Updates CR for epoch 4
                    // Sets MRCR to epoch 4
                    // Clears CR for epoch 3
                    // Creates CR for epoch 5
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 4 }],
            );
        });
        it('delegate in epoch 1, then epoch 3', async () => {
            await simulation.runTestAsync(
                [
                    // Creates CR for epoch 0
                    TestAction.CreatePool,
                    // Moves to epoch 1
                    TestAction.Finalize,
                    // Creates CR for epoch 1
                    // Sets MRCR to epoch 0
                    // Clears CR for epoch 0
                    // Creates CR for epoch 2
                    TestAction.Delegate,
                    // Moves to epoch 2
                    TestAction.Finalize,
                    // Moves to epoch 3
                    TestAction.Finalize,
                ],
                [
                    // Creates CR for epoch 3
                    // Sets MRCR to epoch 3
                    // Clears CR for epoch 1
                    // Creates CR for epoch 4
                    // Clears CR for epoch 2
                    TestAction.Delegate,
                ],
                [{ event: 'SetCumulativeReward', epoch: 3 }],
            );
        });
    });
});
// tslint:enable:no-unnecessary-type-assertion
