/**
* AppInsightsCore.ts
* @author Abhilash Panwar (abpanwar) Hector Hernandez (hectorh)
* @copyright Microsoft 2018
*/
import dynamicProto from "@microsoft/dynamicproto-js";
import {
    AppInsightsCore as InternalCore, DiagnosticLogger, IDiagnosticLogger, INotificationManager, IPlugin, ITelemetryItem, _throwInternal,
    arrForEach, doPerf, dumpObj, eLoggingSeverity, throwError
} from "@microsoft/applicationinsights-core-js";
import { IExtendedAppInsightsCore, IExtendedConfiguration, IExtendedTelemetryItem } from "./DataModels";
import { EventLatencyValue, _eExtendedInternalMessageId } from "./Enums";
import { STR_DEFAULT_ENDPOINT_URL, STR_EMPTY, STR_PROPERTIES, STR_VERSION } from "./InternalConstants";
import { FullVersionString, getTime, isLatency } from "./Utils";

export default class AppInsightsCore extends InternalCore implements IExtendedAppInsightsCore {

    // Function to get w parameter used by OneCollector
    public getWParam: () => number;
    public pluginVersionStringArr: string[] = [];
    public pluginVersionString: string;

    constructor() {
        super();

        dynamicProto(AppInsightsCore, this, (_self, _base) => {
            if (!_self.logger || !_self.logger.queue) {
                // The AI Base can inject a No-Op logger so if not defined or the No-Op, change to use a default logger so initialization errors
                // are not dropped on the floor if one is not already defined
                _self.logger = new DiagnosticLogger({ loggingLevelConsole: eLoggingSeverity.CRITICAL });
            }

            _self.initialize = (config: IExtendedConfiguration, extensions: IPlugin[], logger?: IDiagnosticLogger, notificationManager?: INotificationManager) => {
                doPerf(_self, () => "AppInsightsCore.initialize", () => {

                    let _pluginVersionStringArr = _self.pluginVersionStringArr;

                    // Add default collector url
                    if (config) {
                        if (!config.endpointUrl) {
                            config.endpointUrl = STR_DEFAULT_ENDPOINT_URL;
                        }
                        let propertyStorageOverride = config.propertyStorageOverride;
                        // Validate property storage override
                        if (propertyStorageOverride && (!propertyStorageOverride.getProperty || !propertyStorageOverride.setProperty)) {
                            throwError("Invalid property storage override passed.");
                        }
                        if (config.channels) {
                            arrForEach(config.channels, (channels) => {
                                if (channels) {
                                    arrForEach(channels, (channel) => {
                                        if (channel.identifier && channel.version) {
                                            let ver = channel.identifier + "=" + channel.version;
                                            _pluginVersionStringArr.push(ver);
                                        }
                                    });
                                }
                            });
                        }
                    }

                    _self.getWParam = () => {
                        return (typeof document !== "undefined" || !!config.enableWParam) ? 0 : -1;
                    };

                    if (extensions) {
                        arrForEach(extensions, (ext) => {
                            if (ext && ext.identifier && ext.version) {
                                let ver = ext.identifier + "=" + ext.version;
                                _pluginVersionStringArr.push(ver);
                            }
                        });
                    }

                    _self.pluginVersionString = _pluginVersionStringArr.join(";");
                    _self.pluginVersionStringArr = _pluginVersionStringArr;
        
                    try {
                        _base.initialize(config, extensions, logger, notificationManager);

                        _self.pollInternalLogs("InternalLog");
                    } catch (e) {
                        let logger = _self.logger;
                        let message = dumpObj(e);
                        if (message.indexOf("channels") !== -1) {
                            // Add some additional context to the underlying reported error
                            message += "\n - Channels must be provided through config.channels only!";
                        }
                        _throwInternal(logger,
                            eLoggingSeverity.CRITICAL,
                            _eExtendedInternalMessageId.FailedToInitializeSDK, "SDK Initialization Failed - no telemetry will be sent: " + message
                        );
                    }
                }, () => ({ config, extensions, logger, notificationManager }));
            };

            _self.track = (item: IExtendedTelemetryItem|ITelemetryItem) => {
                doPerf(_self, () => "AppInsightsCore.track", () => {
                    let telemetryItem: IExtendedTelemetryItem = item as IExtendedTelemetryItem;
                    if (telemetryItem) {
                        telemetryItem.timings = telemetryItem.timings || {};
                        telemetryItem.timings.trackStart = getTime();
                        if (!isLatency(telemetryItem.latency)) {
                            telemetryItem.latency = EventLatencyValue.Normal;
                        }

                        let itemExt = telemetryItem.ext = telemetryItem.ext || {};
                        itemExt.sdk = itemExt.sdk || {};
                        itemExt.sdk.ver = FullVersionString;
                        let baseData = telemetryItem.baseData = telemetryItem.baseData || {};
                        baseData[STR_PROPERTIES] = baseData[STR_PROPERTIES] || {};
                        
                        let itemProperties = baseData[STR_PROPERTIES];
                        itemProperties[STR_VERSION] = itemProperties[STR_VERSION] || _self.pluginVersionString || STR_EMPTY;
                    }

                    _base.track(telemetryItem);
                }, () => ({ item: item }), !((item as any).sync));
            };
        });
    }

    /**
     * Initialize the sdk.
     * @param config - The configuration to initialize the SDK.
     * @param extensions - An array of extensions that are to be used by the core.
     */
    public initialize(config: IExtendedConfiguration, extensions: IPlugin[], logger?: IDiagnosticLogger, notificationManager?: INotificationManager) {
        // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
    }

    public track(item: IExtendedTelemetryItem|ITelemetryItem) {
        // @DynamicProtoStub -- DO NOT add any code as this will be removed during packaging
    }
}
