/**
 * notification.js
 * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Contact:
 * DongHee Yang <donghee.yang@samsung.com>
 * Sungmin Kim <sm.art.kim@samsung.com>
 * Jiil Hyoun <jiil.hyoun@samsung.com>
 * Jonghwan Park <iwin100.park@samsung.com>
 * Kitae Kim <kt920.kim@samsung.com>
 *
 * Licensed under the Apache 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://www.apache.org/licenses/LICENSE-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.
 *
 * Contributors:
 * - S-Core Co., Ltd
**/

var dibs = require('../../core/dibs');
var DError = require('../../core/exception');
var async = require('async');
var _ = require('underscore');
var utils = require('../../lib/utils.js');

/**
 * @module models/notification
 */
/**
 * @constructor
 * @param {string} name
 * @param {string} description
 * @param {module:models/notification.options} options
 * @memberOf module:models/notification
 */
function Notification(id, groupId, event, method, type, target, description) {
    this.id = id;
    this.groupId = groupId;
    this.event = event;
    this.method = method;
    this.type = type;
    this.target = target;
    this.description = description;
}

/**
 * @function create
 * @param {number} id
 * @param {string} event
 * @param {string} method
 * @param {string} type
 * @param {string} description
 * @returns {module:dibs.model.common/notification.Notification}
 * @memberOf module:dibs.model.common/notification
 */

module.exports.create = function (id, groupId, event, method, type, target, description) {
    return new Notification(id, groupId, event, method, type, target, description);
};

/**
 * @function insert
 * @param {object} conn - db connection object
 * @param {module:models/notification.insert} notification
 * @param {module:models/notification.callback_err_notification} callback
 * @memberOf module:models/notification
 */

module.exports.insert = function (conn, notification, callback) {
    conn.query('START TRANSACTION', function (err) {
        if (err) {
            callback(new DError('MODEL001', err), null);
            return;
        }

        // excute transaction sql
        async.waterfall(
            [
                // TABLE: notifications
                function (wcb) {
                    if (notification.groupId) {
                        wcb(null, notification.groupId);
                    } else {
                        var sql = 'INSERT INTO notification_group (description) ' +
                            'VALUES (' + utils.DBStr(notification.description) + ')';
                        conn.query(sql, function (err, result) {
                            if (err) {
                                wcb(new DError('MODEL003', {
                                    sql: sql
                                }, err), null);
                                return;
                            }
                            wcb(null, result.insertId);
                        });
                    }
                },
                // TABLE: notification_info
                function (notificationGroupId, wcb) {
                    var targetKey;
                    var targetValue;
                    if (notification.type === 'USER_ID') {
                        targetKey = ', target_user_id';
                        targetValue = notification.target;
                    } else if (notification.type === 'GROUP_ID') {
                        targetKey = ', target_group_id';
                        targetValue = notification.target;
                    } else if (notification.type === 'DATA') {
                        targetKey = ', target_data';
                        targetValue = utils.DBStr(notification.target);
                    } else {
                        wcb(new DError('NOTI002', {
                            type: notification.type
                        }, err), null);
                        return;
                    }
                    var sql = 'INSERT INTO notifications ( notification_group_id' +
                        ', event' +
                        ', method' +
                        ', target_type' +
                        targetKey + ' ) ' +
                        'VALUES (' + notificationGroupId +
                        ', ' + utils.DBStr(notification.event) +
                        ', ' + utils.DBStr(notification.method) +
                        ', ' + utils.DBStr(notification.type) +
                        ', ' + targetValue + ')';
                    conn.query(sql, function (err, result) {
                        if (err) {
                            wcb(new DError('MODEL003', {
                                sql: sql
                            }, err), null);
                            return;
                        }
                        wcb(null, notificationGroupId);
                    });
                },
                // commit sql
                function (id, wcb) {
                    conn.query('COMMIT', function (err) {
                        if (err) {
                            callback(new DError('MODEL002', err), null);
                            return;
                        }
                        wcb(err, id);
                    });
                }
            ],
            function (err, result) {
                if (err) {
                    conn.query('ROLLBACK', function () {});
                    callback(new DError('NOTI001', {
                        desc: notification.description
                    }, err), null);
                    return;
                }
                callback(err, result);
            });
    });
};

