import { Injectable, Inject, PLATFORM_ID } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { TransferState, makeStateKey } from '@angular/platform-browser'
import { isPlatformBrowser } from '@angular/common'
import { Router } from '@angular/router'
import { Subject } from 'rxjs/Subject'

// Services
import { HelperService } from '@k-services/svc.helper'
import { CacheService } from '@k-services/general/svc.cache'

// Helpers
import { AppHelper } from '@k-settings/app-helper'
import { BasketService } from '@k-services/svc.basket';
import { SendCartConfiguration } from './cnf.send-cart';
import { map } from 'rxjs/operators'
import { Observable } from 'rxjs'
import { LocalstorageService, SessionstorageService } from '@k-core/services/general/storage'
import { ActiveCartService } from '@k-core/services/active-cart.service'

// Interface for /Feed/set/quote
interface ISetQuote {
    "status": string;
    "data": {
        errorMessage?: string;
        message?: string;
    }
}

@Injectable()
export class SendCartFactory {

    public couponMessageSource = new Subject<any>()
    public couponMessage$ = this.couponMessageSource.asObservable()

    coupons = new Map

    constructor(
        @Inject(PLATFORM_ID) private _platformId,
        public localStorage: LocalstorageService,
        public sessionStorage: SessionstorageService,
        private _router: Router,
        private _http: HttpClient,
        private _helper: HelperService,
        private _cache: CacheService,
        private _basket: BasketService,
        private _config: SendCartConfiguration,
        private activeCart: ActiveCartService
    ) {
    }

    /**
     * Return a list of BUS Ids
     */
    getBusIds() {
        return this._http.get(this._helper.server + 'feed/get/curtain-busses' + this._helper.credentials + '&noCache').toPromise().then((response: any) => {
            return response.data
        })
    }


    /**
     * Gets product price by `sku`
     * 
     * @param sku 
     */
    getSkuProductPrice(sku) {
        return this._http.get(this._helper.server + 'feed/get/product' + this._helper.credentials + '&sku='+ sku +'&fields:price').toPromise().then((response) => {
            return response
        })
    }


    /**
     * Sets the discounts on a sessionStorage element
     */
    storeDiscounts() {

        if(isPlatformBrowser(this._platformId)) {

            let string = JSON.stringify([...Array.from(this.coupons.entries())])
            this.sessionStorage.setItem('kake-discounts', string)
        }
    }


    /**
     * Gets all discounts available from backend, or checks if they exist in `cache` before requesting
     */
    getDiscounts():Promise<any> {

        if(isPlatformBrowser(this._platformId)) {

            let requestActiveCartRules = {
                key: this._helper.apiKey,
                storeId: this._helper.storeId,
                websiteId: this._helper.websiteId,
                noCache: true,
                cart: this.localStorage.getItem(AppHelper.cookieName)
            }
            
            // For some reason the active cart rules are not being returned correctly from cache, so pull them again until resolved
            if(this._cache.has('active-cart-rules')) {
                return new Promise((resolve) => { resolve(this._cache.get('active-cart-rules')) })
            } else {
    
                return this._http.post(this._helper.server + '/feed/get/active-cart-rules', requestActiveCartRules).toPromise().then((response: any) => {
                    this._cache.set('active-cart-rules', response)
    
                    return response
                })
            }
        }
    }


    /**
     * Add a discount to the order
     * 
     * @param code 
     */
    addDiscount(code, muteMessage?: boolean) {

        if(!this.coupons.has(code.value)) {

            this.coupons.set(code.value, code.value)
            this.storeDiscounts()
        }

        let body = {
            key: this._helper.apiKey,
            websiteId: this._helper.websiteId,
            storeId: this._helper.storeId,
            interceptCouponSet: true,
            noCache: true,
            coupon: {
                code: code.value
            }
        }

        return this._http.post(this._helper.server + 'feed/set/coupon', body).toPromise().then((response: any) => {


                if(!muteMessage) {

                    this.setCouponMessage(response.data.message)
                }

                return response.data.coupons
        })
    }


