import { reflect, string, storage } from '@amp-metrics/mt-metricskit-utils-private';

/*
 *  src/helpers/constants.js
 *  ae-client-kit-core
 *
 *  Copyright © 2019 Apple Inc. All rights reserved.
 *
 */

var _allBaseFieldNames;
var _environmentBaseFieldNames;

function requiredEnvironmentBaseFieldNames() {
    return [
        'app',
        'appVersion',
        'hardwareFamily',
        'hardwareModel',
        'os',
        'osBuildNumber',
        'osLanguages',
        'osVersion',
        'resourceRevNum',
        'screenHeight',
        'screenWidth',
        'userAgent'
    ];
}

function optionalEnvironmentBaseFieldNames() {
    return ['delegateApp', 'hardwareBrand', 'storeFrontCountryCode', 'storeFrontHeader', 'storeFrontLanguage'];
}

function otherBaseFieldNames() {
    return [
        'baseVersion',
        'clientEventId',
        'connection',
        'eventTime',
        'eventType',
        'eventVersion',
        'timezoneOffset',
        'xpPostFrequency',
        'xpSendMethod'
    ];
}

function environmentBaseFieldNames() {
    if (!_environmentBaseFieldNames) {
        _environmentBaseFieldNames = requiredEnvironmentBaseFieldNames().concat(optionalEnvironmentBaseFieldNames());
    }

    return _environmentBaseFieldNames;
}

function allBaseFieldNames() {
    if (!_allBaseFieldNames) {
        _allBaseFieldNames = environmentBaseFieldNames().concat(otherBaseFieldNames());
    }

    return _allBaseFieldNames;
}

/*
 *  src/event_handlers/Base.js
 *  ae-client-kit-core
 *
 *  Copyright © 2018 Apple Inc. All rights reserved.
 *
 */

var attachDelegate = reflect.attachDelegate;
var cryptoRandomBase62String = string.cryptoRandomBase62String;
var exceptionString = string.exceptionString;

var _prototypeInitialized;

/**
 * Creates and returns an object (key/value data map (dictionary)) containing all of the "base" fields common to all metrics events.
 * To override any functionality in this class, use the "setDelegate() method in order to override the specific functions that need customization.
 * Kits can also extend this class and additional methods specific to their event model.
 * @example
 *    // extend kit-core Base class
 *    var MetricsKitBase = function MetricsKitBase() {
 *          kitCore.eventHandlers.Base.apply(this, arguments); // invoke Base constructor
 *     };
 *     MetricsKitBase.prototype = new kitCore.eventHandlers.Base();
 *     MetricsKitBase.prototype.constructor = MetricsKitBase;
 *
 *     // set Kit-specific methods
 *     MetricsKitBase.prototype.environment = function() { return metricsKit.system.environment; }
 *     MetricsKitBase.prototype.eventRecorder = function() { return metricsKit.system.eventRecorder; }
 *     MetricsKitBase.prototype.metricsData = function(pageId, pageType, pageContext, callerSuppliedFieldsMapsN) { ... }
 *     MetricsKitBase.prototype.processMetricsData = function( ... ) { ... }
 *
 *     // extend kit-core known fields
 *     MetricsKitBase.prototype.knownFields = function knownFields() {
 *         var parentKnownFields = Object.getPrototypeOf(MetricsKitBase.prototype).knownFields();
 *         return parentKnownFields.concat(['dsId', 'anotherUserExperienceOnlyField']);
 *     };
 *
 *     // add Kit-specific accessor functions
 *     MetricsKitBase.prototype.dsId = function dsId() { return this.environment().dsId(); };
 *     MetricsKitBase.prototype.anotherUserExperienceOnlyField = function() { ... };
 *
 *     // create a Kit-specific class instance
 *     metricsKit.eventHandlers.base = new MetricsKitBase(metricsKit);
 * @param {MetricsKit/PerfKit/VPAFKit} kit
 * @delegatable
 * @constructor
 */
function Base(processor) {
    if (!reflect.isDefinedNonNull(processor)) {
        throw new Error('A processor instance is required for creating BaseEventHandler.');
    }
    // @private
    this._processor = processor;

    if (!_prototypeInitialized) {
        _prototypeInitialized = true;
        environmentBaseFieldNames().forEach(function (fieldName) {
            Base.prototype[fieldName] = function (callerSuppliedEventFields) {
                var returnValue;

                if (callerSuppliedEventFields && callerSuppliedEventFields.hasOwnProperty(fieldName)) {
                    returnValue = callerSuppliedEventFields[fieldName];
                } else {
                    returnValue = this.environment()[fieldName]();
                }

                return returnValue;
            };
        });
    }
}

