// NG imports
import { Component, OnInit, AfterViewInit, Inject, ApplicationRef, PLATFORM_ID } from '@angular/core'
import { HttpClient }   from '@angular/common/http'
import { Location, isPlatformBrowser } from '@angular/common'
import { Router, ActivatedRoute, Params }  from '@angular/router'

// Core Services
import { BasketService }            from '@k-services/svc.basket'

// Module services
import { EcommerceTrackingService }	from '@k-services/general/tracking'
import { SeoService }               from '@k-services/svc.seo'
import { HelperService }            from '@k-services/svc.helper'

// Common
import { DiscountsService } 		from '@k-common/discount/services/svc.discounts'

// Settings
import { AppHelper }                from '@k-settings/app-helper'
import { website }                  from '@k-settings/app-setup'
import { BehaviorSubject, Observable } from 'rxjs';
import { BasketHelperService } from '@k-core/modules/basket/services/svc.basket-helper'
import { LocalstorageService, SessionstorageService } from '@k-core/services/general/storage'
import { PlatoConfiguratorService } from '@k-core/services/svc.plato-configurator'
import { concatMap, tap } from 'rxjs/operators'
import { from } from 'rxjs/observable/from'


@Component({ 
	moduleId: module.id+ '',
	selector: 'success-page',
	templateUrl: './tpl.success-page.pug',
	styleUrls: ['./sty.success-page.scss']
})

export class SuccessPageView implements OnInit, AfterViewInit {


	// ---- Variables ---- \\
	// Data
	orderId: any
	products: any
	completedProducts: any
	checkLogin: any = undefined
	items: any
	discount: number = 0
	discountPercentage: number = 0
	discountItems = []
	discountTimeout: any
	displayDiscount = 0
	total: number = 0
	plainTotal: number = 0
	loadPriceChanges: boolean = false

	shippingPrice: number = 0
	shippingType: string = ''
	orderMap = new Map
	priceMap = new Map
	basketItems = []

	attributeOrderMap = new Map
	attributeTranslations = new Map
	attr = this._basket.attr

	// Booleans
	showError: boolean = false

	// Helpers
	website: string = website
	image_path = this._helper.mediaServer + 'i/type/'
	

	// Affixes
	attributesWithAffixes = [
		{ name: 'height',			affix: 'cm'},
		{ name: 'width',			affix: 'cm'},
		{ name: 'a',				affix: 'cm'},
		{ name: 'b',				affix: 'cm'},
		{ name: 'c',				affix: 'cm'},
		{ name: 'd',				affix: 'cm'},
		{ name: 'angle',			affix: '°'},
		{ name: 'mountHeights',		affix: 'cm'},
		{ name: 'mountPosition',	affix: 'cm'}
	]


	destroyItems: boolean = true

	discountSource = new BehaviorSubject(0)
	discount$ = this.discountSource.asObservable()


	constructor(
		@Inject(PLATFORM_ID) private platformId: Object,
		public localStorage: LocalstorageService,
		public sessionStorage: SessionstorageService,
		private _router: Router,
		private _route: ActivatedRoute,
		private _helper: HelperService,
		private _http: HttpClient,
		private _seo : SeoService,
		private _basket: BasketService,
		private _discount: DiscountsService,
		private _basketHelper: BasketHelperService,
		private _ecommerce: EcommerceTrackingService,
		private _pcs: PlatoConfiguratorService
	) {
		_route.queryParams.subscribe(params => {

			if(params['orderId'])
				this.orderId = params['orderId']
			else
				this._router.navigate(['/'])
		})

		_basket.attribute$.subscribe(result => {
			this.attributeTranslations = result
		})

		this.discount$.subscribe((result) => {
			this.loadDiscounts()
		})
	}


	// ---- Lifecycle hooks ---- \\
	
	ngOnInit() {

		// TODO: What is happening here? Could it be put into its own function?



		for(let order of this.attr) {
			this.attributeOrderMap.set(order.name, order.order)
		}


		// Set page title
		this._seo.setTitle('Success', false)



		// Make sure to only run on client
		if(isPlatformBrowser(this.platformId)) {

			this.checkLogin = this.localStorage.getItem('user-key')
			this.products = []

			from(this._basket.getBasketProducts())
				.pipe(
					concatMap((products) => this.checkProduct(products))
				)
				.subscribe({
					next: (product) => {
						this.products.push(product)
					}
				})


			// Get basket items from localstorage and calculate total. Then remove localstorage object.
			let localstorageItems = JSON.parse(this.localStorage.getItem(AppHelper.basketIdentifier))
			this.findItems(localstorageItems).then(() => {
				if(isPlatformBrowser(this.platformId)) {

					if(this.destroyItems) {

						this.localStorage.removeItem(AppHelper.basketIdentifier)
						this.localStorage.removeItem('kake-saved-checkout-values')
						this.sessionStorage.removeItem('storedProductType')
						this._basket.emptyBasket()
					}
				}
			})


			// this.requestDiscount(JSON.parse(this.localStorage.getItem(AppHelper.cookieName)))

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

		}
	}
	

