// NG Imports
import { Injectable, Inject, OnDestroy, PLATFORM_ID } from '@angular/core'
import { isPlatformBrowser } from '@angular/common'
import { HttpClient } from '@angular/common/http'

// Libs
import { BehaviorSubject } from 'rxjs/BehaviorSubject'

// Services
import { HelperService } from '@k-services/svc.helper'
import { BasketService } from '@k-services/svc.basket'
import { DiscountsService } from '@k-common/discount/services/svc.discounts'

// Settings
import { store } from '@k-settings/app-setup'
import { currency } from '@k-settings/store-helper'

// Interfaces
import { ISubscription } from 'rxjs/Subscription'
import { ActivatedRoute } from '@angular/router';
import { LocalstorageService } from '../storage'

interface IEcommerceItem {
	id: string;
	name: string;
	sku: string;
	category: string;
	price: string;
	quantity: string;
}

interface IDataLayerObject {
	event: string;
	transactionId: number;
	transactionTotal:number;
	transactionCurrency: string;
	transactionProducts: IDataLayerProducts[];
	transactionAffiliation?: string;
}

interface IDataLayerProducts {
		sku: number;
		name: string;
		category: string;
		price: number;
		quantity: number;
}

interface IDataLayerObject {
	event: string;
	transactionId: number;
	transactionTotal:number;
	transactionCurrency: string;
	transactionProducts: IDataLayerProducts[];
	transactionAffiliation?: string;
}

interface IDataLayerProducts {
	sku: number;
	name: string;
	category: string;
	price: number;
	quantity: number;
}

// Declarations
declare global {
	interface Window {
		ga: any;
	}
}







@Injectable()
export class EcommerceTrackingService {


	// ---- Variables ---- \\
	attributeTranslations = new Map
	transactionSent: boolean = false

	// Observables
	hasAnalyticsSource = new BehaviorSubject<boolean>(false)
	$hasAnalytics = this.hasAnalyticsSource.asObservable()

	// Subscriptions
	hasAnalyticsSubscription: ISubscription
	attributeSubscription: ISubscription

	// Debugging
	debug: boolean = true
	



	

	constructor(
		@Inject(PLATFORM_ID) public platformId: Object,
		public localStorage: LocalstorageService,
		private _http: HttpClient,
		private _activatedRoute: ActivatedRoute,
		private _helper: HelperService,
		private _basket: BasketService,
		private _discount: DiscountsService
	) { 
		// Get ecommerce script at init
		if(isPlatformBrowser(this.platformId)) {
			
			// Check for analytics and set state to true if found
			let smrt = setInterval(() => {
				
				if(!!window.ga) {
					console.log('should have analytics: ', window.ga)
					window.ga('require', 'ecommerce')
					this.hasAnalyticsSource.next(true)

					clearInterval(smrt)
				}
				
			}, 100)


			// Set current attribute map
			this.attributeTranslations = _basket.attribute

			// Subscribe to attribute name updates
			this.attributeSubscription = _basket.attribute$.subscribe(result => {
				this.attributeTranslations = result
			})

		}
	}






	// ---- Lifecycle hooks ---- \\


	ngOnDestroy() {
		if(this.hasAnalyticsSubscription)
			this.hasAnalyticsSubscription.unsubscribe()

		if(this.attributeSubscription)
			this.attributeSubscription.unsubscribe()
	}








	// ---- Functions ---- \\




