import DeferredRunner from "./deferredRunner";
import ECommerce from "./ECommerce";
import ELearning from "./ELearning";
import Loyalty from "./Loyalty";
import Navigation from "./Navigation";
import Payments from "./Payments";
import PatientMgmt from "./PatientMgmt";
import CallCenter from "./CallCenter";
import EmergencyMgmt from "./EmergencyMgmt";

import { default as CfLogFallback } from '../modules/CfLog'
import { default as ECommerceFallback } from '../modules/ECommerce'
import { default as ELearningFallback } from '../modules/ELearning'
import { default as LoyaltyFallback } from '../modules/Loyalty'
import { default as NavigationFallback } from '../modules/Navigation'
import { default as PaymentsFallback } from '../modules/Payments'
import { default as PatientMgmtFallback } from "../modules/PatientMgmt";
import { default as CallCenterFallback } from "../modules/CallCenter";
import { default as EmergencyMgmtFallback } from "../modules/EmergencyMgmt";

import { EventEmitter } from 'events';
import {CfLogEvents, NudgeScreenType} from "../core/commonTypes";
import { getFilenameForVersion, getLatestPatch, getListOfVersionsFromManifest } from "../cdn";

import { version } from '../../package.json'
import CfExceptionHandler from "../core/CfExceptionHandler";


function setExternalScript(cdnUrl) {
    return new Promise((resolve, reject) => {
        const load = async () => {
            try {
                const fetchedManifestResponse = await fetch(`${cdnUrl}manifest.json`)
                const manifest = await fetchedManifestResponse.json()

                const versions = getListOfVersionsFromManifest(manifest)
                const desiredVersion = getLatestPatch(version, versions)

                const scriptTag = document.createElement('script');
                scriptTag.type = 'text/javascript';
                scriptTag.src = `${cdnUrl}${getFilenameForVersion(desiredVersion)}`;
                scriptTag.onload = () => resolve(undefined);
                scriptTag.onerror = () => reject()
                document.body.appendChild(scriptTag);

            } catch (e) {
                console.warn('error loading sdk', e)

                reject()
            }
        }

        load()
    });
}

const moduleName = 'internalCfLog'

export default class CfLog extends EventEmitter {
    private key
    private options
    private deferredRunner = new DeferredRunner()
    private DEFAULT_CDN_URL = 'https://cdn.causalfoundry.ai/'
    private cdnUrl
    private cfLog

    private static cfLogInstance : CfLog
    private static cfSdkUserId : string

    constructor(key, options) {
        super()

        this.key = key
        this.options = options
        this.cdnUrl = this.options.cdnUrl || this.DEFAULT_CDN_URL
        this.cdnUrl = this.cdnUrl[this.cdnUrl.length - 1] === '/' ? this.cdnUrl : `${this.cdnUrl}/`

        delete this.options.cdnUrl

        ECommerce.init(this.deferredRunner)
        ELearning.init(this.deferredRunner)
        Loyalty.init(this.deferredRunner)
        Navigation.init(this.deferredRunner)
        Payments.init(this.deferredRunner)
        PatientMgmt.init(this.deferredRunner)
        CallCenter.init(this.deferredRunner)
        EmergencyMgmt.init(this.deferredRunner)

        if (this.options.skipDynamicLoading || this.options.pauseSDK) {
            this.useFallback()
        } else {
            setExternalScript(this.cdnUrl)
                .then(() => {
                    this.useExternalLibrary()
                })
                .catch(e => {
                    console.log('error loading external script: ', e)
                    this.useFallback()
                    console.log(`using fallback SDK@${version}`)
                })
        }
    }

    private subscribe() {
        this.cfLog.on(CfLogEvents.NudgeAction, (...args) => {
            this.emit(CfLogEvents.NudgeAction, ...args)
        })
    }

    private useExternalLibrary() {
        (window as any).internalCfLog = new (window as any).CfLog(this.key, this.options)
        this.cfLog = (window as any).internalCfLog

        this.subscribe()
        this.deferredRunner.activate(window);
    }

    private useFallback() {
        this.cfLog = new CfLogFallback(this.key, this.options)

        const modules = {
            Loyalty: LoyaltyFallback,
            Navigation: NavigationFallback,
            Payments: PaymentsFallback,
            ELearning: ELearningFallback,
            ECommerce: ECommerceFallback,
            PatientMgmt: PatientMgmtFallback,
            CallCenter: CallCenterFallback,
            EmergencyMgmt: EmergencyMgmtFallback,
            [moduleName]: this.cfLog
        }

        this.subscribe()
        this.deferredRunner.activate(modules);
    }

    public identify(...args) {
        CfLog.cfSdkUserId = args[1] // userId at index 1
        this.deferredRunner.execute(moduleName, 'identify', args)
    }

    public static createSDKInstance(sdkKey, options) {

        if(CfLog.cfLogInstance){
            return CfLog.cfLogInstance
        }

        CfLog.cfLogInstance = new CfLog(sdkKey, options)
        return CfLog.cfLogInstance
    }

    public static getSDKInstance() {
        if(!CfLog.cfLogInstance){
            new CfExceptionHandler().throwAndLogError('CfLog','CfLog instance not created')
            return
        }
        return CfLog.cfLogInstance
    }

    public userBlockStatus(...args) {
        this.deferredRunner.execute(moduleName, 'userBlockStatus', args)
    }

    public updateUserCatalog(...args) {
        this.deferredRunner.execute(moduleName, 'updateUserCatalog', args)
    }

    public updateSiteCatalog(...args) {
        this.deferredRunner.execute(moduleName, 'updateSiteCatalog', args)
    }

    public setUserId(...args) {
        this.deferredRunner.execute(moduleName, 'setUserId', args)
    }

    public fetchNudgesForScreen(nudgeScreenType : NudgeScreenType) {
        this.deferredRunner.execute(moduleName, 'fetchNudgesForScreen', [CfLog.cfSdkUserId, nudgeScreenType])
    }

    public flush(...args) {
        this.deferredRunner.execute(moduleName, 'flush', args)
    }
}