	ngAfterViewInit() {

		if(isPlatformBrowser(this.platformId)) {			
			// Set Analytics data 
			this._ecommerce.addDataLayer(this.products, this.orderId)
		}

	}


	// ---- Functions ---- \\
	checkProduct(product) {
		return new Observable((observer) => {
			if(product.id.toLowerCase().startsWith('-plato')) {

				this._pcs.getVariant(product.data.guid).then((response: any) => {
					
					product.data.price = response.price
					observer.next(product)
				})
				
			} else {
				observer.next(product)
			}
		})
	}
	// Component init of isNaN
	isNaN(value) {
		return isNaN(value)
	}
	

	/**
	 * Sets the spinner flag, and loads the discounts from the service as a promise.
	 * Discount value is added to `this.discount`
	 */
    loadDiscounts() {

        this.loadPriceChanges = true

        new Promise((resolve) => {

            let items = []

            for(let product of this.basketItems) {

                if(product.id.includes('-sku')) {

                    let skuProduct = {...product, price: this.priceMap.get(product.id)}
                    items.push(skuProduct)
                    
                } else {
                    items.push(product)
                }

            }

            // Make sure all items are pushed before resolving
            let interval = setInterval(() => {
                if(items.length == this.basketItems.length) {

                    clearInterval(interval)
                    resolve(items)
                }
            }, 200)
        }).then((response) => {

            this._discount.request(response).then((response: number) => {
    
                this.discount = response
                this.loadPriceChanges = false
            })
        })
    }


	/**
	 * Will return the discounted total.
	 * Percentage is calcualted first then fixed discount is subtracted.
	 * 
	 * If the fixed discount results in a negative value, 0 is returned
	 * 
	 * @returns number
	 * TODO: Is this function and its variable still used? If not, it can be refactored out.
	 */
	calculateDiscount() {
		let discountedPrice = this.total

		if(!!this.discountPercentage) {
			// console.log('discount value: ' + (discountedPrice * (this.discountPercentage / 100)))
			discountedPrice = (discountedPrice - (discountedPrice * (this.discountPercentage / 100)))
		}

		if(this.discount > 0) {
			// console.log('regular discount value: ' + this.discount)
			discountedPrice = (discountedPrice - this.discount >= 0 ? discountedPrice - this.discount : 0)
		}

		this.displayDiscount = discountedPrice
	}


	/**
	 * Maps out items, modifying the PLATO summary, isolates prices and discounts.
	 * 
	 * @param items 
	 * @requires calculateTotals()
	 * @requires loadDiscounts()
	 */
	async findItems(items: Array<any>) {
		let i = 0

		if(!!items) {

			for (let item of items) {
				let iterator = i

				this._basket.getBasketItem(item).then(async (response) => {
				
					if(response.type == 'plato') {

						await this._pcs.getVariant(item.data.guid)
							.then((data: any) => {

								let p: any = data

								if(!!p.summary) {
			
									p.summary += `\r\nvariant Id: ${item.data.guid}\r\nItem Id: ${item.data.code}\r\n`

									// Finds each line, determines if it has a value, and adds it if it does
									const lines = (arr: string[], seperator: string = ':', blacklistedValues: string[] = [], blacklistedKeys: string[] = []): string => {
										
										let summary: string = ''
			
										// Remove upper casing from blacklist arrays
										blacklistedValues = blacklistedValues.map((value) => value.toLowerCase())
										blacklistedKeys = blacklistedKeys.map((key) => key.toLowerCase())
			
										for(let line of arr) {
										
											// Isolates value for DRY
											let key = line.split(seperator)[0]
											key = key ? key.trim() : undefined
			
											let value = line.split(seperator)[1]
											value = value ? value.trim() : undefined
			
											// Checks value exists, has a length and is not a blank space, as that seems to be return from PLATO
											if(!!key && !!value && !blacklistedValues.includes(value.toLowerCase()) && !blacklistedKeys.includes(key.toLowerCase())) {
												summary += `${line}<br />`
											}
										}
			
										return summary
									}
			
									// Executes the local function
									this._helper.siteData$.subscribe((siteInfo: any) => {
										response.summary = lines(p.summary.split('\r\n'), ':', siteInfo.extend.plato_blacklist.values, siteInfo.extend.plato_blacklist.keys)
									})
								}

								// response.summary = data.summary.replace(/\r\n/g, '<br />')
								this.priceMap.set(item.id, data.price)
							})


					} else if(response.type == 'SKU') {
	
						// Ensure calculateTotals is re-run once SKU products have their prices
						await new Promise((resolve) => {
							resolve(this.getSkuPrice(response.id))
						}).then(() => {
	
							this.calculateTotals(this.basketItems, null, null)

							this.discountSource.next(1)
						})
	
					} else {
						this.discountSource.next(1)
					}
	
					this.orderMap.set(response.id, iterator)
					this.basketItems.push(response)
					this.calculateTotals(this.basketItems, null, null)
				})
				
				i++
			}

			// Get the discounts once items have been found
            let interval = setInterval(() => {
        
                if(this.basketItems.length == items.length) {
                    
					// this.loadDiscounts()
					
					this.discountSource.next(1)
                    clearInterval(interval)
                }
            }, 500)
		}
		else {
			// display error message
			this.showError = true
		}
	}