Base._className = 'eventHandlers.base';

/**
 * Allows replacement of one or more of this class instance's functions
 * Any method on the passed-in object which matches a method that this class has will be called instead of the built-in class method.
 * To replace *all* methods of his class, simply have your delegate implement all the methods of this class
 * Your delegate can be a true object instance, an anonymous object, or a class object.
 * Your delegate is free to have as many additional non-matching methods as it likes (these methods will not be copied to the target object).
 * It can even act as a delegate for multiple MetricsKit objects, though that is not recommended.
 *
 * "setDelegate()" may be called repeatedly, with the functions in the most-recently set delegates replacing any functions matching those in the earlier delegates, as well as any as-yet unreplaced functions.
 * This allows callers to replace some number of methods that need custom implementations.
 * If, for example, a client wants to use the standard logger implementation with the exception of, say, the "debug" method, they can
 * call "setDelegate()" with their own delegate containing only a single method of "debug" as the delegate, which would leave all the other methods intact.
 *
 * NOTE: The delegate function will have a property called origFunction representing the original function that it replaced.
 * This allows the delegate to, essentially, call "super" before or after it does some work.
 * If a replaced method is overridden again with a subsequent "setDelegate()" call, the "origFunction" property will be the previous delegate's function.
 * @example:
 * To override one or more methods, in place:
 *      base.setDelegate({ app: function() { return 'myApp'; });
 * To override one or more methods with a separate object:
 *      base.setDelegate(customBaseDelegate);
 *      (where "customBaseDelegate" might be defined elsewhere as, e.g.:
 *          var customBaseDelegate = { app: function() { return Device.appIdentifier; };
 *                                            appVersion: function() { return Device.appVersion; } };
 * To override one or more methods with an instantiated object from a class definition:
 *      base.setDelegate(new CustomBaseDelegate());
 *      (where "CustomBaseDelegate" might be defined elsewhere as, e.g.:
 *         function CustomBaseDelegate() {}
 *         CustomBaseDelegate.prototype.app = function app() { return Device.appIdentifier; };
 *         CustomBaseDelegate.prototype.appVersion = function appVersion() { return Device.appVersion; };
 * To override one or more methods with a class object (with "static" methods):
 *      base.setDelegate(CustomBaseDelegate);
 *      (where "CustomBaseDelegate" might be defined elsewhere as, e.g.:
 *         function CustomBaseDelegate() {}
 *         CustomBaseDelegate.app = function app() { return Device.appIdentifier; };
 *         CustomBaseDelegate.appVersion = function appVersion() { return Device.appVersion; };
 * @param {Object} delegate Object or Class with delegate method(s) to be called instead of default (built-in) methods.
 * @returns {Boolean} true if one or more methods on the delegate object match one or more methods on the default object,
 * otherwise returns false.
 */
Base.prototype.setDelegate = function setDelegate(delegate) {
    return attachDelegate(this, delegate);
};

/**
 * The active environment class
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @see src/system/Environment
 * @return {Environment}
 * @overridable
 */
Base.prototype.environment = function environment() {
    // Don't wrap the throw in a helper function or the backtrace won't be as nice.
    throw exceptionString(Base._className, 'environment');
};

/**
 * The active eventRecorder
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @return {Object} an event recorder that implements a sendMethod() function
 * @overridable
 */
Base.prototype.eventRecorder = function eventRecorder() {
    // Don't wrap the throw in a helper function or the backtrace won't be as nice.
    throw exceptionString(Base._className, 'eventRecorder');
};

/**
 * Creates a simple map object (dictionary) with all the "base" fields required by AMP Analytics
 * Some fields can be derived by this class itself.
 * Some fields need to be provided by callers.
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @returns key/value pairs of all "base" fields required by AMP Analytics.
 * WARNING: May return "null" if metrics are disabled via the metrics.disabled config source value, or on error.
 * @overridable
 *
 * TODO: consider adding default implementation for shared Kit use
 */
Base.prototype.metricsData = function metricsData() {
    // Don't wrap the throw in a helper function or the backtrace won't be as nice.
    throw exceptionString(Base._className, 'metricsData');
};

