contract DAOVaultMediumUSDC is ERC20, Ownable {
    using SafeERC20 for IERC20;
    using Address for address;
    using SafeMath for uint256;

    IERC20 public token;
    IStrategy public strategy;
    address public pendingStrategy;

    bool public canSetPendingStrategy = true;
    uint256 public unlockTime;
    uint256 public constant LOCKTIME = 2 days;

    event MigrateFunds(address indexed fromStrategy, address indexed toStrategy, uint256 amount);

    constructor(address _token, address _strategy) ERC20("DAO Vault Medium USDC", "dvmUSDC") {
        _setupDecimals(6);

        token = IERC20(_token);
        strategy = IStrategy(_strategy);
    }

    /**
     * @notice Deposit into strategy
     * @param _amounts A list that contains amounts to deposit
     * Requirements:
     * - Only EOA account can call this function
     */
    function deposit(uint256[] memory _amounts) external {
        require(!address(msg.sender).isContract(), "Only EOA");

        uint256 _before = strategy.balanceOf(address(this));
        strategy.deposit(_amounts);
        uint256 _after = strategy.balanceOf(address(this));
        
        _mint(msg.sender, _after.sub(_before));
    }

    /**
     * @notice Withdraw from strategy
     * @param _shares A list that contains shares to withdraw
     * @dev amount _shares = amount daoToken
     * Requirements:
     * - Only EOA account can call this function
     */
    function withdraw(uint256[] memory _shares) external {
        require(!address(msg.sender).isContract(), "Only EOA");
        
        uint256 _before = strategy.balanceOf(address(this));
        strategy.withdraw(_shares);
        uint256 _after = strategy.balanceOf(address(this));

        _burn(msg.sender, _before.sub(_after));
    }

    /**
     * @notice Refund from strategy
     * @notice This function usually only available when strategy in vesting state
     * Requirements:
     * - Only EOA account can call this function
     * - Amount dvmToken of user must greater than 0
     */
    function refund() external {
        require(!address(msg.sender).isContract(), "Only EOA");

        uint256 _shares = balanceOf(msg.sender);
        require(_shares > 0, "No balance to refund");

        uint256 _before = strategy.balanceOf(address(this));
        strategy.refund(_shares);
        uint256 _after = strategy.balanceOf(address(this));

        _burn(msg.sender, _before.sub(_after));
    }

    /**
     * @notice Set pending strategy
     * @param _pendingStrategy Address of pending strategy
     * Requirements:
     * - Only owner of this contract call this function
     * - Pending strategy must be a contract
     */
    function setPendingStrategy(address _pendingStrategy) external onlyOwner {
        require(canSetPendingStrategy, "Cannot set pending strategy now");
        require(_pendingStrategy.isContract(), "New strategy is not contract");

        pendingStrategy = _pendingStrategy;
    }

    /**
     * @notice Unlock function migrateFunds()
     * Requirements:
     * - Only owner of this contract call this function
     */
    function unlockMigrateFunds() external onlyOwner {
        unlockTime = block.timestamp + LOCKTIME;
        canSetPendingStrategy = false;
    }

    /**
     * @notice Migrate all funds from old strategy to new strategy
     * Requirements:
     * - Only owner of this contract call this function
     * - Calling this function within unlock time
     * - Pending strategy is set
     */
    function migrateFunds() external onlyOwner {
        require(unlockTime <= block.timestamp && unlockTime + 1 days >= block.timestamp, "Function locked");
        require(pendingStrategy != address(0), "No pendingStrategy");

        uint256 _amount = token.balanceOf(address(strategy));
        require(_amount > 0, "No balance to migrate");

        token.safeTransferFrom(address(strategy), pendingStrategy, _amount);
        // Remove balance of old strategy token
        IERC20 oldStrategyToken = IERC20(address(strategy));
        oldStrategyToken.safeTransfer(address(strategy), oldStrategyToken.balanceOf(address(this)));

        address oldStrategy = address(strategy);
        strategy = IStrategy(pendingStrategy);
        pendingStrategy = address(0);
        canSetPendingStrategy = true;

        unlockTime = 0; // Lock back this function

        emit MigrateFunds(oldStrategy, address(strategy), _amount);
    }
}
/// @title Contract to interact between user and strategy, and distribute daoToken to user
function refund() external {
    require(!address(msg.sender).isContract(), "Only EOA");

    uint256 _shares = balanceOf(msg.sender);
    require(_shares > 0, "No balance to refund");

    uint256 _before = strategy.balanceOf(address(this));
    strategy.refund(_shares);
    uint256 _after = strategy.balanceOf(address(this));

    _burn(msg.sender, _before.sub(_after));
}

/**
 * @notice Refund from strategy
 * @notice This function usually only available when strategy in vesting state
 * Requirements:
 * - Only EOA account can call this function
 * - Amount dvmToken of user must greater than 0
 */