/*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

import { DataBoundary } from "../../account/AccountInfo.js";

/**
 * State of the performance event.
 *
 * @export
 * @enum {number}
 */
export const PerformanceEventStatus = {
    NotStarted: 0,
    InProgress: 1,
    Completed: 2,
} as const;
export type PerformanceEventStatus =
    (typeof PerformanceEventStatus)[keyof typeof PerformanceEventStatus];

export type SubMeasurement = {
    name: string;
    startTimeMs: number;
};

/**
 * Performance measurement taken by the library, including metadata about the request and application.
 *
 * @export
 * @typedef {PerformanceEvent}
 */
export type PerformanceEvent = {
    /**
     * Unique id for the event
     *
     * @type {string}
     */
    eventId: string;

    /**
     * State of the perforance measure.
     *
     * @type {PerformanceEventStatus}
     */
    status: PerformanceEventStatus;

    /**
     * Login authority used for the request
     *
     * @type {string}
     */
    authority: string;

    /**
     * Client id for the application
     *
     * @type {string}
     */
    clientId: string;

    /**
     * Correlation ID used for the request
     *
     * @type {string}
     */
    correlationId: string;

    /**
     * End-to-end duration in milliseconds.
     * @date 3/22/2022 - 3:40:05 PM
     *
     * @type {number}
     */
    durationMs?: number;

    /**
     * Visibility of the page when the event completed.
     * Read from: https://developer.mozilla.org/docs/Web/API/Page_Visibility_API
     *
     * @type {?(string | null)}
     */
    endPageVisibility?: string | null;

    /**
     * Whether the result was retrieved from the cache.
     *
     * @type {(boolean | null)}
     */
    fromCache?: boolean | null;

    /**
     * Event name (usually in the form of classNameFunctionName)
     *
     * @type {string}
     */
    name: string;

    /**
     * Visibility of the page when the event completed.
     * Read from: https://developer.mozilla.org/docs/Web/API/Page_Visibility_API
     *
     * @type {?(string | null)}
     */
    startPageVisibility?: string | null;

    /**
     * Online status when the event started.
     * Read from: https://developer.mozilla.org/docs/Web/API/NavigatorOnLine/onLine
     */
    startOnlineStatus?: boolean | null;

    /**
     * Unix millisecond timestamp when the event was initiated.
     *
     * @type {number}
     */
    startTimeMs: number;

    /**
     * Whether or the operation completed successfully.
     *
     * @type {(boolean | null)}
     */
    success?: boolean | null;

    /**
     * Add specific error code in case of failure
     *
     * @type {string}
     */
    errorCode?: string;

    /**
     * Add specific sub error code in case of failure
     *
     * @type {string}
     */
    subErrorCode?: string;

    /**
     * Server error number
     */
    serverErrorNo?: string;

    /**
     * Server sub error number
     */
    serverSubErrorNo?: string;

    /**
     * Name of the library used for the operation.
     *
     * @type {string}
     */
    libraryName: string;

    /**
     * Version of the library used for the operation.
     *
     * @type {string}
     */
    libraryVersion: string;

    /**
     * Version of the library used last. Used to track upgrades and downgrades
     */
    previousLibraryVersion?: string;

    /**
     * Whether the response is from a native component (e.g., WAM)
     *
     * @type {?boolean}
     */
    isNativeBroker?: boolean;

    /**
     * Platform-specific fields, when calling STS and/or broker for token requests
     */
    isPlatformAuthorizeRequest?: boolean;
    isPlatformBrokerRequest?: boolean;
    brokerErrorName?: string;
    brokerErrorCode?: string;

    /**
     * Request ID returned from the response
     *
     * @type {?string}
     */
    requestId?: string;

    /**
     * Cache lookup policy
     *
     * @type {?number}
     */
    cacheLookupPolicy?: number | undefined;

    /**
     * Cache Outcome
     * @type {?number}
     */
    cacheOutcome?: number;

    /**
     * Sub-measurements for internal use. To be deleted before flushing.
     */
    incompleteSubMeasurements?: Map<string, SubMeasurement>;

    visibilityChangeCount?: number;
    onlineStatusChangeCount?: number;
    incompleteSubsCount?: number;

    /**
     * Network connection info from the Network Information API (Chromium only).
     * Read from: https://developer.mozilla.org/docs/Web/API/NetworkInformation
     */
    networkEffectiveType?: string;
    networkRtt?: number;

    /**
     * CorrelationId of the in progress iframe request that was awaited
     */
    awaitIframeCorrelationId?: string;
    /**
     * Monitor_window_timeout debugging telemetry
     */
    redirectBridgeTimeoutMs?: number;
    isRedirectUriCrossOrigin?: boolean;
    redirectBridgeMessageVersion?: number;
    lateResponseExperimentEnabled?: boolean;

    /**
     * Size of the id token
     *
     * @type {number}
     */
    idTokenSize?: number;

    /**
     *
     * Size of the access token
     *
     * @type {number}
     */

    accessTokenSize?: number;

    /**
     *
     * Size of the refresh token
     *
     * @type {number}
     */

    refreshTokenSize?: number | undefined;

    /**
     * Application name as specified by the app.
     *
     * @type {?string}
     */
    appName?: string;

    /**
     * Application version as specified by the app.
     *
     * @type {?string}
     */
    appVersion?: string;

    /**
     * The following are fields that may be emitted in native broker scenarios
     */
    extensionId?: string;
    extensionVersion?: string;
    matsBrokerVersion?: string;
    matsAccountJoinOnStart?: string;
    matsAccountJoinOnEnd?: string;
    matsDeviceJoin?: string;
    matsPromptBehavior?: string;
    matsApiErrorCode?: number;
    matsUiVisible?: boolean;
    matsSilentCode?: number;
    matsSilentBiSubCode?: number;
    matsSilentMessage?: string;
    matsSilentStatus?: number;
    matsHttpStatus?: number;
    matsHttpEventCount?: number;

    /**
     * Http POST metadata
     */
    httpVerToken?: string;
    httpStatus?: number;
    contentTypeHeader?: string;
    contentLengthHeader?: string;

    /**
     * Platform broker fields
     */
    allowPlatformBroker?: boolean;
    extensionInstalled?: boolean;
    extensionHandshakeTimeoutMs?: number;
    extensionHandshakeTimedOut?: boolean;

    /**
     * Nested App Auth Fields
     */
    nestedAppAuthRequest?: boolean;

    /**
     * Multiple matched access/id/refresh tokens in the cache
     */
    multiMatchedAT?: number;
    multiMatchedID?: number;
    multiMatchedRT?: number;

    errorName?: string;
    errorStack?: string[];

    // Event context as JSON string
    context?: string;

    // Cache Data
    cacheLocation?: string;
    cacheRetentionDays?: number;
    accountCachedBy?: string;
    acntLoggedOut?: boolean;

    // Number of cached accounts matched by homeAccountId in buildAccountToCache
    cacheMatchedAccounts?: number;

    // Number of tokens in the cache to be reported when cache quota is exceeded
    cacheRtCount?: number;
    cacheIdCount?: number;
    cacheAtCount?: number;

    // Scenario id to track custom user prompts
    scenarioId?: string;

    accountType?: "AAD" | "MSA" | "B2C";

    /**
     * Server error that triggers a request retry
     *
     * @type {string}
     */
    retryError?: string;

    embeddedClientId?: string;
    embeddedRedirectUri?: string;

    isAsyncPopup?: boolean;

    cacheRtExpiresOnSeconds?: number;
    ntwkRtExpiresOnSeconds?: number;
    extRtExpiresOnSeconds?: number;
    rtOffsetSeconds?: number;

    sidFromClaim?: boolean;
    // Backward-compatible alias for sidFromClaim
    sidFromClaims?: boolean;
    sidFromRequest?: boolean;
    loginHintFromRequest?: boolean;
    loginHintFromUpn?: boolean;
    loginHintFromClaim?: boolean;
    domainHintFromRequest?: boolean;

    prompt?: string;

    usePreGeneratedPkce?: boolean;

    // Number of MSAL JS instances in the frame
    msalInstanceCount?: number;
    // Number of MSAL JS instances using the same client id in the frame
    sameClientIdInstanceCount?: number;

    navigateCallbackResult?: boolean;

    dataBoundary?: DataBoundary;

    // Hashed logs in the format [millis1,hash1;millis2,hash2;...]
    logs?: string;

    // Whether the application is configured for MCP flows
    isMcp?: boolean;

    /**
     * Source of cloud discovery metadata (config, cache, network, hardcoded_values)
     */
    cloudDiscoverySource?: string;

    /**
     * Source of authority endpoint metadata (config, cache, network, hardcoded_values)
     */
    authorityEndpointSource?: string;

    /**
     * Number of accounts removed during cache cleanup
     */
    accountsRemoved?: number;

    /**
     * Number of access tokens removed during cache cleanup
     */
    accessTokensRemoved?: number;

    /**
     * Number of failures when removing token binding keys
     */
    removeTokenBindingKeyFailure?: number;

    /**
     * Reason for silent refresh fallback to iframe
     * Format: errorCode or errorCode|subError
     *
     * @type {?string}
     */
    silentRefreshReason?: string;

    /**
     * Whether this request was deduped with another in-flight request
     */
    deduped?: boolean;

    /**
     * Whether the user has "Keep Me Signed In" enabled
     */
    kmsi?: boolean;

    /**
     * Cached SSO capability status from the most recent SSO verification
     */
    ssoCapable?: boolean;

    /**
     * Whether this event was executed in the background
     */
    isBackground?: boolean;

    /**
     * Cache migration telemetry — pre-migration counts
     */
    preMigrateAcntCount?: number;
    preMigrateATCount?: number;
    preMigrateITCount?: number;
    preMigrateRTCount?: number;

    /**
     * Cache migration telemetry — post-migration counts
     */
    postMigrateAcntCount?: number;
    postMigrateATCount?: number;
    postMigrateITCount?: number;
    postMigrateRTCount?: number;

    /**
     * Cache migration telemetry — old schema counts
     */
    oldAcntCount?: number;
    oldATCount?: number;
    oldITCount?: number;
    oldRTCount?: number;

    /**
     * Cache migration telemetry — skipped and migrated counts
     */
    skipATMigrateCount?: number;
    skipITMigrateCount?: number;
    skipRTMigrateCount?: number;
    migratedATCount?: number;
    migratedITCount?: number;
    migratedRTCount?: number;

    /**
     * Cache telemetry — expired, invalid, and removed counts
     */
    expiredCacheRemovedCount?: number;
    expiredAcntRemovedCount?: number;
    invalidCacheCount?: number;

    /**
     * Encrypted cache telemetry
     */
    unencryptedCacheCount?: number;
    encryptedCacheCount?: number;
    encryptedCacheExpiredCount?: number;
    encryptedCacheCorruptionCount?: number;

    /**
     * Container for dynamically-named telemetry fields.
     * Fields whose names are constructed at runtime (e.g., "[eventName]CallCount")
     * should be stored here instead of being set as top-level properties.
     * Use the "ext." prefix when calling addFields/incrementFields to automatically
     * route fields to this sub-object.
     *
     * @remarks
     * This property is typed as `Record<string, string | number>`.
     */
    ext?: Record<string, string | number>;
};