/**
 * All of the various eventHandlers invoke this method to generate their metrics data
 * The data is a simple map object (dictionary) with all the fields required by AMP Analytics for that event
 * Some fields can be derived by this class itself.
 * This function typically expects to be called with the correct context
 *     (e.g. base.processMetricsData.apply(this, arguments))
 * @returns {Object} key/value pairs of all fields required by AMP Analytics.
 * WARNING: May return "null" if metrics and/or the specific eventType for this handler is disabled, or on error.
 *
 * TODO: consider adding default implementation for shared Kit use
 */
Base.prototype.processMetricsData = function processMetricsData() {
    // Don't wrap the throw in a helper function or the backtrace won't be as nice.
    throw exceptionString(Base._className, 'processMetricsData');
};

/**
 * @return {Array} all the fields that base eventHandlers know about
 */
Base.prototype.knownFields = function knownFields() {
    return allBaseFieldNames();
};

/**
 * ********************* ACCESSOR FUNCTIONS *********************
 * We create accessor functions for every data field because:
 * 1. Cleans/simplifies all methods that use it.
 * 2. Facilitates writing test case shims
 * 3. Allows specific feature suppliers to be overridden (via setDelegate()))
 */

/**
 * The app identifier of the binary app
 * @param {Map} callerSuppliedEventFields
 * @returns {String} The app identifier of the binary app
 * @example "com.apple.appstore" or "com.apple.gamecenter"
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.app = function app(callerSuppliedEventFields) { };

/**
 * The version number of this application
 * @example "1.0", "5.43", etc.
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the version number of this application
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.appVersion = function appVersion(callerSuppliedEventFields) { };

/**
 * The version of the set of base data to be sent up
 * @returns {number} the version of the set of base data to be sent up
 * @overridable
 */
Base.prototype.baseVersion = function baseVersion() {
    return 1;
};

/**
 * A unique identifier for each event
 * @return {String}
 * @overridable
 */
Base.prototype.clientEventId = function clientEventId(callerSuppliedEventFields) {
    return (callerSuppliedEventFields && callerSuppliedEventFields.clientEventId) || cryptoRandomBase62String(true);
};

/**
 * Type of internet connection.
 * Only applicable to devices
 * Beware that users on WiFi may actually be receiving 3G speeds (i.e. if device is tethered to a portable hotspot.)
 * @example "WiFi, "3G, etc.
 * @param {Map} callerSuppliedEventFields
 * @returns {String} type of internet connection
 * @overridable
 */
Base.prototype.connection = function connection(callerSuppliedEventFields) {
    return (callerSuppliedEventFields && callerSuppliedEventFields.connection) || this.environment().connectionType();
};

/**
 * The identifier of the process generating the event, if different from “app”, or blank otherwise.
 * @example 'web-experience-app'
 * @returns {String}
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.delegateApp = function delegateApp(callerSuppliedEventFields) { };

/**
 * The id of this user ("directory service id").
 * This id will get mapped to a consumerId on the server.
 * @example 659261189
 * @param {Map} callerSuppliedEventFields
 * @returns {String}
 * @overridable
 */
Base.prototype.dsId = function dsId(callerSuppliedEventFields) {
    return (callerSuppliedEventFields && callerSuppliedEventFields.dsId) || this.environment().dsId();
};

/**
 * The time (UTC) in milliseconds at which this event happened.
 * This field is central to determining the sequence of user events
 * Use online epoch converter to test your values.
 * @example 1437356433388 (http://www.epochconverter.com converts that to:  July 19, 2015 at 6:40:33 PM PDT GMT-7:00 DST)
 * @param {Map} callerSuppliedEventFields
 * @returns {number} the time (UTC) in milliseconds at which this event happened
 * @overridable
 */
Base.prototype.eventTime = function eventTime(callerSuppliedEventFields) {
    return (callerSuppliedEventFields && callerSuppliedEventFields.eventTime) || Date.now();
};

/**
 * The version of the set of data to be sent up
 * @return {Number}
 * @overridable
 */
Base.prototype.eventVersion = function eventVersion(callerSuppliedEventFields) {
    return (callerSuppliedEventFields && callerSuppliedEventFields.eventVersion) || null;
};

