Internal helper to move a task from one state to another (e.g. from
        QUEUED to DELAYED). The "when" argument indicates the timestamp of the
        task in the new state. If no to_state is specified, the task will be
        simply removed from the original state.

        The "mode" param can be specified to define how the timestamp in the
        new state should be updated and is passed to the ZADD Redis script (see
        its documentation for details).

        Raises TaskNotFound if the task is not in the expected state or not in
        the expected queue.

def _move(self, from_state=None, to_state=None, when=None, mode=None):
        """
        Internal helper to move a task from one state to another (e.g. from
        QUEUED to DELAYED). The "when" argument indicates the timestamp of the
        task in the new state. If no to_state is specified, the task will be
        simply removed from the original state.

        The "mode" param can be specified to define how the timestamp in the
        new state should be updated and is passed to the ZADD Redis script (see
        its documentation for details).

        Raises TaskNotFound if the task is not in the expected state or not in
        the expected queue.
        """

        pipeline = self.tiger.connection.pipeline()
        scripts = self.tiger.scripts
        _key = self.tiger._key

        from_state = from_state or self.state
        queue = self.queue

        assert from_state
        assert queue

        scripts.fail_if_not_in_zset(_key(from_state, queue), self.id,
                                    client=pipeline)
        if to_state:
            if not when:
                when = time.time()
            if mode:
                scripts.zadd(_key(to_state, queue), when, self.id,
                                  mode, client=pipeline)
            else:
                pipeline.zadd(_key(to_state, queue), self.id, when)
            pipeline.sadd(_key(to_state), queue)
        pipeline.zrem(_key(from_state, queue), self.id)

        if not to_state: # Remove the task if necessary
            if self.unique:
                # Only delete if it's not in any other queue
                check_states = set([ACTIVE, QUEUED, ERROR, SCHEDULED])
                check_states.remove(from_state)
                # TODO: Do the following two in one call.
                scripts.delete_if_not_in_zsets(_key('task', self.id, 'executions'),
                                                    self.id, [
                    _key(state, queue) for state in check_states
                ], client=pipeline)
                scripts.delete_if_not_in_zsets(_key('task', self.id),
                                                    self.id, [
                    _key(state, queue) for state in check_states
                ], client=pipeline)
            else:
                # Safe to remove
                pipeline.delete(_key('task', self.id, 'executions'))
                pipeline.delete(_key('task', self.id))

        scripts.srem_if_not_exists(_key(from_state), queue,
                _key(from_state, queue), client=pipeline)

        if to_state == QUEUED:
            pipeline.publish(_key('activity'), queue)

        try:
            scripts.execute_pipeline(pipeline)
        except redis.ResponseError as e:
            if '<FAIL_IF_NOT_IN_ZSET>' in e.args[0]:
                raise TaskNotFound('Task {} not found in queue "{}" in state "{}".'.format(
                    self.id, queue, from_state
                ))
            raise
        else:
            self._state = to_state