// Core imports
import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core'
import { ActivatedRoute } from '@angular/router'
import { isPlatformBrowser } from '@angular/common'

// Services
import { SendCartService } from '../../services/svc.send-cart'
import { DiscountsService } from '@k-core/common/discount/services/svc.discounts'
import { BasketService }    from '@k-services/svc.basket'

// Helpers
import { AppHelper } from '@k-settings/app-helper'

// RxJS
import { Subscription, BehaviorSubject } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators'
import { forkJoin } from 'rxjs/observable/forkJoin'
import { LocalstorageService } from '@k-core/services/general/storage'


/**
 * @name SummaryComponent
 * @description Creates a summary listing for the send-cart checkout
 * @author Kasper Hansen
 * @since 07-01-2019
 * ------------------------------------
 * @author Kasper Hansen
 * @since 03-10-2019
 * @changelog change subtotal, total and tax to observables, rather than
 * inlined functions, to make up for response times on different requests.
 * Cleanup in console logs and variables, as there was much stuff that had
 * been implemented for very specific debugging and not been removed over
 * time.
 */
@Component({
    moduleId: module.id+ '',
    selector: 'send-cart-summary',
    templateUrl: './template/t--summary.pug',
    styleUrls: ['sty.summary.scss']
})


export class SummaryComponent {

    // ---- Variables ---- \\
    cart: any
    subtotal: number = 0
    discounts: number = 0
    shipping: number = 0
    total: number = 0


    // Basket variables
    basketItems: any[] = []
    basketItemsInterval: any


    // Discounts variables
    discountsMap: any[] = []

    
    // Observables
    subscriptions = new Subscription

    subtotalSource = new BehaviorSubject<Number>(0)
    subtotal$ = this.subtotalSource.asObservable()

    totalSource = new BehaviorSubject<Number>(0)
    total$ = this.totalSource.asObservable()

    taxSource = new BehaviorSubject<Number>(0)
    tax$ = this.taxSource.asObservable()

    constructor(
        @Inject(PLATFORM_ID) private _platformId,
        public localStorage: LocalstorageService,
        private _sendCart: SendCartService,
        private _discount: DiscountsService,
        private _basketService: BasketService,
        private _activatedRoute: ActivatedRoute
    ) {

        this.subscriptions.add(
            
            this.subtotal$
                .pipe(
                    distinctUntilChanged()
                )
                .subscribe(() => {
                    this.returnTotals()
                })    
        )
            
        // Only register changes, if the cart actually changes
        this.subscriptions.add(

            _sendCart.cart$
                .subscribe((response: any) => {

                    this.cart = response

                    if(isPlatformBrowser(this._platformId)) {


                        this.generateStuff(false).then(() => {
                            // console.log('subscription generation done')
                        })
                    }
            })
        )

        this.subscriptions.add(

            _discount.discount$
                .pipe(
                    distinctUntilChanged()
                )
                .subscribe({
                    next: (value) => {

                        this.discounts = value
                        this.returnTotals()
                    }
                })
        )
    }



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