/**
 * The hardware brand of the device. Not required for Apple devices.
 * @example "Samsung", "LG", "Google"
 * @param {Map} callerSuppliedEventFields
 * @returns {String}
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.hardwareBrand = function hardwareBrand(callerSuppliedEventFields) { };

/**
 * The hardware family of the device
 * @example "iPhone", "Macbook Pro"
 * @param {Map} callerSuppliedEventFields
 * @returns {String}
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.hardwareFamily = function hardwareFamily(callerSuppliedEventFields) { };

/**
 * The model of the device
 * @example "iPhone10,2", "MacbookPro11,5"
 * @returns {String}
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.hardwareModel = function hardwareModel(callerSuppliedEventFields) { };

/**
 * The name of the OS
 * @example "ios", "macos", "windows"
 * @param {Map} callerSuppliedEventFields
 * @returns {String}
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.os = function os(callerSuppliedEventFields) { };

/**
 * The build number of the OS
 * @example "15D60", "17E192"
 * @param {Map} callerSuppliedEventFields
 * @returns {String}
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.osBuildNumber = function osBuildNumber(callerSuppliedEventFields) { };

/**
 * A string array of language IDs, ordered in descending preference
 * @example ["en-US", "fr-CA"]
 * @param {Map} callerSuppliedEventFields
 * @returns {Array}
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.osLanguages = function osLanguages(callerSuppliedEventFields) { };

/**
 * The full OS version number
 * In ITML, the value can be retrieved via Device.systemVersion
 * @example "8.2.1" (iOS) "10.10.3" (Desktop)
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the full OS version number
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.osVersion = function osVersion(callerSuppliedEventFields) { };

/**
 * The HTML resources revision number
 * @example 2C97 or 8.4.0.0.103
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the HTML resources revision number
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.resourceRevNum = function resourceRevNum(callerSuppliedEventFields) { };

/**
 * The client screen height in pixels
 * @example 568
 * @param {Map} callerSuppliedEventFields
 * @returns {number} the client screen height in pixels
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.screenHeight = function screenHeight(callerSuppliedEventFields) { };

/**
 * The client screen width in pixels
 * @example 320
 * @param {Map} callerSuppliedEventFields
 * @returns {number} the client screen width in pixels
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.screenWidth = function screenWidth(callerSuppliedEventFields) { };

/**
 * ISO 3166 Country Code. Apps that cannot provide a storeFrontHeader should provide a storeFrontCountryCode instead
 * @example US
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the store front country code
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.storeFrontCountryCode = function storeFrontCountryCode(callerSuppliedEventFields) { };

/**
 * The value contained in the X-Apple-Store-Front header value at the time the event is being created.
 * @example K143441-1,29 ab:rSwnYxS0
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the value contained in the X-Apple-Store-Front header value at the time the event is being created
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.storeFrontHeader = function storeFrontHeader(callerSuppliedEventFields) { };

/**
 * The difference, in minutes, between GMT (UTC) and timezone of event origin ("local time).
 * This means that the offset is positive if the local timezone is behind UTC and negative if it is ahead.
 * Daylight saving time prevents this value from being a constant, even for a given locale
 * @example 420 (PST, not -420) or -600 (Australian Eastern Standard Time, UTC+10)
 * @param {Map} callerSuppliedEventFields
 * @returns {number} the difference, in minutes, between GMT (UTC) and timezone of event origin ("local time).
 * @overridable
 */
Base.prototype.timezoneOffset = function timezoneOffset(callerSuppliedEventFields) {
    return (callerSuppliedEventFields && callerSuppliedEventFields.timezoneOffset) || new Date().getTimezoneOffset();
};

/**
 * The client’s user agent string.  If the "app field is not provided, "userAgent may be used to derive the value of the "app field
 * @example AppStore/2.0 iOS/8.3 model/iPhone7,2 build/12F70 (6; dt:106)
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the client’s user agent string.  If the "app field is not provided, "userAgent may be used to derive the value of the "app field
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Base.prototype.userAgent = function userAgent(callerSuppliedEventFields) { };

/**
 * How often, in milliseconds, batches of events should get sent to the server.
 * This field should be based on the client's most recent config value of "postFrequency".
 * This is valuable for problem analysis because it indicates if and how clients are honoring the "postFrequency" value
 * they were supplied with.
 * This cannot be a "passthrough" field, because it can change (via new config) during program execution, so the value
 * in effect at event creation time is what is needed.
 * @example 60000
 * @param {Map} callerSuppliedEventFields
 * @returns {number} how often, in milliseconds, batches of events should get sent to the server
 * @overridable
 */
