declare module Peerius {
    let Peerius: any;
}

module insite.catalog {
    "use strict";

    interface ProductRecommendationModel {
        type?: string;
        lang: string;
        channel: string;
        user: {};
        recContent: "full" | "refCodeOnly";
        customAttributes: { locationId: string };
        customer: { customerId?: string; segmentIds?: string[] }
    }

    interface ProductRecommendationHomeModel extends ProductRecommendationModel {
    }

    interface ProductRecommendationCategoryModel extends ProductRecommendationModel {
        category: string;
    }

    interface ProductRecommendationProudctModel extends ProductRecommendationModel {
        product: { refCode: string };
    }

    interface ProductRecommendationBasketItemsModel {
        items: any[];
        currency: string;
        basketId: string;
    }

    interface ProductRecommendationBasketModel extends ProductRecommendationModel {
        basket: ProductRecommendationBasketItemsModel;
    }

    interface ProductRecommendationOrderModel extends ProductRecommendationModel {
        order: {
            orderNo: string,
            items: any[],
            currency: string,
            subtotal: number,
            shipping: number,
            total: number,
        };
    }

    interface ProductRecommendationWishlistModel extends ProductRecommendationModel {
        wishList: {
            items: any[],
        };
    }

    interface ProductRecommendationCheckoutModel extends ProductRecommendationModel {
        checkout: {
            items: any[],
            currency: string,
            subtotal: number,
            shipping: number,
            total: number
        };
    }

    interface ProductRecommendationSearchResultsModel extends ProductRecommendationModel {
        searchResults: {
            term: string,
            totalNumberOfResults: number,
            pageNumber: number,
            resultsOnPage: number,
            filters: any[],
            topResults: any[],
        };
    }

    interface ProductRecommendationBrandModel extends ProductRecommendationModel {
        brand: string
    }

    export class ProductRecommendationsService {
        session: SessionModel;
        settingsCollection: core.SettingsCollection;
        lang: string;
        currency: string;
        pages: string[] = [
            '/Catalog',
            '/Product',
            '/Cart',
            '/Checkout/ReviewAndPay',
            '/OrderConfirmation',
            '/search',
            '/MyLists/Details',
        ];

        static $inject = ["$q", "$rootScope", "$window"];

        constructor(
            protected $q: ng.IQService,
            protected $rootScope: ng.IRootScopeService,
            protected $window: ng.IWindowService,) {
            this.$rootScope.$on("settingsLoaded", (_, settingsCollection: core.SettingsCollection) => {
                this.settingsCollection = settingsCollection;

                if (!this.enableProductRecommendations) {
                    return;
                }

                const PeeriusCallbacks = {
                    track: { site: this.siteName },
                    smartRecs: (data: []) => {
                        this.print("Recommended Products: ", data);
                        this.triggerWidgetUpdateEvent(data);
                    },
                    info: function (jsonData) {
                    },
                    apiVersion: this.apiVersion
                };

                this.$window.PeeriusCallbacks = PeeriusCallbacks;
                if (typeof this.$window.isPeeriusLoaded === 'undefined') {
                    this.$window.isPeeriusLoaded = false;
                }
            });
            this.$rootScope.$on("sessionLoaded", (_, session: SessionModel) => {
                this.session = session;
                this.lang = this.session.language.languageCode;
                this.currency = this.session.currency.currencyCode;
            });
        }

        protected waitForData() {
            const defer = this.$q.defer();

            const i = setInterval(() => {
                if (this.settingsCollection && this.session) {
                    clearInterval(i);
                    defer.resolve();
                }
            }, 100);

            return defer.promise;
        }

        public print(message: any, ...optionalParams: any): void {
            if (this.enablePeeriusRequestsConsoleLogs) {
                console.log(`Optimizely - ${message}`, ...optionalParams)
            }
        }

        public printError(message: any, ...optionalParams: any): void {
            if (this.enablePeeriusRequestsConsoleLogs) {
                console.error(`Optimizely - ${message}`, ...optionalParams)
            }
        }

        private generateGeneralPrepare(): ProductRecommendationModel {
            let locationId = this.session?.shipTo?.customerNumber || "";
            let customerId = this.session?.billTo?.customerNumber || "";
            let segmentIds = this.session?.["personas"]?.map(persona => persona.name) || [];

            return {
                lang: this.lang,
                channel: "web",
                user: {
                    id: this.session.userName || ""
                },
                recContent: "refCodeOnly",
                customAttributes: {
                    locationId,
                },
                customer: {
                    customerId,
                    segmentIds,
                }
            };
        }