export type PerformanceEventContext = {
    dur?: number;
    err?: string;
    subErr?: string;
    fail?: number;
};

export type PerformanceEventStackedContext = PerformanceEventContext & {
    name?: string;
    childErr?: string;
};

/**
 * Prefix used to mark telemetry field names as dynamic.
 * Fields with this prefix in addFields/incrementFields calls will be routed
 * to the PerformanceEvent.ext sub-object.
 */
export const EXT_FIELD_PREFIX = "ext.";

export const IntFields: ReadonlySet<string> = new Set([
    "accessTokenSize",
    "durationMs",
    "idTokenSize",
    "matsSilentStatus",
    "matsHttpStatus",
    "refreshTokenSize",
    "startTimeMs",
    "status",
    "multiMatchedAT",
    "multiMatchedID",
    "multiMatchedRT",
    "unencryptedCacheCount",
    "encryptedCacheExpiredCount",
    "oldAccountCount",
    "oldAccessCount",
    "oldIdCount",
    "oldRefreshCount",
    "currAccountCount",
    "currAccessCount",
    "currIdCount",
    "currRefreshCount",
    "expiredCacheRemovedCount",
    "upgradedCacheCount",
    "cacheMatchedAccounts",
    "networkRtt",
    "redirectBridgeTimeoutMs",
    "redirectBridgeMessageVersion",
]);
