/**
 * AppInsightsCore.ts
 * @author Abhilash Panwar (abpanwar) Hector Hernandez (hectorh)
 * @copyright Microsoft 2018
 */
import { AppInsightsCore as InternalCore, IPlugin, eLoggingSeverity, ITelemetryItem, IDiagnosticLogger, INotificationManager, doPerf, arrForEach, dumpObj, DiagnosticLogger } from "@microsoft/applicationinsights-core-js";
import { IExtendedConfiguration, IExtendedTelemetryItem, IExtendedAppInsightsCore } from "./DataModels";
import { EventLatencyValue, _eExtendedInternalMessageId } from "./Enums";
import { isLatency, FullVersionString, getTime } from "./Utils";
import dynamicProto from "@microsoft/dynamicproto-js";

const PropVersion = "version";
const properties = "properties";

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", () => {
                    // Add default collector url
                    if (config) {
                        if (!config.endpointUrl) {
                            config.endpointUrl = "https://browser.events.data.microsoft.com/OneCollector/1.0/";
                        }
                        let propertyStorageOverride = config.propertyStorageOverride;
                        // Validate property storage override
                        if (propertyStorageOverride && (!propertyStorageOverride.getProperty || !propertyStorageOverride.setProperty)) {
                            throw new Error("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;
                                            _self.pluginVersionStringArr.push(ver);
                                        }
                                    });
                                }
                            });
                        }
                    }

                    _self.getWParam = () => {
                        return typeof document !== "undefined" ? 0 : -1;
                    };

                    if (extensions) {
                        arrForEach(extensions, (ext) => {
                            if (ext && ext.identifier && ext.version) {
                                let ver = ext.identifier + "=" + ext.version;
                                _self.pluginVersionStringArr.push(ver);
                            }
                        });
                    }

                    _self.pluginVersionString = _self.pluginVersionStringArr.join(";");
                    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!";
                        }
                        logger.throwInternal(
                            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 || {};
                        if (!baseData[properties]) {
                            baseData[properties] = {};
                        }

                        let itemProperties = baseData[properties];
                        if (!itemProperties[PropVersion]) {
                            itemProperties[PropVersion] = "";
                        }

                        if (_self.pluginVersionString !== "") {
                            itemProperties[PropVersion] = _self.pluginVersionString;
                        }
                    }

                    _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
    }
}