        this.generateStuff(true).then(() => {
            // console.log('done awaiting...')
        })
    }

    ngOnDestroy() {
        this.subscriptions.unsubscribe()
    }


    // ---- Functions ---- \\
    async generateStuff(init) {

        this.discountsMap = []

        if(init) {

            let renderItemsRun = await this._sendCart.renderItems()
            let setObjectRun = await this._basketService.setObject(this._basketService.getBasketProducts(true))
            let calculateSubtotalRun = await this.calculateSubtotal()
        }

        let calculateTotalDiscountsRun = await this.calculateTotalDiscounts()
    }


    requestBasketItems(result) {
        return new Promise((resolve) => {

            this.basketItems = []

            for(let item of result) {

                this._basketService.getBasketItem(item).then((response) => {

                    if(this.basketItems.find((item) => item.id == response.id)) {

                    } else {
                        this.basketItems.push(response)
                    }
                })
            }


            let basketItemsInterval = setInterval(() => {

                if(this.basketItems.length === result.length) {

                    clearInterval(basketItemsInterval)
                    resolve(true)
                }
            }, 200)

        })
    }


    /**
     * Calculates subtotal by adding all products together
     */
    async calculateSubtotal() {

        this.subtotal = 0
        let subtotal = 0


        for await(let product of this.cart.products) {
            subtotal += (product.price * product.quantity)
        }


        this.subtotal = await subtotal

        this._discount.setTotal(this.subtotal)
        this.subtotalSource.next(this.subtotal)

        return await this.subtotal
    }


    /**
     * Calculates the totals of `discounts` and `individualDiscounts`
     *
     * @returns {number} `this.discounts`
     */
    calculateTotalDiscounts() {


        let debug: boolean = false

        // Subscribe for looking after the debug parameter flag, with a discount value.
        this._activatedRoute.queryParams.take(1).subscribe((param) => {
            if(!!param.debug && param.debug === 'discount') {
                debug = true
            }
        })

        // this.discounts = 0

        return this.executeCalculateDiscounts().then((result) => {

            this.discounts = result

            this.returnTotals()
        })

    }

    /**
     * Executes asyncroneous requests in sync order
     */
    async executeCalculateDiscounts() {

        let localStorageItemsRun = await this._getLocalstorage()
        let elements = await this._isolateBasket()
        let missingDiscountsRun = await this._handleMissingDiscounts(localStorageItemsRun)

        // Post all products and discounts, but remove duplicates
        let discountsRequest = await this._discount.request(Array.from(new Set(elements)))

        return discountsRequest
    }

    /**
     * Isolate basket values into a element array
     */
    private async _isolateBasket() {

        let elements = await [
            ...this.cart.products,
            ...this.cart.discounts,
            ...this.cart.misc,
            ...this.cart.individualDiscounts
        ]

        return elements
    }


    /**
     * Isolates and adds missing discounts, before calculating, required for coupons to appear, as they're added to cart
     */
    private async _handleMissingDiscounts(basket) {

        for await(let discount of this.cart.discounts) {

            // Declares if the discount is found within the basket
            let doesDiscountExist = basket.find(product => {

                let discountId = discount.id
                let productId = product.id


                // If DI_hash exists, skip the DI_hash and check against the final part of the string
                if(discountId.includes('DI_')) {
                    discountId = discountId.split('_')
                    discountId = discountId[discountId.length - 1]
                    productId = productId.split('--')[1]
                }

                // Checks the identifier against each other, i.e. -cu--mix10--hash, it will find mix10 and compare
                if(!!discountId && discountId.includes('-cu--')) {
                    productId = productId.split('--')[1]
                    discountId = discountId.split('--')[1]
                }

                if(productId == discountId) {
                    this.discountsMap.push(discountId)

                    return discountId
                } else {
                    return false
                }
            })

            // if element doesn't exist on localStorage, add it.
            if(!doesDiscountExist && !!basket) {

                this.basketItems.push(discount)

                let localStorageCart = JSON.parse(this.localStorage.getItem(AppHelper.basketIdentifier))

                localStorageCart.push(discount)

                const result = [];
                const map = new Map();
                for (const item of localStorageCart) {

                    let itemId = (item.id.includes('DI_') ? item.id.replace('DI_', '') : item.id)

                    if(!map.has(itemId)) {
                        map.set(itemId, true);    // ensure the mapped data is correct and unique
                        result.push(item);
                    }
                }

                this.localStorage.setItem(AppHelper.basketIdentifier, JSON.stringify(result))
            }
        }
    }
    /**
     * Gets the localstorage asyncronecously
     */
    private async _getLocalstorage() {

        // Get the localStorage item
        let localstorageItemRun = await JSON.parse(this.localStorage.getItem(AppHelper.basketIdentifier))

        return localstorageItemRun
    }


    /**
     * Handshakes `cost` to `shipping` and returns the value
     *
     * @param cost
     */
    calculateShipping(cost) {
        this.shipping = cost

        this.returnTotals()

        return this.shipping
    }


    /**
     * Calculates total by adding subtotal and shipping together, then removing discounts
     */
    returnTotals() {
        this.total = this.subtotal + this.shipping - this.discounts

        this.calculateTax()
        // console.log('Settings total', [this.subtotal, this.shipping, this.discounts, this.subtotal + this.shipping - this.discounts])


        this.totalSource.next(this.subtotal + this.shipping - this.discounts)
    }


    /**
     * TODO: Get tax from backend
     */
    calculateTax() {
        let tax = 20

        this.taxSource.next((this.total / 100) * tax)
    }
}