        public prepareProduct(productModel: ProductModel) {
            this.waitForData().then((result: any) => {
                const epiProductModel: ProductRecommendationProudctModel = {
                    type: 'product',
                    product: {
                        refCode: productModel.product.erpNumber
                    },
                    ...this.generateGeneralPrepare(),
                }
                this.prepareTrack(epiProductModel);
            });
        }

        public prepareSearchResultPage(products: ProductCollectionModel) {
            this.waitForData().then((result: any) => {
                const filters = products.attributeTypeFacets.map((item, index) => {
                    return {
                        filter: item.name,
                        operator: "IN",
                        values: item.attributeValueFacets
                            .filter(filterValue => filterValue.selected)
                            .map(filterValue => filterValue.value),
                    }
                });

                const topResultsItems = products.products.slice(0, 3).map(item => {
                    return { "refCode": item.id }
                });

                const epiSearchResultsModel: ProductRecommendationSearchResultsModel = {
                    type: 'searchresults',
                    searchResults: {
                        term: products.originalQuery,
                        totalNumberOfResults: products.pagination.totalItemCount,
                        pageNumber: products.pagination.currentPage,
                        resultsOnPage: products.pagination.pageSize,
                        filters,
                        topResults: topResultsItems
                    },
                    ...this.generateGeneralPrepare(),
                }

                this.prepareTrack(epiSearchResultsModel);
            })
        }

        public prepareCategory(catalogPage: CatalogPageModel) {
            this.waitForData().then((result: any) => {
                let category = "";
                catalogPage.breadCrumbs.forEach((item, index) => {
                    // We don't need to add "home" to category path
                    if (item.text.toLowerCase() === 'home')
                        return;

                    category += item.text;
                    if (index != (catalogPage.breadCrumbs.length - 1))
                        category += ">";
                });

                const epiCategoryModel: ProductRecommendationCategoryModel = {
                    type: 'category',
                    category,
                    ...this.generateGeneralPrepare(),
                }
                this.prepareTrack(epiCategoryModel);
            });
        }

        public prepareBasket(cart: CartModel) {
            this.waitForData().then((result: any) => {
                /**
                 * for min-order qty
                const cartItems = cart.cartLines.map(item => {
                    return {
                        refCode: item.erpNumber,
                        qty: item.qtyOrdered / parseInt(item.properties['minOrderQty']),
                        price: this.roundeNumber(item.pricing.actualPrice * parseInt(item.properties['minOrderQty'])),
                    }
                });
                **/
                // without min-order qty
                const cartItems = cart.cartLines.map(item => {
                    return {
                        refCode: item.erpNumber,
                        qty: item.qtyOrdered,
                        price: item.pricing.actualPrice,
                    }
                });

                const epiBasketModel: ProductRecommendationBasketModel = {
                    type: "basket",
                    basket: {
                        items: cartItems,
                        currency: this.currency,
                        basketId: cart.trackId,
                    },
                    ...this.generateGeneralPrepare(),
                };

                this.prepareTrack(epiBasketModel);
            });
        }

        public prepareOrder(orderHistory: OrderModel, cart: CartModel) {
            this.waitForData().then((result: any) => {
                const cartItems = cart.cartLines.map(item => {
                    return {
                        refCode: item.erpNumber,
                        qty: item.qtyOrdered,
                        price: item.pricing.actualPrice,
                    }
                });

                const epiOrderModel: ProductRecommendationOrderModel = {
                    type: 'order',
                    order: {
                        orderNo: orderHistory.erpOrderNumber || orderHistory.webOrderNumber,
                        currency: this.currency,
                        shipping: orderHistory.shippingCharges,
                        total: orderHistory.orderTotal,
                        subtotal: orderHistory.orderSubTotal,
                        items: cartItems
                    },
                    ...this.generateGeneralPrepare(),
                };

                this.prepareTrack(epiOrderModel);
            })
        }

        public prepareWishlist(wishListLineCollection: WishListLineCollectionModel) {
            this.waitForData().then((result: any) => {
                const wishlistItems = wishListLineCollection.wishListLines.map(item => {
                    return {
                        refCode: item.erpNumber,
                    }
                });

                const epiWishlistModel: ProductRecommendationWishlistModel = {
                    type: 'wishlist',
                    wishList: {
                        items: wishlistItems
                    },
                    ...this.generateGeneralPrepare(),
                }
                this.prepareTrack(epiWishlistModel);
            })
        }