/**
 * @callback callback_err_optionlength
 * @param {error|undefined} error
 * @param {string} optionlength
 * @memberOf module:models/notification
 */

/**
 * @function update
 * @param {object} conn - db connection object
 * @param {module:models/notification.update} notification
 * @param {module:models/notification.callback_err_optionlength} callback
 * @memberOf module:models/notification
 */

module.exports.update = function (conn, notification, callback) {
    var i;
    conn.query('START TRANSACTION', function (err) {
        if (err) {
            callback(new DError('MODEL001', err), null);
            return;
        }

        // Check condition of key
        if (notification.id === undefined) {
            callback(new DError('', err), null);
            return;
        }

        // excute transaction sql
        async.waterfall(
            [
                // Find
                function (cb) {
                    select(conn, {
                        id: notification.id
                    }, function (err, notifications) {
                        if (err) {
                            cb(err, null);
                            return;
                        } else if (notifications.length === 0) {
                            cb(new DError('NOTI005', {
                                id: notifications.id
                            }, err), null);
                            return;
                        }
                        cb(null, notifications[0]); // result is only one.
                    });
                },
                // TABLE: notifications
                function (notifications, cb) {
                    var update_list = [];
                    var update_key_list = ['event', 'method'];
                    for (i = 0; i < update_key_list.length; i++) {
                        var key = update_key_list[i];
                        if (notification[key] !== undefined) {
                            update_list.push(key + ' = ' + utils.DBStr(notification[key]));
                        }
                    }

                    if (notification.type === 'USER_ID') {
                        update_list.push('target_type = ' + utils.DBStr(notification.type));
                        update_list.push('target_user_id = ' + notification.target);
                    } else if (notification.type === 'GROUP_ID') {
                        update_list.push('target_type = ' + utils.DBStr(notification.type));
                        update_list.push('target_group_id= ' + notification.target);
                    } else if (notification.type === 'DATA') {
                        update_list.push('target_type = ' + utils.DBStr(notification.type));
                        update_list.push('target_data = ' + utils.DBStr(notification.target));
                    } else {
                        wcb(new DError('NOTI002', {
                            type: notification.type
                        }, err), null);
                        return;
                    }

                    // None update list
                    if (update_list.length === 0) {
                        cb(null, null);
                        return;
                    }

                    var sql = 'UPDATE notifications SET ' + update_list.join(', ') + ' WHERE id = ' + notification.id;
                    conn.query(sql, function (err, result) {
                        if (err) {
                            cb(new DError('MODEL003', {
                                sql: sql
                            }, err), null);
                            return;
                        }
                        cb(null, result);
                    });
                },
                // commit sql
                function (result, cb) {
                    conn.query('COMMIT', function (err) {
                        if (err) {
                            cb(new DError('MODEL002', err), null);
                            return;
                        }
                        cb(err, result);
                    });
                }
            ],
            function (err, result) {
                if (err) {
                    conn.query('ROLLBACK', function () {});
                    callback(new DError('ENV006', err), null);
                    return;
                }
                callback(err, result);
            });
    });
};

/**
 * @callback callback_err_notification
 * @param {error|undefined} error
 * @param {module:models/notification.Environment} notification
 * @memberOf module:models/notification
 */

/**
 * @function select
 * @param {object} conn - db connection object
 * @param {module:models/notification.notificationQuerySql} condition
 * @param {module:models/notification.callback_err_notification} callback
 * @memberOf module:models/notification
 */