    /**
     * Remove a discount from the order
     * 
     * @param code 
     */
    removeDiscount(code) {

        // Save localStorage if code removal fails
        // ? Does not seem to be used
        // let temporaryLocalStorage = JSON.parse(this.localStorage.getItem(AppHelper.basketIdentifier))
        
        return new Promise((resolve) => {

            this._removeFromLocalStorage(code).subscribe({
                error: e => {
                    console.error(e)
                },
                complete: () => {

                    new Promise((resolve) => {
            
                        resolve(this.coupons.delete(code.split('--')[1]))
                    }).then((response) => {
            
                        this.storeDiscounts()
                    })
            
            
                    let body = {
                        key: this._helper.apiKey,
                        websiteId: this._helper.websiteId,
                        storeId: this._helper.storeId,
                        remove: true,
                        interceptCouponSet: true,
                        noCache: true,
                        coupon: {
                            code: code
                        }
                    }
            
                    this._http.post(this._helper.server + 'feed/set/coupon', body).toPromise().then((response: any) => {
                        if(response.status == 'success') {
            
                            this.setCouponMessage(response.data.message)                
                            resolve(response.data.coupons ? response.data.coupons : [])
            
                        }
                    })
                }
            })
        })
    }


    /**
     * Removal of a code from the localStorage object. Looks at the type of discount, and acts accordingly
     * reinstates the localStorage element, after code has been spliced
     * 
     * @tutorial
     * ____________
     * -cu--mix10--hash
     * 
     * split('--') = ['cu', 'mix10', 'hash']
     * 
     * [1] = 'mix10'
     * ____________
     * 
     * -- This should never appear within the element
     * 
     * -ir--DI_hash1_hash2
     * 
     * split('_') = ['-ir', 'DI', 'hash1', 'hash2']
     * 
     * [3] = hash2
     * ____________
     * -ir--hash2
     * 
     * split('--') = ['-ir', 'hash2']
     * 
     * [1] = hash2
     * ____________
     * 
     * @param code 
     */
    private _removeFromLocalStorage(code) {

        return new Observable((observer) => {


            // Gets the most recent cart, to ensure manipulation does not happen of an older element
            let localCart = JSON.parse(this.localStorage.getItem(AppHelper.basketIdentifier))
    
    
    
            // Maps out the code identifier to match the identifier used in the following findIndex
            let split

            if(code.startsWith('-ir')) {

    
                if(code.includes('DI_')) {
                    split = code.split('_')
                } 
                else {
                    split = code.split('--')
                }

                // Get the last element in array
                code = split[split.length - 1]

            } else if(code.startsWith('-cu')) {
                code = code.split('--')[1]

            }



            // Throw error if undefined
            if(typeof code === 'undefined') {
                observer.error('code is undefined')
            }




            // Finds the index of the requested code, to remove it. Checks `-cu--` for the discount identifier, `DI_` for the frontend URI and everything else, it isolates the hash
            let position = localCart.findIndex(element => {           
                let identifier
                let split
                if(element.id.startsWith('-ir')) {

                    if(element.id.includes('DI_')) {
                        split = element.id.split('_')
                    } 
                    else {
                        split = element.id.split('--')
                    }
    
                    // Get the last element in array
                    identifier = split[split.length - 1]
        
                } else if(element.id.startsWith('-cu')) {

                    identifier = element.id.split('--')[1]
                }
    
    
                // return true if cart element has same id as target code
                if(code == identifier) {
                    console.warn('match found', [code, identifier])
                    return element.id.includes(identifier) 
                }
            })
    
            if(position !== -1) {
                // Removes the discount and stores it in a variable. Can probably be removed, but for now, we'll keep it for logging
                let removedDiscount = localCart.splice(position, 1)
        
                observer.complete()

                // Reinstates manipulated cart
                this.localStorage.setItem(AppHelper.basketIdentifier, JSON.stringify(localCart))
            }
            else {
                observer.error('could not find position of discount in localstorage')
            }
        })

    }