Base.prototype.xpPostFrequency = function xpPostFrequency(callerSuppliedEventFields) {
    return (
        (callerSuppliedEventFields && callerSuppliedEventFields.xpPostFrequency) ||
        this._processor.config.value('postFrequency')
    );
};

/**
 * The methodology being used by the eventRecorder to send batches of events to the server
 * This field should be hardcoded in the client based on what method it is using to encode and send its events to Figaro.
 * The three typical values are:
 *      "itms" - use this value when/if JavaScript code enqueues events for sending via the "itms.recordEvent()" method in ITML.
 *      "itunes" - use this value when/if JavaScript code enqueues events by calling the "iTunes.recordEvent()" method in Desktop Store apps.
 *      "javascript" - use this value when/if JavaScript code enqueues events for sending via the JavaScript eventQueue management. This is typically only used by older clients which don't have the built-in functionality of itms or iTunes available to them.
 * @example "itms", "itunes", "javascript"
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the methodology being used by the eventRecorder to send batches of events to the server
 * @overridable
 */
Base.prototype.xpSendMethod = function (callerSuppliedEventFields) {
    return (callerSuppliedEventFields && callerSuppliedEventFields.xpSendMethod) || this.eventRecorder().sendMethod();
};

/*
 *  src/event_handlers/index.js
 *  ae-client-kit-core
 *
 *  Copyright © 2018 Apple Inc. All rights reserved.
 *
 */

var eventHandlers = {
    Base: Base
};

/*
 *  src/system/Environment.js
 *  ae-client-kit-core
 *
 *  Copyright © 2018 Apple Inc. All rights reserved.
 *
 */

var attachDelegate$1 = reflect.attachDelegate;
var exceptionString$1 = string.exceptionString;
var localStorageObject = storage.localStorageObject;
var sessionStorageObject = storage.sessionStorageObject;

var _prototypeInitialized$1;

/**
 * Provides a set of environment-specific (platform-specific) functions which can be individually overridden for the needs
 * of the particular environment, or replaced en masse by providing a single replacement environment delegate object.
 * Kits can extend this class and additional methods specific to their event model.
 * @example
 *     // MetricsKit code
 *
 *     // extend kit-core Environment class
 *     var MetricsKitEnvironment = function MetricsKitEnvironment() {
 *          kitCore.system.Environment.apply(this, arguments); // invoke Environment constructor
 *     };
 *     MetricsKitEnvironment.prototype = new kitCore.system.Environment();
 *     MetricsKitEnvironment.prototype.constructor = MetricsKitEnvironment;
 *
 *     // add Kit-specific functions
 *     MetricsKitEnvironment.prototype.dsId = function dsId() {
 *         throw this._exceptionString(_utils.reflect.functionName());
 *     };
 *     MetricsKitEnvironment.prototype.anotherUserExperienceOnlyField = function() { ... };
 *
 *     // create a Kit-specific class instance
 *     metricsKit.system.environment = new MetricsKitEnvironment();
 *
 *     // client app code remains unchanged:
 *     var customEnvironmentDelegate = {
 *         app: function() { return 'myAppName'; },
 *         appVersion: function() { ... } // etc.
 *     };
 *
 *     metricsKit.system.environment.setDelegate(customEnvironmentDelegate);
 *
 * @constructor
 */
function Environment() {
    // consider moving this code into a separate init method
    if (!_prototypeInitialized$1) {
        _prototypeInitialized$1 = true;
        requiredEnvironmentBaseFieldNames().forEach(function (fieldName) {
            Environment.prototype[fieldName] = function () {
                throw exceptionString$1(Environment._className, fieldName);
            };
        });

        optionalEnvironmentBaseFieldNames().forEach(function (fieldName) {
            Environment.prototype[fieldName] = function () {};
        });
    }
}

Environment._className = 'system.environment';