	/**
	 * TODO:: Descibe
	 * @param code 
	 */
	getOrder(code) {

		if(this.attributeOrderMap.has(code))
			return this.attributeOrderMap.get(code)
		else
			return 99

	}


	/**
	 * TODO: Describe
	 * @param id 
	 */
	getSkuPrice(id) {
		return this._basket.getResultPrice(id.split('--')[1]).then((res) => {
			this.priceMap.set(id, res)
		})
	}


	/**
	 * TODO: Descibe
	 * @param id 
	 */
	getQuantity(id:number) {
		return this._basket.getQuantityInBasket(id)
	}


	/**
	 * Gets the price, if it exists within the option (custom configurations like misc product and individual discount functionalities)
	 * 
	 * @param options 
	 */
	getPrice(options) {

		let result = 0

		for(let option of options) {
			if(option.code == 'price') {
				result = option.value
			}
		}

		return result
	}


	/**
	 * Calculates total based on types and criteria
	 * 
	 * @param items     All `this.basketItems`
	 * @param id        Current product id, so code can see which product is getting a `quantity` change
	 * @param quantity  Current quantity change for `id`, this is required as value is fed before quantity change is applied
	 */
	calculateTotals(items, id, quantity) {

		let result = 0
		this.discount = 0
		let debugTotals = true

		for(let item of items) {

			if(item.id == id)
				item.quantity = quantity

			if(!!item.type) {

				switch(item.type.toLowerCase()) {
					case 'diverse':
						for(let option of item.options) {
	
							if(option.code == 'price') {
	
								if(option.value && item.quantity) {

									result += (option.value * item.quantity)
		
									if(debugTotals) {
										console.log('%cDIVERSE', 'color: DodgerBlue', 'CALCULATIONS')
										console.table({'price': option.value, 'quantity': item.quantity, 'total': option.value * item.quantity})
									}
								}
							}
						}     
	
						break;
	

					case 'sku':
					case 'plato':
						if(this.priceMap.get(item.id) && item.quantity) {

							result += this.priceMap.get(item.id) * item.quantity
	
							if(debugTotals) {
								console.log('%cSKU/SUNWAY', 'color: DodgerBlue', 'CALCULATIONS')
								console.table({'price': this.priceMap.get(item.id), 'quantity': item.quantity, 'total': this.priceMap.get(item.id) * item.quantity})
							}
						}

						break;
	
					default:
						if(item.price && item.quantity) {

							result += (item.price * item.quantity)

							if(debugTotals) {
								console.log('%cDEFAULT', 'color: DodgerBlue', 'CALCULATIONS')
								console.table({'price': item.price, 'quantity': item.quantity, 'total': item.price * item.quantity})
							}
						}
	
	
						break;
				}
			}

		}


		this.total = result
		this.plainTotal = this.total
		this._discount.total = this.total
	}


	/**
	 * Retruns attribute affix if attribute is set to have an affix
	 *
	 * @param attribute
	 */
	hasMeasureAffix(attribute: string) {
		let affix = null

		for(let attr of this.attributesWithAffixes) {
			if(attr.name === attribute) {
				affix = attr.affix
			}
		}

		return affix
	}

	getAttributeByCode(code: string, item: any): string {
        return item.options.find((attribute) => attribute.code == code).value
    }
}