/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

var _ = require('underscore');

var Cassandra = require('oae-util/lib/cassandra');
var OaeUtil = require('oae-util/lib/util');

/**
 * Get the list of followers of the specified user
 *
 * @param  {String}     userId                  The id of the user whose followers to fetch
 * @param  {String}     start                   The id of the user from which to start listing followers
 * @param  {Number}     limit                   The maximum number of followers to fetch
 * @param  {Function}   callback                Standard callback function
 * @param  {Object}     callback.err            An error that occurred, if any
 * @param  {String[]}   callback.followerIds    The list of user ids of users who follow the specified user
 * @param  {String}     [callback.nextToken]    The value to provide as the `start` parameter when fetching the next page of followers. If unspecified, there are no more followers to fetch
 */
var getFollowers = module.exports.getFollowers = function(userId, start, limit, callback) {
    start = start || '';

    Cassandra.runPagedQuery('FollowingUsersFollowers', 'userId', userId, 'followerId', start, limit, null, function(err, rows, nextToken) {
        if (err) {
            return callback(err);
        }

        var followerIds = _.map(rows, function(row) {
            return row.get('followerId').value;
        });

        return callback(null, followerIds, nextToken);
    });
};

/**
 * Get the list of users the specified user follows
 *
 * @param  {String}     userId                      The id of the user whose following list to fetch
 * @param  {String}     start                       The id of the user from which to start listing followed users
 * @param  {Number}     limit                       The maximum number of followed user ids to fetch
 * @param  {Function}   callback                    Standard callback function
 * @param  {Object}     callback.err                An error that occurred, if any
 * @param  {String[]}   callback.followedUserIds    The user ids of users that are followed by the specified user
 * @param  {String}     [callback.nextToken]        The value to provide as the `start` parameter when fetching the next page of followed users. If unspecified, there are no more users to fetch
 */
var getFollowing = module.exports.getFollowing = function(userId, start, limit, callback) {
    start = start || '';

    Cassandra.runPagedQuery('FollowingUsersFollowing', 'userId', userId, 'followingId', start, limit, null, function(err, rows, nextToken) {
        if (err) {
            return callback(err);
        }

        var followingIds = _.map(rows, function(row) {
            return row.get('followingId').value;
        });

        return callback(null, followingIds, nextToken);
    });
};

/**
 * Given a list of potential followed user ids, determine which of them are being followed by the provided user
 *
 * @param  {String}     followingUserId     The id of the user to test against
 * @param  {String[]}   followedUserIds     The ids of the users who the following user may be following
 * @param  {Function}   callback            Standard callback function
 * @param  {Object}     callback.err        An error that occurred, if any
 * @param  {Object}     callback.following  An object whose key is the user id from followedUserIds and value is `true` or falsey, indicating whether or not the following user is following them
 */
var isFollowing = module.exports.isFollowing = function(followingUserId, followedUserIds, callback) {
    if (_.isEmpty(followedUserIds)) {
        return callback(null, {});
    }

    Cassandra.runQuery('SELECT "followingId", "value" FROM "FollowingUsersFollowing" WHERE "userId" = ? AND "followingId" IN (?)', [followingUserId, followedUserIds], function(err, rows) {
        if (err) {
            return callback(err);
        }

        var following = {};
        _.each(rows, function(row) {
            row = Cassandra.rowToHash(row);
            if (row.value === '1') {
                following[row.followingId] = true;
            }
        });

        return callback(null, following);
    });
};

/**
 * Save the followed users for the specified follower user id
 *
 * @param  {String}     followerUserId      The id of the user who will follow the list of followed user ids
 * @param  {String[]}   followedUserIds     The id of the users who will be followed by the follower user id
 * @param  {Function}   callback            Standard callback function
 * @param  {Object}     callback.err        An error that occurred, if any
 */
var saveFollows = module.exports.saveFollows = function(followerUserId, followedUserIds, callback) {
    followedUserIds = followedUserIds || [];
    followedUserIds = _.compact(followedUserIds);
    if (_.isEmpty(followedUserIds)) {
        return callback();
    }

    var queries = [];
    _.each(followedUserIds, function(followedUserId) {
        queries.push(
            // Query that indicates the follower is following the user
            {
                'query': 'INSERT INTO "FollowingUsersFollowing" ("userId", "followingId", "value") VALUES (?, ?, ?)',
                'parameters': [followerUserId, followedUserId, '1']
            },

            // Query that indicates the user is followed by the user
            {
                'query': 'INSERT INTO "FollowingUsersFollowers" ("userId", "followerId", "value") VALUES (?, ?, ?)',
                'parameters': [followedUserId, followerUserId, '1']
            }
        );
    });

    Cassandra.runBatchQuery(queries, callback);
};

/**
 * Perform the queries necessary to ensure that the user identified by `followerUserid` no longer follows
 * the users identified by `followedUserIds`.
 *
 * @param  {String}     followerUserId      The id of the user who will unfollow the given followed users
 * @param  {String[]}   followedUserIds     The id of the users who will become unfollowed
 * @param  {Function}   callback            Standard callback function
 * @param  {Object}     callback.err        An error that occurred, if any
 */
var deleteFollows = module.exports.deleteFollows = function(followerUserId, followedUserIds, callback) {
    followedUserIds = followedUserIds || [];
    followedUserIds = _.compact(followedUserIds);
    if (_.isEmpty(followedUserIds)) {
        return callback();
    }

    var queries = [];

    _.each(followedUserIds, function(followedUserId) {
        queries.push(
            // Query that will delete all the `followedUserIds` from the follower's "Following" index
            {
                'query': 'DELETE FROM "FollowingUsersFollowing" WHERE "userId" = ? AND "followingId" = ?',
                'parameters': [followerUserId, followedUserId]
            },

            // Query that will delete the `followerUserIds` from each followed user's "Followers" index
            {
                'query': 'DELETE FROM "FollowingUsersFollowers" WHERE "userId" = ? AND "followerId" = ?',
                'parameters': [followedUserId, followerUserId]
            }
        );
    });

    Cassandra.runBatchQuery(queries, callback);
};