    /**
     * Get Shipping Methods, return an internal Object with a warning, in case it is not found
     * 
     */
    getShippingMethods() {

        // return new Promise((resolve) => {
        //     resolve({"status":"success","data":{"quote":{"activeShippingMethods":[{"carrier_code":"klean_datarate","method_code":"clickcollect","carrier_title":"Klik og hent","method_title":"ClickCollect","amount":0,"message":"Afhent i en Jysk n\u00e6r dig","has_pickup":true,"pickup_list_url":"https:\/\/dev.jysk.klean.dk\/datarate\/retrieve\/pickuplocations?identifier=jysk_clickcollect&getAll"},{"carrier_code":"klean_datarate","method_code":"glepickup","carrier_title":"GLS Pakkeshop","method_title":"GLSPickup","amount":0,"message":"Bestil forsendelse med GLS, v\u00e6lg butik til afhentning","has_pickup":true,"pickup_list_url":"https:\/\/dev.jysk.klean.dk\/datarate\/retrieve\/pickuplocations?identifier=gls&getAll"},{"carrier_code":"lengthrate","method_code":"GLS","carrier_title":"Levering til privat adresse","method_title":"lengthrate","amount":49,"message":"GLS","has_pickup":false},{"carrier_code":"mountrate","method_code":"mountrate","carrier_title":"Levering med montering","method_title":"","amount":1182.75,"message":"","has_pickup":false},{"carrier_code":"retailrate","method_code":"retailrate","carrier_title":"Levering med montering (retail)","method_title":"Retail Rate","amount":1040.25,"message":"","has_pickup":false}]}},"serverHash":"devjyskkleandk_23045864"})
        // })

        return this._http.get(this._helper.server + 'feed/get/quote'+ this._helper.credentials +'&getActiveShippingMethods&noCache').pipe(

            map((response: any) => {
                if(response.status != 'error')
                    return response.data
            })
        )
    }


    /**
     * POST options to get translated version
     * 
     * @param options 
     */
    requestType(option: any) {

        let cacheId = 'config-label-'+JSON.stringify(option)

        let object = {
            noCache: true,
            key: this._helper.apiKey,
            storeId: this._helper.storeId,
            websiteId: this._helper.websiteId,
            attributes: {
                type_id: option
            }
        }


        if(this._cache.has(cacheId)) {
            return new Promise((resolve) => { resolve(this._cache.get(cacheId)) })
        } else {
            
            return this._http.post(this._helper.server + 'feed/get/configurator-labels', object).toPromise()
    
                .then((response: any) => {
                    if(response.status == 'error') throw response
                    
                    this._cache.set(cacheId, response)
                    return response
                }).catch((error) => {
                    console.error({type: 'POST', url: 'feed/get/configurator-labels', message: error.data.errorMessage})
                    return []
                })
        }
    }


    /**
     * PATCH quote request, then navigate to success page, once completed.
     * 
     * @param cart 
     */
    sendCart(cart) {

        let body = {
            key: this._helper.apiKey,
            websiteId: this._helper.websiteId,
            storeId: this._helper.storeId,
            quote: cart,
            localStorage: this.localStorage.getItem(AppHelper.basketIdentifier)
        }

        return this._http.post(this._helper.server + 'feed/set/quote', body).toPromise().then((response: ISetQuote) => {
            
            if(isPlatformBrowser(this._platformId)) {

                // If the request succceeds, empty the session and send to success page, else log out the error
                if(response.status === 'success') {

                    this._basket.emptyBasket()
                    this.sessionStorage.clear()
                    this.activeCart.getActiveCartLink()
                    this._router.navigate([AppHelper['successEmail']])

                    return response.data

                } else {

                    return response.data
                }

            }
        })
    }


    /**
     * PATCH quote request, with a `saveAndClose` flag, to terminate the current basket, redirects to frontpage once completed.
     * 
     * @param cart 
     */
    saveAndClose(cart) {

        let body = {
            key: this._helper.apiKey,
            websiteId: this._helper.websiteId,
            storeId: this._helper.storeId,
            quote: cart,
            localStorage: this.localStorage.getItem(AppHelper.basketIdentifier),
            saveAndClose: true
        }

        this._http.post(this._helper.server + 'feed/set/quote', body).subscribe((response) => {
            
            // if(isPlatformBrowser(this._platformId)) {

            //     console.warn('trigger reset and close from response')

                // Reset the content - Deprecated?
                // this._config.resetSource.next(true)

                this.sessionStorage.clear()
                this._basket.emptyBasket()
                this.activeCart.getActiveCartLink()

                this._router.navigate(['/'])
            // }
        })
    }


    /**
     * Validates the current session against Magento and returns `boolean` based on result
     * 
     * @param id
     * @returns boolean
     */
    validateSession(id) {

        let body = {
            key: this._helper.apiKey,
            websiteId: this._helper.websiteId,
            storeId: this._helper.storeId,
            quoteId: id,
            noCache: true
        }

        return this._http.post(this._helper.server + 'feed/get/checkActiveQuote', body).toPromise().then((response: any) => {
            return response.data.active
        })
    }


    /**
     * Sets the coupon message as an observable
     * 
     * @param message 
     * @param state 
     */
    setCouponMessage(message, state = 'set') {
        if(state == 'set')
            this.couponMessageSource.next(message)
    }
}