	// ---- Public

	
	/**
	 * Sends a request to get the shipping price. 
	 * Then iterate over products and calculates a total (excluding shipping). 
	 * A transaction is then created and products are added.
	 * Finally the data is sent to analytics ecommerce tracking.
	 * 
	 * ! NOT USED
	 * 
	 * @param products 
	 * @param transactionId 
	 */
	public addTransactionAndItems(products, transactionId)  {
		this.transactionSent = false
		let items = []

		this._http.get(this._helper.server + '/feed/get/shipment-data' + this._helper.credentials + '&orderId=' + transactionId).toPromise().then((response: any) => {

			let shippingPrice = parseFloat(response.data.shippingPrice)
			let shippingMethod = response.data.shippingMethod

			// Return shipping so we can use the shipping price in transaction
			return {'method': shippingMethod, 'price': shippingPrice}
		}).then((shipping) => {

			// Only continue if analytics is available
			this.hasAnalyticsSubscription = this.$hasAnalytics.subscribe(() => {

				if(!this.transactionSent) {
					new Promise((resolve) => {
						let index = 0
						let total: number = 0
		
						// Iterate over products, add to items, and add to total
						for(let product of products) {
							console.log('iterating product: ', product)
	
							this._basket.getBasketItem(product).then((response) => {
	
								console.log('pushing product into items array: ', response)
								if(!!response) {
	
									items.push({
										'id': transactionId,
										'sku': response.sku ? response.sku : 'n/a',
										'name': this.attributeTranslations.has(response.type_id) ? this.attributeTranslations.get(response.type_id) : response.type_id,
										'category': response.type ? response.type : 'n/a',
										'price': parseFloat(response.price),
										'quantity': parseFloat(product.quantity)
									})
			
									total += (parseFloat(response.price) * parseFloat(product.quantity))
								}
	
	
								// Resolve once all products have been added. Failsafe for no products
								index++
								if(index >= products.length && items.length > 0) {
									resolve({'id': transactionId, 'total': total, shipping: shipping})
								}
							})
						}
		
					}).then((transaction: any) => {
						
						// Add transaction to add items to
						this._addTransaction(transaction.id, transaction.total, transaction.shipping.price)
					}).then(() => {
		
						// Add items to transaction
						for(let item of items) {
							this._addItem(item)
						}
					}).finally(() => {
						// Send transaction and clear
						this._sendData()
					})


					// Stop duplicate transactions
					this.transactionSent = true
				}
			})
		})

	}




	/**
     * Adds a DataLayer, from Analytics basic eCommerce tracking
     * Uses `orderId` from `queryParams` to set transaction, then populates
     * the rest of the values through `this.basket`
     * Be aware, this only pushes dataLayer if `products` exist
	 * 
	 * TODO: This has become a mess, fix it, can be debugged by adding an &debug=true flag on localhost
     * 
     * @todo Fix `dataLayer` not existing on `window` type
     * @param products `this.basket` as `Object`
     * @param transactionId `queryParam.params['orderId']` response
     */
    addDataLayer(products, transactionId): void {

        this._addProductsToDataLayer(products).then((data: any) => {
			
            let dataLayer = window.dataLayer || []

			// prepare datalayer object
			let dataLayerObject: IDataLayerObject = {
                'event': 'custom.transaction',
                'transactionId': transactionId,
                'transactionTotal': data.total,
                'transactionCurrency': currency,
                'transactionProducts': data.products,
			}
			
			if(isPlatformBrowser(this.platformId)) {

				if(this.localStorage.getItem('deco-bus')) {
					dataLayerObject.transactionAffiliation = this.localStorage.getItem('deco-bus')
				}
			}

			if(this.debug) console.log('addDataLayer --- dataLayerPush', dataLayerObject)


			// push object to dataLayer
            dataLayer.push(dataLayerObject)
        })


    }
	





	

	// ---- Private



