/**
 * artifact.js
 * Copyright (c) 2016 Samsung Electronics Co., Ltd. All rights reserved.
 *
 * Contact:
 * Sungmin Kim <sm.art.kim@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 async = require('async');
var _ = require('underscore');

var DError = require('../../core/exception.js');
var utils = require('../../lib/utils.js');

/**
 * @module models/artifact
 */

/**
 * @constructor
 * @param {number} id - id
 * @param {string} name - artifact name
 * @param {string} distName - distribution name
 * @param {string} type - artifact type
 * @param {string} version - artifact version
 * @param {string} environment - environment
 * @param {string} attribute - attribute
 * @param {string} status - status
 * @param {string} file - artifact file name
 * @param {string} path - artifact file path
 * @param {number} size - artifact file size
 * @param {string} checksum - artifact checksum
 * @param {string} description - artifact description
 * @param {array} options - artifact info
 */

function Artifact(name, distName, type, version, environment, attribute, status, file, path, size, checksum, description, options) {
    var self = this;
    /** @type {number} */
    this.id = -1;
    /** @type {string} */
    this.name = name || '';
    /** @type {string} */
    this.distName = distName || '';
    /** @type {string} */
    this.type = type || '';
    /** @type {string} */
    this.version = version || '';
    /** @type {string} */
    this.environment = environment || '';
    /** @type {string} */
    this.attribute = attribute || '';
    /** @type {string} */
    this.status = status || 'OPEN';
    /** @type {string} */
    this.file = file || '';
    /** @type {string} */
    this.path = path || '';
    /** @type {number} */
    this.size = size || -1;
    /** @type {string} */
    this.checksum = checksum || '';
    /** @type {string} */
    this.description = description || '';
    /** @type {array} */
    this.options = options || {};
}


/**
 * @function create
 * @param {string} name - artifact name
 * @param {string} distName - distribution name
 * @param {string} type - artifact type (archive/binary)
 * @param {string} version - artifact version
 * @param {string} environment - environment
 * @param {string} attribute - attribute (root/binary/)
 * @param {string} status - status (OPEN/CLOSE/TEMP)
 * @param {string} file - artifact file name
 * @param {string} path - artifact file path
 * @param {number} size - artifact file size
 * @param {string} checksum - artifact checksum
 * @param {string} description - artifact description
 * @param {array} options - snapshot info
 * @memberOf module:models/snapshot
 */

module.exports.create = create;
function create(name, distName, type, version, environment, attribute, status,
    file, path, size, checksum, description, options, callback) {

    callback(null, new Artifact(name, distName, type, version, environment, attribute, status,
        file, path, size, checksum, description, options));
}


module.exports.insert = function (conn, artifact, arg3, arg4) {
    var callback;
    var transaction = true;
    if (_.isFunction(arg3)) {
        callback = arg3;
    } else {
        transaction = arg3;
        callback = arg4;
    }

    var artifactObj = _.clone(artifact);

    async.waterfall([
        function (cb) {
            if (transaction !== false) {
                conn.query('START TRANSACTION', function (err) {
                    cb(err);
                });
            } else {
                cb(null);
            }
        },
        function (cb) {
            // insert artifact
            var sql = 'INSERT INTO artifacts SET ' +
                ' name = ' + utils.DBStr(artifactObj.name) +
                ', distribution_name = ' + utils.DBStr(artifactObj.distName) +
                ', type = ' + utils.DBStr(artifactObj.type) +
                ', version = ' + utils.DBStr(artifactObj.version) +
                ', environment = ' + utils.DBStr(artifactObj.environment) +
                ', attribute = ' + utils.DBStr(artifactObj.attribute) +
                ', status = ' + utils.DBStr(artifactObj.status) +
                ', file = ' + utils.DBStr(artifactObj.file) +
                ', path = ' + utils.DBStr(artifactObj.path) +
                ', size = ' + artifactObj.size +
                ', checksum = ' + utils.DBStr(artifactObj.checksum) +
                ', description = ' + utils.DBStr(artifactObj.description);

            conn.query(sql, function (err, result) {
                if (err) {
                    cb(new DError('MODEL003', sql, err));
                } else {
                    artifactObj.id = result.insertId;
                    cb(null);
                }
            });
        },
        function (cb) {
            var options = _.clone(artifactObj.options);

            async.eachLimit(_.keys(options), 5, function (idx, cb1) {
                if (_.isUndefined(artifactObj.options[idx]) || _.isNull(artifactObj.options[idx])) {
                    cb1(null);
                } else {
                    var strNtype = utils.objectToStringAndType(artifactObj.options[idx]);
                    var sql = 'INSERT INTO artifact_info SET ' +
                        '  artifact_id = ' + utils.DBStr(artifactObj.id) +
                        ', property = ' + utils.DBStr(idx) +
                        ', value = ' + utils.DBStr(strNtype.string) +
                        ', type = ' + utils.DBStr(strNtype.type);

                    conn.query(sql, function (err) {
                        if (err) {
                            cb1(new DError('MODEL003', sql, err));
                        } else {
                            cb1(null);
                        }
                    });
                }
            }, cb);
        },
        function (cb) {
            if (transaction !== false) {
                conn.query('COMMIT', function (err) {
                    cb(err);
                });
            } else {
                cb(null);
            }
        }
    ],
    function (err) {
        if (err) {
            if (transaction !== false) {
                conn.query('ROLLBACK', function () {});
            }
        }
        callback(err, artifactObj);
    });
};