/**
 * Allows replacement of one or more of this class instance's functions
 * Any method on the passed-in object which matches a method that this class has will be called instead of the built-in class method.
 * To replace *all* methods of his class, simply have your delegate implement all the methods of this class
 * Your delegate can be a true object instance, an anonymous object, or a class object.
 * Your delegate is free to have as many additional non-matching methods as it likes (these methods will not be copied to the target object).
 * It can even act as a delegate for multiple MetricsKit objects, though that is not recommended.
 *
 * "setDelegate()" may be called repeatedly, with the functions in the most-recently set delegates replacing any functions matching those in the earlier delegates, as well as any as-yet unreplaced functions.
 * This allows callers to replace some number of methods that need custom implementations.
 * If, for example, a client wants to use the standard logger implementation with the exception of, say, the "debug" method, they can
 * call "setDelegate()" with their own delegate containing only a single method of "debug" as the delegate, which would leave all the other methods intact.
 *
 * NOTE: The delegate function will have a property called origFunction representing the original function that it replaced.
 * This allows the delegate to, essentially, call "super" before or after it does some work.
 * If a replaced method is overridden again with a subsequent "setDelegate()" call, the "origFunction" property will be the previous delegate's function.
 * @example:
 * To override one or more methods, in place:
 *      environment.setDelegate({ app: function() { return 'myApp'; });
 * To override one or more methods with a separate object:
 *      environment.setDelegate(customEnvironmentDelegate);
 *      (where "customEnvironmentDelegate" might be defined elsewhere as, e.g.:
 *          var customEnvironmentDelegate = { app: function() { return Device.appIdentifier; },
 *                                            appVersion: function() { return Device.appVersion; } };
 * To override one or more methods with an instantiated object from a class definition:
 *      environment.setDelegate(new CustomEnvironmentDelegate());
 *      (where "CustomEnvironmentDelegate" might be defined elsewhere as, e.g.:
 *         function CustomEnvironmentDelegate() {}
 *         CustomEnvironmentDelegate.prototype.app = function app() { return Device.appIdentifier; };
 *         CustomEnvironmentDelegate.prototype.appVersion = function appVersion() { return Device.appVersion; };
 * To override one or more methods with a class object (with "static" methods):
 *      environment.setDelegate(CustomEnvironmentDelegate);
 *      (where "CustomEnvironmentDelegate" might be defined elsewhere as, e.g.:
 *         function CustomEnvironmentDelegate() {}
 *         CustomEnvironmentDelegate.app = function app() { return Device.appIdentifier; };
 *         CustomEnvironmentDelegate.appVersion = function appVersion() { return Device.appVersion; };
 * @param {Object} delegate Object or Class with delegate method(s) to be called instead of default (built-in) methods.
 * @returns {Boolean} true if one or more methods on the delegate object match one or more methods on the default object,
 * otherwise returns false.
 */
Environment.prototype.setDelegate = function setDelegate(delegate) {
    return attachDelegate$1(this, delegate);
};

/**
 * The app identifier of the binary app
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "com.apple.appstore" or "com.apple.gamecenter"
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.app = function app() { };

/**
 * The version number of this application
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "1.0", "5.43", etc.
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.appVersion = function appVersion() { };

/**
 * Type of internet connection.
 * Only applicable to devices
 * Beware that users on WiFi may actually be receiving 3G speeds (i.e. if device is tethered to a portable hotspot.)
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "WiFi, "3G, etc.
 * @returns {String}
 */
Environment.prototype.connectionType = function connectionType() {
    // Don't wrap the throw in a helper function or the backtrace won't be as nice.
    // TODO: use a constant instead of a hardcoded string
    throw exceptionString$1(Environment._className, 'connectionType');
};

