/**
 * @module package/quiqqer/order/bin/frontend/classes/Basket
 * @author www.pcsg.de (Henning Leutz)
 *
 * @event onLoad [self]
 * @event onRemoveBegin [self]
 * @event onRemove [self]
 *
 * @event onAddBegin [self]
 * @event onAdd [self, Product]
 *
 * @event onClearBegin [self]
 * @event onClear [self]
 *
 * @event onRefreshBegin [self]
 * @event onRefresh [self]f
 */
define('package/quiqqer/order/bin/frontend/classes/Basket', [

    'qui/QUI',
    'qui/classes/DOM',
    'package/quiqqer/order/bin/frontend/Orders',
    'Ajax',
    'Locale'

], function (QUI, QUIDOM, Orders, QUIAjax, QUILocale) {
    "use strict";

    var lg          = 'quiqqer/order';
    var STORAGE_KEY = 'quiqqer-basket-products';

    return new Class({

        Extends: QUIDOM,
        Type   : 'package/quiqqer/order/bin/frontend/classes/Basket',

        Binds: [
            '$onProductChange'
        ],

        options: {
            mergeLocalStorage: 1 // 0 = use old basket, 1 = merge both, 2 = use new one
        },

        initialize: function (options) {
            this.parent(options);

            this.$products  = [];
            this.$basketId  = null;
            this.$orderHash = null;

            this.$isLoaded       = false;
            this.$mergeIsRunning = false;
            this.$saveDelay      = null;

            this.$calculations = {};

        },

        /**
         * Load the basket
         */
        load: function () {
            // basket from user
            if (QUIQQER_USER && QUIQQER_USER.id) {
                return this.$checkLocalBasketLoading().then(function () {
                    this.$isLoaded = true;
                    this.fireEvent('refresh', [this]);
                    this.fireEvent('load', [this]);
                }.bind(this)).catch(function (err) {
                    console.error(err);
                });
            }

            var self     = this,
                products = [],
                data     = QUI.Storage.get(STORAGE_KEY);

            this.$basketId = String.uniqueID();

            if (!data) {
                this.$isLoaded = true;
                return Promise.resolve();
            }

            this.fireEvent('refreshBegin', [this]);

            try {
                data = JSON.decode(data);

                if (!data) {
                    data = {};
                }
            } catch (e) {
                data = {};
            }

            if (typeof data.currentList !== 'undefined') {
                this.$basketId = data.currentList;
            }

            if (typeof data.products !== 'undefined' &&
                typeof data.products[this.$basketId] !== 'undefined') {
                products = data.products[this.$basketId];
            }

            var proms = [];

            for (var i = 0, len = products.length; i < len; i++) {
                proms.push(
                    this.addProduct(
                        products[i].id,
                        products[i].quantity,
                        products[i].fields
                    )
                );
            }

            return Promise.all(proms).then(function () {
                if (self.getAttribute('productsNotExists')) {
                    QUI.getMessageHandler().then(function (MH) {
                        MH.addError(
                            QUILocale.get(lg, 'message.products.removed')
                        );

                        self.getAttribute('productsNotExists', false);
                    });
                }

                self.$isLoaded = true;
                self.save().then(function () {
                    self.fireEvent('refresh', [self]);
                    self.fireEvent('load', [self]);
                });
            });
        },

        /**
         * Return the loading status of the basket
         * is the basket already loaded?
         *
         * @returns {boolean}
         */
        isLoaded: function () {
            return this.$isLoaded;
        },

        /**
         * Return the basket id
         *
         * @return {Number}
         */
        getId: function () {
            return this.$basketId;
        },

        /**
         * Return the order hash, if an order is active
         *
         * @return {null|*}
         */
        getHash: function () {
            return this.$orderHash;
        },

        /**
         * Return the quantity of the products in the current list
         *
         * @returns {Number}
         */
        getQuantity: function () {
            var quantity = 0;

            for (var i in this.$products) {
                if (this.$products.hasOwnProperty(i)) {
                    quantity = parseInt(quantity) + parseInt(this.$products[i].getQuantity());
                }
            }

            return quantity;
        },

        /**
         * Load the current basket of the user
         *
         * @return {Promise}
         */
        loadBasket: function () {
            var self = this;

            return self.getBasket().then(function (basket) {
                self.$calculations = basket;

                return self.$loadData(basket);
            });
        },

        /**
         * Data helper for the products
         *
         * @param data
         * @return {Promise}
         */
        $loadData: function (data) {
            if (data === null || typeof data.products === 'undefined') {
                return Promise.reject('Data is null');
            }

            this.$basketId = data.id;
            this.$products = [];

            if (typeof data.products === 'undefined') {
                return Promise.resolve();
            }

            if (!data.products.length) {
                return Promise.resolve();
            }

            var self     = this;
            var products = data.products;

            return new Promise(function (resolve) {
                require(['package/quiqqer/order/bin/frontend/classes/Product'], function (ProductCls) {
                    for (var i = 0, len = products.length; i < len; i++) {
                        self.$products.push(
                            new ProductCls(products[i])
                        );
                    }

                    resolve();
                });
            });
        },

        /**
         * check if the locale storage has to be considered and integrated into the basket
         */
        $checkLocalBasketLoading: function () {
            var self            = this;
            var storageProducts = [];

            try {
                var storageData = JSON.decode(
                    QUI.Storage.get(STORAGE_KEY)
                );

                var currentList = storageData.currentList;

                if (typeof storageData.products !== 'undefined' &&
                    typeof storageData.products[currentList] !== 'undefined') {
                    storageProducts = storageData.products[currentList];
                }
            } catch (e) {
                // nothing
            }

            return this.getBasket().then(function (basket) {
                if (!storageProducts.length) {
                    self.$calculations = basket;

                    return self.$loadData(basket);
                }

                /**
                 * consider local storage
                 */

                self.$basketId = basket.id;
                self.$products = [];

                var addStorageProducts = function () {
                    var proms = [];

                    for (var i = 0, len = storageProducts.length; i < len; i++) {
                        proms.push(
                            self.addProduct(
                                storageProducts[i].id,
                                storageProducts[i].quantity,
                                storageProducts[i].fields
                            )
                        );
                    }

                    return Promise.all(proms);
                };

                // clear the storage, otherwise the next time we have a double basket.
                QUI.Storage.set(STORAGE_KEY, JSON.encode({}));

                // 0 = use old basket
                if (self.getAttribute('mergeLocalStorage') === 0) {
                    return Promise.resolve();
                }

                // load old one
                self.$mergeIsRunning = true;

                // 2 = use new one
                if (self.getAttribute('mergeLocalStorage') === 2) {
                    return addStorageProducts().then(function () {
                        self.$mergeIsRunning = false;
                    });
                }

                // 1 = merge both
                return self.$loadData(basket).then(function () {
                    return addStorageProducts();
                }).then(function () {
                    self.$mergeIsRunning = false;
                });
            });
        },

        /**
         * Return the basket for the session user
         *
         * @return {Promise}
         */
        getBasket: function () {
            return new Promise(function (resolve) {
                QUIAjax.get('package_quiqqer_order_ajax_frontend_basket_getBasket', resolve, {
                    'package': 'quiqqer/order'
                });
            });
        },

        /**
         * Return the basket calculations (sum, subSum, prices)
         *
         * @return {{currencyData: {}, isEuVat: number, isNetto: number, nettoSubSum: number, nettoSum: number, subSum: number, sum: number, vatArray: {}, vatText: {}}}
         */
        getCalculations: function () {
            if ("calculations" in this.$calculations) {
                return this.$calculations.calculations;
            }

            return {
                currencyData: {},
                isEuVat     : 0,
                isNetto     : 0,
                nettoSubSum : 0,
                nettoSum    : 0,
                subSum      : 0,
                sum         : 0,
                vatArray    : {},
                vatText     : {}
            };
        },

        /**
         * Add a product to the basket
         *
         * @param {Number|Object} product
         * @param {Number} [quantity]
         * @param {Object} [fields]
         */
        addProduct: function (product, quantity, fields) {
            var self      = this;
            var productId = product;

            quantity = parseInt(quantity) || 1;
            fields   = fields || {};

            if (!quantity) {
                quantity = 1;
            }

            if (typeOf(product) === 'package/quiqqer/order/bin/frontend/classes/Product') {
                productId = parseInt(product.getId());
                fields    = product.getFields();
                quantity  = product.getQuantity();
            }

            return this.existsProduct(productId).then(function (available) {
                if (!available) {
                    self.setAttribute('productsNotExists', true);
                    return Promise.resolve();
                }

                self.fireEvent('refreshBegin', [this]);
                self.fireEvent('addBegin');

                return new Promise(function (resolve) {
                    require(['package/quiqqer/order/bin/frontend/classes/Product'], function (ProductCls) {
                        var Product = product;

                        if (typeOf(productId) === 'string') {
                            productId = parseInt(productId);
                        }

                        if (typeOf(productId) === 'number') {
                            Product = new ProductCls({
                                id    : productId,
                                events: {
                                    onChange: self.$onProductChange
                                }
                            });
                        }

                        // normal basket
                        Product.setQuantity(quantity).then(function () {
                            return Product.setFieldValues(fields);
                        }).then(function () {
                            self.$products.push(Product);
                            return self.save();
                        }).then(function () {
                            resolve();
                            self.fireEvent('refresh', [self]);
                            self.fireEvent('add', [self, Product]);
                        });
                    });
                });
            }).catch(function (err) {
                console.error(err);
            });
        },

        /**
         * Remove product pos
         *
         * @param pos
         * @return {Promise}
         */
        removeProductPos: function (pos) {
            var self  = this,
                index = pos - 1;

            return new Promise(function (resolve) {
                QUIAjax.post('package_quiqqer_order_ajax_frontend_basket_removePos', function () {
                    if (typeof self.$products[index] === 'undefined') {
                        return resolve();
                    }

                    self.fireEvent('refreshBegin', [self]);
                    self.fireEvent('removeBegin', [self]);

                    self.$products.splice(index, 1);
                    self.save().then(resolve);
                }, {
                    'package': 'quiqqer/order',
                    basketId : self.$basketId,
                    pos      : index
                });
            }).then(function () {
                self.fireEvent('remove', [self]);
                self.fireEvent('refresh', [self]);
            });
        },

        /**
         * Clears the basket
         *
         * @return {Promise}
         */
        clear: function () {
            var self = this;

            this.fireEvent('clearBegin', [this]);

            return new Promise(function (resolve) {
                QUIAjax.post('package_quiqqer_order_ajax_frontend_basket_clear', function (result) {
                    self.refresh().then(function () {
                        self.fireEvent('clear', [self]);
                        resolve(result);
                    });
                }, {
                    'package': 'quiqqer/order',
                    basketId : self.$basketId
                });
            });
        },

        /**
         * Refresh the basket data
         *
         * @return {Promise}
         */
        refresh: function () {
            var self = this;

            return this.loadBasket().then(function () {
                self.fireEvent('refresh', [self]);
            }).catch(function (err) {
                if (err !== 'Data is null') {
                    console.error(err);
                }
            });
        },

        /**
         * set quantity for
         *
         * @param pos
         * @param quantity
         * @return {*}
         */
        setQuantity: function (pos, quantity) {
            pos = pos - 1;

            if (typeof this.$products[pos] === 'undefined') {
                return Promise.resolve();
            }

            var self    = this,
                Product = this.$products[pos];

            return Product.setQuantity(quantity).then(function () {
                self.$products[pos] = Product;
                return self.save();
            });
        },

        /**
         * Return the basket products
         *
         * @return {Array}
         */
        getProducts: function () {
            return this.$products;
        },

        /**
         * Returns the number of products
         *
         * @return {Number}
         */
        count: function () {
            return this.getProducts().length;
        },

        /**
         * Saves the basket to the temporary order
         *
         * @param {Boolean} [force] - force the save delay, prevent homemade ddos
         * @return {Promise}
         */
        save: function (force) {
            if (!this.isLoaded() && this.$mergeIsRunning === false) {
                return Promise.resolve();
            }

            var self = this;

            force = force || false;

            return new Promise(function (resolve) {
                if (force === false) {
                    // save delay, prevent homemade ddos
                    if (this.$saveDelay) {
                        clearTimeout(this.$saveDelay);
                    }

                    this.$saveDelay = (function () {
                        this.save(true).then(resolve);
                    }).delay(100, this);

                    return;
                }

                if (!this.$basketId) {
                    resolve();
                    return;
                }

                var products = [];

                for (var i = 0, len = this.$products.length; i < len; i++) {
                    products.push(this.$products[i].getAttributes());
                }

                // no locale storage
                if (QUIQQER_USER && QUIQQER_USER.id) {
                    QUIAjax.post('package_quiqqer_order_ajax_frontend_basket_save', function (result) {
                        self.$calculations = result;
                        resolve(result);
                    }, {
                        'package': 'quiqqer/order',
                        basketId : this.$basketId,
                        products : JSON.encode(products)
                    });

                    return;
                }

                // locale storage
                var data = QUI.Storage.get(STORAGE_KEY);

                if (!data) {
                    data = {};
                } else {
                    data = JSON.decode(data);

                    if (!data) {
                        data = {};
                    }
                }

                if (typeof data.products === 'undefined') {
                    data.products = {};
                }

                data.currentList              = this.$basketId;
                data.products[this.$basketId] = products;

                QUI.Storage.set(STORAGE_KEY, JSON.encode(data));

                QUIAjax.post('package_quiqqer_order_ajax_frontend_basket_calc', function (result) {
                    self.$calculations = result;
                    resolve(result);
                }, {
                    'package': 'quiqqer/order',
                    products : JSON.encode(products)
                });
            }.bind(this));
        },

        /**
         * Exists the product, is it available?
         *
         * @param {String|Number} productId
         * @return {Promise}
         */
        existsProduct: function (productId) {
            return new Promise(function (resolve, reject) {
                QUIAjax.get('package_quiqqer_order_ajax_frontend_basket_existsProduct', resolve, {
                    'package': 'quiqqer/order',
                    productId: productId,
                    onError  : reject
                });
            });
        },

        /**
         * event : on change
         */
        $onProductChange: function () {
            this.fireEvent('refreshBegin', [this]);

            this.save().then(function () {
                this.fireEvent('refresh', [this]);
            }.bind(this));
        },

        /**
         *
         * @param {String} [orderHash]
         * @return {Promise}
         */
        toOrder: function (orderHash) {
            var self = this;

            if (typeof orderHash === 'undefined') {
                orderHash = '';
            }

            return new Promise(function (resolve, reject) {
                QUIAjax.get('package_quiqqer_order_ajax_frontend_basket_toOrderInProcess', resolve, {
                    'package': 'quiqqer/order',
                    orderHash: orderHash,
                    basketId : self.$basketId,
                    onError  : reject
                });
            });
        }
    });
});