	/**
	 * Adds products to dataLayerProducts array, requests discounts and returns products array and total
	 * 
	 * @param products 
	 */
	private _addProductsToDataLayer(products): Promise<any> {
		
		return new Promise((resolve) => {
			let index = 0
			let productsToDatalayer = []
			let calctotal = 0
			let basketItems = [] // Used for discount


			// loop through products
            for(let product of products) {
	
				if(this.debug) console.log('addDataLayer --- product:', product)


				// Make sure not to add key or discount as product
				if(!product.id.includes('-key--') && !product.id.includes('-cu--') && !product.id.includes('-ir--')) {
					if(this.debug) console.log('addDataLayer --- Product for processing:', product)



					// Request product to get price
					this._basket.getBasketItem(product).then((response) => {

						if(this.debug) console.log('addDataLayer --- before price check')
						if(this.debug) console.log('addDataLayer --- response', response)


						// Adding item to basketItems
						basketItems.push(response)



						// If price is obscured, find it
						if(!response.price) {

							// Get DV product price
							let optionsPrice = response.options.find((option) => {
								if(option.code === 'price') {
									return option.value
								}
							})

							// Set the price, if option doesnt exist, make a different request to get price
							if(optionsPrice && optionsPrice.value) {
								response.price = optionsPrice.value

							} else {
								// Get SKU product price
								this._basket.getResultPrice(product.id.split('--')[1]).then((skuPrice) => {
									response.price = skuPrice
								})
							}
						}




						// Wait for price, before continuing
						let int = setInterval(() => {

							if(response.price) {

								let productsToDataLayerObject: IDataLayerProducts = {
									'sku': response.sku,
									'name': this.attributeTranslations.has(response.type_id) ? this.attributeTranslations.get(response.type_id) : response.type_id,
									'category': response.type ? response.type : 'n/a',
									'price': parseFloat(response.price),
									'quantity': parseFloat(product.quantity)
								}

								if(this.debug) console.log('addDataLayer --- before dataLayer push', productsToDataLayerObject)


								// Push product to datalayer and add price to total
								productsToDatalayer.push(productsToDataLayerObject)
								calctotal += (parseFloat(response.price) * parseFloat(product.quantity))



								// bump index		
								index++		


								if(this.debug) console.log('addDataLayer --- before products length check', index >= products.length)
								
								// Resolve once all products have been added
								if(index >= products.length) {

									if(this.debug) console.log('addDataLayer --- resolving?')


									// Get discount, and resolve
									this._discount.request(basketItems).then((res) => {
										let discount = !!res ? res : 0
										resolve({products: productsToDatalayer, total: calctotal - discount})		
									})
								}
								clearInterval(int)
							}

						}, 100)


				
					})
				}
				else {
					index++
				}

            }

        })
	}




	/**
	 * Adds a new transaction to the ecommerce shopping cart. 
	 * This transaction represents the entire transaction that occurs on your site.
	 * 
	 * ! NOT USED
	 * 
	 * @param id 
	 * @param total 
	 * @param shipping - defaults to 0
	 * @param tax - defaults to 0
	 */
	private _addTransaction(id: string, total: number, shipping_cost: number = 0, tax:number = 0) {
		if(isPlatformBrowser(this.platformId)) {

			let transaction = {
				'id': id,                     			// Transaction ID. Required.
				'affiliation': store,   				// Affiliation or store name.
				'revenue': total.toString(),            // Grand Total.
				'shipping': shipping_cost.toString(),   // Shipping.
				'tax': tax.toString()                   // Tax.
			}

			console.warn('GA - adding new transaction: ', transaction)
			window.ga('ecommerce:addTransaction', transaction)
		}
		else {
			// Error logging?
			console.warn('addTransaction() - could not add transaction since window is not available')
		}
	}


	/**
	 * Adds a new ecommerce item to be tracked.
	 * An item represents the individual products that were in the shopping cart
	 * 
	 * @param item 
	 */
	private _addItem(item: IEcommerceItem) {
		if(isPlatformBrowser(this.platformId)) {
			console.log('GA - adding new item: ', item)

			window.ga('ecommerce:addItem', item)
		}
		else {
			// Error logging?
			console.warn('addItem() - could not add item since window is not available')
		}
	}


	/**
	 * Sends transactions and clears transactions
	 */
	private _sendData() {
		if(isPlatformBrowser(this.platformId)) {
			window.ga('ecommerce:send')
		}
		else {
			// Error logging?
			console.warn('sendData() - could not send data since window is not available')
		}
	}
}