module.exports.select = select;
function select(conn, condition, callback) {
    var i;
    var where = '';
    var whereList = [];

    var availableKeyList = _.select(['id', 'group_id', 'event', 'method', 'type', 'data'],
        function (key) {
            return (condition[key] !== undefined);
        });

    whereList = _.map(availableKeyList, function (key) {
        var query = '';
        var value = condition[key];
        if (value === 'NULL') {
            query = ('notifications.' + key + ' is NULL ');
        } else if ((new RegExp('^[=><]+')).test(value)) {
            query = ('notifications.' + key + ' ' + value + ' ');
        } else if (_.isArray(value)) {
            query = ('notifications.' + key + ' in (' + _.map(value, function (val) {
                    return utils.DBStr(val);
                }).join(',') + ') ');
        } else {
            query = ('notifications.' + key + ' = ' + utils.DBStr(condition[key]));
        }
        return query;
    });

    if (condition.groupId !== undefined) {
        whereList.push('notifications.notification_group_id = ' + utils.DBStr(condition.groupId));
    }
    if (condition.type !== undefined) {
        if (condition.data === undefined) {
            callback(new DError('NOTI004', err));
            return;
        }
        if (condition.type === 'USER_ID') {
            whereList.push('notifications.target_user_id = ' + condition.target);
        } else if (condition.type === 'GROUP_ID') {
            whereList.push('notifications.target_group_id = ' + condition.target);
        } else if (condition.type === 'DATA') {
            whereList.push('notifications.target_data = ' + utils.DBStr(condition.target));
        } else {
            callback(new DError('NOTI002', {
                type: condition.type
            }, err));
            return;
        }
    }

    // limit info
    var limit = '';
    if (condition.count !== undefined) {
        if (condition.offset !== undefined) {
            limit += (' LIMIT ' + condition.offset + ' , ' + condition.count);
        } else {
            limit += (' LIMIT ' + condition.count);
        }
    }

    if (whereList.length > 0) {
        where = ' WHERE notifications.notification_group_id = notification_group.id AND ' + whereList.join(' AND ');
    } else {
        where = ' WHERE notifications.notification_group_id = notification_group.id ';
    }

    var sql = 'SELECT notifications.id' +
        ', notifications.notification_group_id groupId' +
        ', notifications.event' +
        ', notifications.method' +
        ', notifications.target_type type ' +
        ', CASE notifications.target_type ' +
        'WHEN \'USER_ID\' THEN notifications.target_user_id ' +
        'WHEN \'GROUP_ID\' THEN notifications.target_group_id ' +
        'WHEN \'DATA\' THEN notifications.target_data ' +
        'ELSE notifications.target_data ' +
        'END AS target' +
        ' FROM notifications, notification_group' +
        where + limit;
    conn.query(sql, function (err, notifications) {
        if (err) {
            callback(new DError('MODEL003', {
                sql: sql
            }, err));
            return;
        }
        callback(null, notifications);
    });
}

/**
 * @typedef {object} notificationQuerySql
 * @property {string} id
 * @property {string} name
 * @property {string} description
 * @property {string} count
 * @property {string} offset
 * @memberOf module:models/notification
 */

/**
 * @function delete
 * @param {object} conn - db connection object
 * @param {module:models/notification.notificationQuerySql} condition
 * @param {module:models/notification.callback_err_notification} callback
 * @memberOf module:models/notification
 */

module.exports.delete = function (conn, condition, callback) {
    conn.query('START TRANSACTION', function (err) {
        if (err) {
            callback(new DError('MODEL001', err), null);
            return;
        }

        // Check condition of key
        if (condition.id === undefined) {
            callback(new DError('NOTI006', err), null);
            return;
        }

        // excute transaction sql
        async.waterfall(
            [
                // Find
                function (cb) {
                    select(conn, condition, function (err, notifications) {
                        if (err) {
                            cb(err, null);
                            return;
                        }
                        if (notifications[0] === undefined) {
                            cb(new DError('NOTI007', {
                                id: condition.id
                            }, err), null);
                            return;
                        }

                        cb(null, notifications[0]); // result is only one.
                    });
                },
                // TABLE: notifications
                function (notification, cb) {
                    var sql = 'DELETE FROM notifications WHERE id = ' + notification.id + '';
                    conn.query(sql, function (err, result) {
                        if (err) {
                            cb(new DError('MODEL003', {
                                sql: sql
                            }, err), null);
                            return;
                        }
                        cb(null, notification);
                    });
                },
                // commit sql
                function (notification, cb) {
                    conn.query('COMMIT', function (err) {
                        if (err) {
                            cb(new DError('MODEL002', err), null);
                            return;
                        }
                        cb(err, notification);
                    });
                }
            ],
            function (err, result) {
                if (err) {
                    conn.query('ROLLBACK', function () {});
                    callback(new DError('NOTI008', err), null);
                    return;
                }
                callback(err, result);
            });
    });
};
