'use strict';
const aws = require("aws-sdk");
const attr = require('dynamodb-data-types').AttributeValue;
const reservedWords = require("./reserved");
/**
 * Help class to scan a table.
 * @class
 */
class scanBuilder {
    /**
    * Create a new scan builder to scan a table.
    * @constructor
    * @param {string} TableName The name of the table to assign to the scan builder.
    * @returns {scanBuilder}
    */
    constructor(TableName, _db) {
        this.params = {
            TableName,
            FilterExpression: ""
        };
        this._db = _db;
    }
    /**
     * Adds a filter to the scan to remove unwanted items.
     * @param {string} name The name of the key to search for.
     * @param {*} value The value of the key to be compared to.
     * @param {string} op How to compare the given value and each of the table's items.
     * @returns {scanBuilder} this
     */
    addFilter(name, value, op) {
        if (reservedWords.set[name.toUpperCase()]) {
            if (this.params.FilterExpression == "") {
                this.params.FilterExpression += "#" + name.substring(0, 3) + " " + op + " :" + name.substring(0, 3);
            } else {
                this.params.FilterExpression += " and " + "#" + name.substring(0, 3) + " " + op + " :" + name.substring(0, 3);
            }
            if (!this.params.ExpressionAttributeNames) {
                this.params.ExpressionAttributeNames = {};
            }
            if (!this.params.ExpressionAttributeValues) {
                this.params.ExpressionAttributeValues = {};
            }
            this.params.ExpressionAttributeNames["#" + name.substring(0, 3)] = name;
            this.params.ExpressionAttributeValues[":" + name.substring(0, 3)] = attr.wrap1(value);
        } else {
            if (this.params.FilterExpression == "") {
                this.params.FilterExpression += name + " " + op + " :" + name;
            } else {
                this.params.FilterExpression += " and " + name + " " + op + " :" + name;
            }
            if (!this.params.ExpressionAttributeValues) {
                this.params.ExpressionAttributeValues = {};
            }
            this.params.ExpressionAttributeValues[":" + name] = attr.wrap1(value);
        }
        return this;
    }
    /**
     * Adds an alias to the key name.
     * @deprecated
     * @param {string} alias 
     * @param {string} name
     * @returns {scanBuilder} this
     */
    addName(alias, name) {
        if (!this.params.ExpressionAttributeNames) {
            this.params.ExpressionAttributeNames = {};
        }
        this.params.ExpressionAttributeNames["#" + alias] = name;
        return this;
    }
    /**
     * 
     * @param {string} alias Alias or name of an value.
     * @param {*} value
     * @returns {scanBuilder} this
     */
    addValue(alias, value) {
        if (!this.params.ExpressionAttributeValues) {
            this.params.ExpressionAttributeValues = {};
        }
        this.params.ExpressionAttributeValues[":" + alias] = attr.wrap1(value);
        return this;
    }
    /**
     * 
     * @param {string} filter How the scan to filter out items.
     * @returns {scanBuilder} this
     */
    setFilter(filter) {
        this.params.FilterExpression = filter;
        return this;
    }
    /**
     * @returns {Promise.<Array, Error>} Returns an Promise containing an array of items scanned that matched the filters. 
     */
    execute() {
        var db = this._db;
        var params = this.params;
        var items = [];
        return new Promise(function (resolve, reject) {
            var onScan = function (err, data) {
                if (err) {
                    reject(err);
                    return;
                }
                items.push.apply(items, data.Items);
                if (typeof data.LastEvaluatedKey != "undefined") {
                    params.ExclusiveStartKey = data.LastEvaluatedKey;
                    db.scan(params, onScan);
                } else {
                    resolve(items);
                }
            };
            db.scan(params, onScan);
        });
    }
}
/**
 * Help class to update an item.
 * @class
 */
class updateBuilder {
     /**
    * Create a new update builder to update a item.
    * @constructor
    * @param {string} TableName The name of the table to assign to the update builder.
    * @returns {updateBuilder}
    */
    constructor(TableName, _db) {
        this.params = {
            TableName,
            UpdateExpression: ""
        };
        this._db = _db;
    }
    /**
     * 
     * @param {string} key The name of your primary key.
     * @param {string} value The value it must match to be udpated.
     * @returns {updateBuilder} this
     */
    setKey(key, value) {
        this.params.Key = {
            key: value
        };
        return this;
    }
    /**
     * 
     * @param {string} key Name of a key in the item to be updated.
     * @param {*} value The updated value of that key.
     * @returns {updateBuilder} this
     */
    updateItem(key, value) {
        if (!this.params.ExpressionAttributeValues) {
            this.params.ExpressionAttributeValues = {};
        }
        if (reservedWords.set[key.toUpperCase()]) {
            if (!this.params.ExpressionAttributeNames) {
                this.params.ExpressionAttributeNames = {};
            }
            this.params.ExpressionAttributeNames["#" + key.substring(0, 3)] = key;
            this.params.ExpressionAttributeValues[":" + key.substring(0, 3)] = attr.wrap1(value);
            if (this.params.UpdateExpression == "") {
                this.params.UpdateExpression = "set #" + key.substring(0, 3) + " = :" + key.substring(0, 3);
            } else {
                this.params.UpdateExpression += ", #" + key.substring(0, 3) + " = :" + key.substring(0, 3);
            }
        } else {
            this.params.ExpressionAttributeValues[":" + key] = attr.wrap1(value);
            if (this.params.UpdateExpression == "") {
                this.params.UpdateExpression = "set " + key + " = :" + key;
            } else {
                this.params.UpdateExpression += ", " + key + " = :" + key;
            }
        }
        return this;
    }
    /**
     * @returns {Promise.<Array, Error>} Returns an Promise containing an array of items updated. 
     */
    execute() { 
        var db = this._db;
        var params = this.params;
        var items = [];
        return new Promise(function (resolve, reject) {
            var onScan = function (err, data) {
                if (err) {
                    reject(err);
                    return;
                }
                items.push.apply(items, data.Items);
                if (typeof data.LastEvaluatedKey != "undefined") {
                    params.ExclusiveStartKey = data.LastEvaluatedKey;
                    db.scan(params, onScan);
                } else {
                    resolve(items);
                }
            };
            db.scan(params, onScan);
        });
    }
}
/**
 * Main ddb class to bind to a AWS Account.
 * @class
 */
class ddb {
    constructor(opts = {}) {
        try {
            if (opts.profile) {
                var credentials = new aws.SharedIniFileCredentials({profile: opts.profile});
                aws.config.credentials = credentials;
            }   
            this._db = new aws.DynamoDB({
                accessKeyId: opts.accessKeyId || process.env.AWS_ACCESS_KEY_ID,
                secretAccessKey: opts.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY,
                region: opts.region || "us-east-1"
            });
        } catch (e) {
            throw new Error(e);
        }
    }
    /**
     * Sets up a scan builder for a table.
     * @param {string} TableName 
     */
    scan(TableName) {
        return new scanBuilder(TableName, this._db);
    }
    /**
     * Sets up a update builder for a table.
     * @param {string} TableName 
     */
    update(TableName) {
        return new updateBuilder(TableName, this._db);
    }
}
exports = module.exports = ddb;