        public prepareCheckout(cart: CartModel) {
            this.waitForData().then((result: any) => {
                /**
                 * for min-order qty
                const cartItems = cart.cartLines.map(item => {
                    return {
                        refCode: item.erpNumber,
                        qty: item.qtyOrdered / parseInt(item.properties['minOrderQty']),
                        price: this.roundeNumber(item.pricing.actualPrice * parseInt(item.properties['minOrderQty'])),
                    }
                });
                */
                // for min-order qty
                const cartItems = cart.cartLines.map(item => {
                    return {
                        refCode: item.erpNumber,
                        qty: item.qtyOrdered,
                        price: item.pricing.actualPrice,
                    }
                });

                const epiCheckoutModel: ProductRecommendationCheckoutModel = {
                    type: 'checkout',
                    checkout: {
                        currency: this.currency,
                        shipping: cart.shippingAndHandling,
                        total: cart.orderGrandTotal,
                        subtotal: cart.orderSubTotal,
                        items: cartItems
                    },
                    ...this.generateGeneralPrepare(),
                }
                this.prepareTrack(epiCheckoutModel);
            });
        }

        public prepareBrand(products: ProductCollectionModel) {
            this.waitForData().then((result: any) => {
                const epiBrandModel: ProductRecommendationBrandModel = {
                    type: "brand",
                    brand: products.brandFacets.filter(item => item.selected).map(e => e.name)[0] || "",
                    ...this.generateGeneralPrepare(),
                }
                this.prepareTrack(epiBrandModel);
            });
        }

        prepareHome() {
            this.waitForData().then((result: any) => {
                const epiOtherModel: ProductRecommendationHomeModel = {
                    type: "home",
                    ...this.generateGeneralPrepare(),
                }
                this.prepareTrack(epiOtherModel);
            });
        }

        prepareOther(url: string) {
            if (!this.isOtherPage(url))
                return;

            this.waitForData().then((result: any) => {
                const epiOtherModel: ProductRecommendationModel = {
                    type: "other",
                    ...this.generateGeneralPrepare(),
                }
                this.prepareTrack(epiOtherModel)
            })
        }

        private isOtherPage(url: string) {
            return !this.pages.some((item) => url.toLowerCase().indexOf(item.toLowerCase()) > -1);
        }

        private prepareTrack(prepareTrackData: ProductRecommendationModel) {
            if (!this.enableProductRecommendations) {
                return;
            }

            const track = { ...prepareTrackData, site: this.siteName };
            this.print("prepareTrack: ", track);

            this.$window.PeeriusCallbacks.track = track;
            const json = encodeURIComponent(JSON.stringify(this.$window.PeeriusCallbacks.track));

            if (!this.$window.isPeeriusLoaded) {
                $.getScript(this.peeriusTrackingUrl, function (data, textStatus, jqxhr) {
                    (window as any).isPeeriusLoaded = true;
                });
            } else {
                (Peerius as any).sendAjax("rest.pagex?jd=" + json, this.$window.PeeriusCallbacks.apiVersion);
            }
        }

        triggerWidgetUpdateEvent = (response) => {
            this.$rootScope.$broadcast("productRecommendationsLoaded", response);
        }

        public sendImpressionTracking(recommendationIds: any[]) {
            this.print("Impression Tracking: ", recommendationIds);
            (Peerius as any).sendAjax(`${this.siteName}/rec/shown.pagex?ids=${recommendationIds.join(',')}`)
        }

        public sendProductRecommendationTrack(recommendationId: string, isNewPageWasOpen?: boolean) {
            this.print("Product Recommendation Trackind - Id: ", recommendationId);
            if (isNewPageWasOpen) {
                (Peerius as any).smartRecsClick(recommendationId);
            } else {
                (Peerius as any).smartRecsSendClick(recommendationId);
            }
        }

        get enableProductRecommendations(): boolean {
            return this.settingsCollection.productSettings.enableProductRecommendations;
        }

        get enablePeeriusRequestsConsoleLogs(): boolean {
            return this.settingsCollection.productSettings.enablePeeriusRequestsConsoleLogs;
        }

        get apiVersion(): string {
            return this.settingsCollection.productSettings.peeriusApiVersion;
        }

        get siteName(): string {
            return this.settingsCollection.productSettings.peeriusSiteName;
        }

        get peeriusTrackingUrl(): string {
            return this.settingsCollection.productSettings.peeriusTrackingUrl;
        }

        private roundeNumber = (number, digits = 4) => {
            return parseFloat((number).toFixed(digits));
        }
    }

    angular
        .module("insite")
        .service("productRecommendationsService", ProductRecommendationsService);
}