/**
 * The identifier of the process generating the event, if different from “app”, or blank otherwise.
 * NO DEFAULT IMPLEMENTATION... HOWEVER: these fields are not required, so an exception will not be thrown if they
 * are omitted.
 * @example 'web-experience-app'
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.delegateApp = function delegateApp(callerSuppliedEventFields) { };

/**
 * The id of this user ("directory service id").
 * This id will get mapped to a consumerId on the server.
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example 659261189
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
Environment.prototype.dsId = function dsId() {
    // Don't throw an exception to avoid breaking minor version backwards compatibility.
    // Consider adding an exception in the next major version.
};

/**
 * The hardware brand of the device. Not required for Apple devices.
 * NO DEFAULT IMPLEMENTATION... HOWEVER: these fields are not required, so an exception will not be thrown if they
 * are omitted.
 * @example "Samsung", "LG", "Google"
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.hardwareBrand = function hardwareBrand() { };

/**
 * The hardware family of the device
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "iPhone", "Macbook Pro"
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.hardwareFamily = function hardwareFamily() { };

/**
 * The model of the device
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "iPhone10,2", "MacbookPro11,5"
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.hardwareModel = function hardwareModel() { };

/**
 * The name of the OS
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "ios", "macos", "windows"
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.os = function os() { };

/**
 * The build number of the OS
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "15D60", "17E192"
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.osBuildNumber = function osBuildNumber() { };

/**
 * A string array of language IDs, ordered in descending preference
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example ["en-US", "fr-CA"]
 * @returns {Array}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.osLanguages = function osLanguages() { };

/**
 * The full OS version number
 * In ITML, the value can be retrieved via Device.systemVersion
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example "8.2.1" (iOS) "10.10.3" (Desktop)
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.osVersion = function osVersion() { };

/**
 * HTML resources revision number
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example 2C97 or 8.4.0.0.103
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.resourceRevNum = function resourceRevNum() { };

/**
 * Client screen height in pixels
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example 568
 * @returns {number}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.screenHeight = function screenHeight() { };

/**
 * Client screen width in pixels
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example 320
 * @returns {number}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.screenWidth = function screenWidth() { };

/**
 * ISO 3166 Country Code. Apps that cannot provide a storeFrontHeader should provide a storeFrontCountryCode instead
 * NO DEFAULT IMPLEMENTATION... Either this method or storeFrontHeader must be replaced.
 * are omitted.
 * @example US
 * @param {Map} callerSuppliedEventFields
 * @returns {String} the store front country code
 * @overridable
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.storeFrontCountryCode = function storeFrontCountryCode(callerSuppliedEventFields) { };

/**
 * The value contained in the X-Apple-Store-Front header value at the time the event is being created.
 * NO DEFAULT IMPLEMENTATION... Either this method or storeFrontHeader must be replaced.
 * @example K143441-1,29 ab:rSwnYxS0
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.storeFrontHeader = function storeFrontHeader() { };

/**
 * Client’s user agent string.  If the "app field is not provided, "userAgent may be used to derive the value of the "app field
 * NO DEFAULT IMPLEMENTATION... THIS METHOD MUST BE REPLACED
 * @example AppStore/2.0 iOS/8.3 model/iPhone7,2 build/12F70 (6; dt:106)
 * @returns {String}
 */
// This prototype method is created dynamically upon instance initialization
// Environment.prototype.userAgent = function userAgent() { };

/**
 * Some clients have platform-specific implementations of these objects (e.g. iTunes.sessionStorage),
 * so we cover them in case they need to be overridden.
 */
Environment.prototype.localStorageObject = localStorageObject;
Environment.prototype.sessionStorageObject = sessionStorageObject;

/**
 * Fetching identifier entity from AMS Metrics Identifier API
 * @param {String} idNamespace - The id namespace that is defined under 'metricsIdentifier' in the bag
 * @param {'userid' | 'clientid'} idType - The identifier type (userid or clientid)
 * @param {Boolean} crossDeviceSync - The boolean flag to indicate whether the identifier is synced across devices
 * @returns {Promise}
 * @overridable
 */
Environment.prototype.platformIdentifier = function platformIdentifier() {};

/*
 *  src/system/index.js
 *  ae-client-kit-core
 *
 *  Copyright © 2018 Apple Inc. All rights reserved.
 *
 */

var system = {
    Environment: Environment
};

/*
 *  src/helpers/MetricsData.js
 *  ae-client-kit-core
 *
 *  Copyright © 2018 Apple Inc. All rights reserved.
 *
 */

/**
 * Creates and returns a MetricsData instance with APIs to store event data and create and send events to AMP Analytics
 * @delegatable
 * @constructor
 * @return {MetricsData}
 */
function MetricsData() {
    // @private
    this._eventData = {}; // this data may be unclean; it should be cleaned before being enqueued as an event
}

/**
 ************************************ PUBLIC METHODS/IVARS ************************************
 */

/**
 * Add event data to this metricsData object
 * This data may be unclean; it should be cleaned before being enqueued as an event
 * @param {Object<varargs>} callerSuppliedEventFieldsMapN a variable number of Object arguments from 0-N,
 *                          each containing key/value pairs representing event fields to include when sending the event.
 *                          Later objects take precedence over earlier ones, overriding any field value that may have already been there.
 * @overridable
 */
MetricsData.prototype.updateData = function updateData(/* callerSuppliedEventFieldsMapN(varargs) */) {
    reflect.extend.apply(null, [this._eventData].concat(Array.prototype.slice.call(arguments)));
};

/*
 *  src/helpers/index.js
 *  ae-client-kit-core
 *
 *  Copyright © 2018 Apple Inc. All rights reserved.
 *
 */

var helpers = {
    MetricsData: MetricsData
};

export { eventHandlers, helpers, system };