module.exports.select = select;
function select(conn, condition, callback) {
    var where = '';
    var whereList = [];
    var keyList = ['id', 'name', 'distribution_name', 'type', 'version', 'environment', 'attribute', 'file', 'path', 'size', 'checksum', 'description'];
    var limit = '';

    var cond = _.clone(condition);
    cond.distribution_name = condition.distName;
    delete cond['distName'];

    _.each(keyList, function (key) {
        if (cond[key] !== undefined) {
            var query = null;
            if (key === 'id') {
                if (_.isArray(cond[key])) {
                    query = key + ' in (' + _.map(cond[key], function (val) { return utils.DBStr(val); }).join(',') + ') ';
                } else {
                    query = key + ' = ' + utils.DBStr(cond[key]);
                }
            } else {
                query = key + ' = ' + utils.DBStr(cond[key]);
            }
            whereList.push('artifacts.' + query);
        }
    });

    async.waterfall([
        function (cb) {
            // limit info
            if (cond.count !== undefined) {
                if (cond.offset !== undefined) {
                    limit += (' LIMIT ' + cond.offset + ' , ' + cond.count);
                } else {
                    limit += (' LIMIT ' + cond.count);
                }
            }

            if (whereList.length > 0) {
                where = ' WHERE ' + whereList.join(' AND ');
            }
            cb(null);
        },
        function (cb) {
            var sql = 'SELECT * FROM artifacts ' + where + limit;

            conn.query(sql, function (err, artifacts) {
                if (err) {
                    cb(new DError('MODEL003', { sql: sql }, err));
                } else {
                    async.map(artifacts, function (artifact, cb1) {
                        var info = 'SELECT * FROM artifact_info WHERE artifact_id = ' + utils.DBStr(artifact.id);

                        conn.query(info, function (err1, artifactInfo) {
                            if (err1) {
                                cb1(new DError('MODEL003', { sql: info }, err1));
                            } else {

                                var options = {};
                                _.each(artifactInfo, function (data) {
                                    options[data.property] = utils.stringAndTypeToObj(data.value, data.type);
                                });

                                create(artifact.name, artifact.distribution_name, artifact.type, artifact.version,
                                    artifact.environment, artifact.attribute, artifact.status, artifact.file, artifact.path,
                                    artifact.size, artifact.checksum, artifact.description, options, function (err2, result) {
                                        result.id = artifact.id;
                                        cb1(err2, result);
                                    });
                            }
                        });
                    }, function (err3, results) {
                        if (err3) {
                            cb(new DError('ARTIFACT001', {
                                condition: cond
                            }, err3));
                        } else {
                            cb(null, results);
                        }
                    });
                }
            });
        }
    ],
    function (err, results) {
        callback(err, results);
    });
};


module.exports.update = function (conn, artifact, callback) {
    async.waterfall([
        function (cb) {
            conn.query('START TRANSACTION', function (err) {
                if (err) {
                    cb(new DError('MODEL001', err));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            var sql = 'UPDATE artifacts SET' +
                ' name = ' + utils.DBStr(artifact.name) +
                ', distribution_name = ' + utils.DBStr(artifact.distName) +
                ', type = ' + utils.DBStr(artifact.type) +
                ', version = ' + utils.DBStr(artifact.version) +
                ', environment = ' + utils.DBStr(artifact.environment) +
                ', attribute = ' + utils.DBStr(artifact.attribute) +
                ', status = ' + utils.DBStr(artifact.status) +
                ', file = ' + utils.DBStr(artifact.file) +
                ', path = ' + utils.DBStr(artifact.path) +
                ', size = ' + utils.DBStr(artifact.size) +
                ', checksum = ' + utils.DBStr(artifact.checksum) +
                ', description = ' + utils.DBStr(artifact.description) +
                ' WHERE id = ' + utils.DBStr(artifact.id);

            conn.query(sql, function (err) {
                if (err) {
                    cb(new DError('MODEL003', {
                        sql: sql
                    }, err), null);
                } else {
                    cb(err);
                }
            });
        },
        function (cb) {
            conn.query('COMMIT', function (err) {
                if (err) {
                    cb(new DError('MODEL002', err));
                } else {
                    cb(err);
                }
            });
        }
    ],
    function (err) {
        if (err) {
            conn.query('ROLLBACK', function () {});
        }
        callback(err);
    });
};

// TODO: need to improve delete routine
module.exports.delete = function (conn, id, callback) {
    async.waterfall([
        function (cb) {
            conn.query('START TRANSACTION', function (err) {
                if (err) {
                    cb(new DError('MODEL001', err));
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            var sql = 'DELETE FROM artifact_info WHERE artifact_id = ' + utils.DBStr(id);
            conn.query(sql, function (err) {
                if (err) {
                    cb(new DError('MODEL003', { sql: sql }, err), null);
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            var sql = 'DELETE FROM artifacts WHERE id = ' + utils.DBStr(id);
            conn.query(sql, function (err) {
                if (err) {
                    cb(new DError('MODEL003', { sql: sql }, err), null);
                } else {
                    cb(null);
                }
            });
        },
        function (cb) {
            conn.query('COMMIT', function (err) {
                if (err) {
                    cb(new DError('MODEL002', err));
                } else {
                    cb(err);
                }
            });
        }
    ],
    function (err) {
        if (err) {
            conn.query('ROLLBACK', function () {});
        }
        callback(err);
    });
};
