import * as metricsLocation from "./location";
import * as metricsUtil from "./util";
import { isNothing, isSome } from "@jet/environment/types/optional";
import * as serverData from "../../../foundation/json-parsing/server-data";
import { attributeAsDictionary } from "../../../foundation/media/attributes";
import { shallowCopyOf } from "../../../foundation/util/objects";
import { productVariantDataForData, productVariantDataHasVariant } from "../../product-page/product-page-variants";
import { EventLinter } from "../event-linter";
import { eligibleSlotPositionsForAdPlacement } from "../../ads/ad-common";
import { FlattenedTodayItemType } from "../../today/today-parse-util";
import { getSelectedCustomCreativeId } from "../../search/custom-creative";
export const iAdURLParameterStringToken = "X-AppStore-iAdClickToken";
export const iAdURLLineItemParameterStringToken = "X-AppStore-iAdLineItem";
export const iAdDismissAdActionMetricsParameterStringToken = "X-AppStore-iAdDismissAdActionMetrics";
// User defined type guard for determining if an object conforms to ContentMetricsOptions interface.
export function isContentMetricsOptions(object) {
    return object && Object.prototype.hasOwnProperty.call(object, "id");
}
export class IAdSearchInformation {
    /**
     * Initialise a new `IAdSearchInformation`
     * @param objectGraph The Object Graph.
     * @param placementType The placement type for the ad this object is tracking.
     * @param baseSlotInformation The initial list of slotInfos for this ad placement
     * @param iAdId The unique id for the ad instance.
     * @param appStoreClientRequestId The unique id for the client requesting the ad.
     * @param wasOdmlSuccessful Whether native ODML processing was successful.
     * @param positionInfo The position info data describing the requested position of this ad.
     */
    constructor(objectGraph, placementType, baseSlotInformation, iAdId, appStoreClientRequestId, wasOdmlSuccessful, positionInfo) {
        this.placementType = placementType;
        this.placementId = placementType === null ? null : this.placementIdFromType(placementType);
        this.pageFields = {};
        this.clickFields = {};
        this.impressionsFields = {};
        this.fastImpressionFields = {};
        this.iAdClickEventFields = {};
        this._iAdApplied = false;
        this._iAdAdamId = undefined;
        this.positionInfo = positionInfo;
        this.slotInfo = baseSlotInformation;
        this.setInitialAdData(objectGraph, iAdId, appStoreClientRequestId);
        if (serverData.isDefinedNonNull(wasOdmlSuccessful)) {
            this.pageFields["iAdOdmlSuccess"] = wasOdmlSuccessful;
        }
        this.fastImpressionFields["iAdEligible"] = true;
    }
    /**
     * Construct an IAdSearchInformation from the given JSON representation.
     * This is necessary over and above standard JSON parsing to preserve our ability to call functions on this object.
     * @param objectGraph The Object Graph.
     * @param json The JSON representation of the object.
     * @returns A constructed IAdSearchInformation from the JSON.
     */
    static from(objectGraph, json) {
        var _a, _b, _c, _d;
        const iAdInfo = new IAdSearchInformation(objectGraph, serverData.asString(json.placementType), serverData.asArrayOrEmpty(json.slotInfo), (_a = serverData.asString(json.iAdId)) !== null && _a !== void 0 ? _a : undefined, (_b = serverData.asString(json.appStoreClientRequestId)) !== null && _b !== void 0 ? _b : undefined, (_c = serverData.asBoolean(json.wasOdmlSuccessful)) !== null && _c !== void 0 ? _c : undefined, serverData.asInterface(json.positionInfo));
        iAdInfo._iAdApplied = serverData.asBooleanOrFalse(json._iAdApplied);
        iAdInfo._iAdAdamId = (_d = serverData.asString(json._iAdAdamId)) !== null && _d !== void 0 ? _d : undefined;
        Object.assign(iAdInfo.pageFields, json.pageFields);
        Object.assign(iAdInfo.clickFields, json.clickFields);
        Object.assign(iAdInfo.impressionsFields, json.impressionsFields);
        Object.assign(iAdInfo.fastImpressionFields, json.fastImpressionFields);
        Object.assign(iAdInfo.iAdClickEventFields, json.iAdClickEventFields);
        iAdInfo.updateContainerId(serverData.asString(json.containerId));
        return iAdInfo;
    }
    /**
     * Create an array of `IAdSlotInformation` objects based on the current ad information available.
     * This isn't ideal, but we need to understand the available ad slots so we can report
     * on all slots, whether they have ads in them or not.
     */
    static createInitialSlotInfos(objectGraph, placementType, positionInfo, flattenedTodayFeed) {
        var _a;
        switch (placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                const productPageContainerId = IAdSearchInformation.containerIdFromType(placementType);
                const slotIndex = (_a = positionInfo === null || positionInfo === void 0 ? void 0 : positionInfo.slot) !== null && _a !== void 0 ? _a : 0;
                const productPageSlot = {
                    slotId: `${productPageContainerId}_${slotIndex}`,
                    slotIndex: slotIndex,
                    hasAdData: false,
                };
                return [
                    {
                        containerId: productPageContainerId,
                        slots: [productPageSlot],
                    },
                ];
                break;
            case "today":
                const placementEligibleSlotPositions = eligibleSlotPositionsForAdPlacement(objectGraph, placementType);
                const eligibleAdSlots = isNothing(placementEligibleSlotPositions)
                    ? []
                    : placementEligibleSlotPositions.map((slotPosition) => slotPosition.slot);
                const containerSlotInfoMapping = {};
                /// iAd provides slots that are not zero based, adjust the slot
                const adjustedAdSlot = isSome(positionInfo) ? positionInfo.slot - 1 : null;
                let adPositionEncountered = false;
                eligibleAdSlots.forEach((eligibleIndex) => {
                    var _a;
                    // If we've reached the ad we need to subtract 1 from the index, to act as if the ad ad was
                    // actually part of the feed.
                    const augmentedFeedIndexIndex = adPositionEncountered ? eligibleIndex - 1 : eligibleIndex;
                    const todayItem = flattenedTodayFeed === null || flattenedTodayFeed === void 0 ? void 0 : flattenedTodayFeed.find((item) => item.containedAdSlots.includes(augmentedFeedIndexIndex));
                    const isAdSlotIndex = adjustedAdSlot === eligibleIndex;
                    const todayContainerId = iAdContainerIdForSlotInTodayItem(augmentedFeedIndexIndex, isAdSlotIndex, todayItem);
                    const containerSlotInformation = (_a = containerSlotInfoMapping[todayContainerId]) !== null && _a !== void 0 ? _a : {
                        containerId: todayContainerId,
                        slots: [],
                    };
                    containerSlotInfoMapping[todayContainerId] = containerSlotInformation;
                    const todaySlot = {
                        slotId: `${todayContainerId}_${eligibleIndex}`,
                        slotIndex: eligibleIndex,
                        hasAdData: false,
                    };
                    containerSlotInformation.slots.push(todaySlot);
                    adPositionEncountered = adPositionEncountered || isAdSlotIndex;
                });
                return Object.values(containerSlotInfoMapping);
            default:
                return null;
        }
    }
    get iAdIsPresent() {
        return this._iAdApplied;
    }
    get iAdAdamId() {
        return this._iAdAdamId;
    }
    /**
     * Update our information with a new ad response.
     * This is primarily used for asynchronous ad requests, where we need to update the metrics info subsequent to the initial page load.
     * @param objectGraph The Object Graph.
     * @param adResponse An ad response to use to update some fields.
     */
    updateForAdResponse(objectGraph, adResponse) {
        var _a;
        if (serverData.isNull(adResponse)) {
            return;
        }
        this.placementType = adResponse.placementType;
        this.placementId = this.placementIdFromType(this.placementType);
        this.positionInfo = (_a = adResponse.onDeviceAd) === null || _a === void 0 ? void 0 : _a.positionInfo;
        this.setInitialAdData(objectGraph, adResponse.iAdId, adResponse.clientRequestId);
    }
    /**
     * Set the initial ad data, if available.
     * This function requires that an ad fetch has been made, and can be called from two paths:
     * 1. Via the constructor, where the ad fetch is made at page load time, or
     * 2. Via `updateForAdResponse`, where the ad fetch was made asynchronously after the page load.
     * @param objectGraph The Object Graph.
     * @param iAdId The unique id provided for the ad instance.
     * @param appStoreClientRequestId The unique id representing the App Store ad request client.
     */
    setInitialAdData(objectGraph, iAdId, appStoreClientRequestId) {
        if (isNothing(appStoreClientRequestId)) {
            return;
        }
        // ToroID Suppression expects this to be replaced with -1 for figaro events
        const iAdIdValue = isNothing(iAdId) ? "-1" : iAdId;
        this.pageFields[EventLinter.hasIAdData] = true;
        switch (this.placementType) {
            case "today":
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                // Only set `hasIAdData` on impressions fields initially if it's a Chainlink placement.
                // This covers the "ad eligible" cases where we still want to check ad metrics when ads are not present.
                this.impressionsFields[EventLinter.hasIAdData] = true;
                break;
            default:
                break;
        }
        this.pageFields["iAdAppStoreClientRequestId"] = appStoreClientRequestId;
        switch (this.placementType) {
            case "today":
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                this.clickFields["iAdAppStoreClientRequestId"] = appStoreClientRequestId;
                this.impressionsFields["iAdAppStoreClientRequestId"] = appStoreClientRequestId;
                break;
            default:
                break;
        }
        this.pageFields["iAdId"] = iAdIdValue;
        this.impressionsFields["iAdId"] = iAdIdValue;
        this.clickFields["iAdId"] = iAdIdValue;
        this.updateContainerId(null);
        // Update slot info with data
        this.updateSlotInfo();
        if (serverData.isDefinedNonNullNonEmpty(this.slotInfo)) {
            this.pageFields["iAdSlotInfo"] = this.slotInfo;
            this.clickFields["iAdSlotInfo"] = this.slotInfo;
            this.impressionsFields["iAdSlotInfo"] = this.slotInfo;
        }
        if (this.placementId !== null && this.placementId.length > 0) {
            this.pageFields["iAdPlacementId"] = this.placementId;
            this.clickFields["iAdPlacementId"] = this.placementId;
            // Attach the `iAdPlacementId` to top-level impressions for v4 impressions.
            this.impressionsFields["iAdPlacementId"] = this.placementId;
            // For Chainlink placements, the `iAdPlacementId` sits within the impressions array.
            // We manually remove `iAdPlacementId` from the top-level fields for v5 impressions in `createMetricsFastImpressionsData`.
            switch (this.placementType) {
                case "today":
                case "productPageYMAL":
                case "productPageYMALDuringDownload":
                    this.fastImpressionFields["iAdPlacementId"] = this.placementId;
                    break;
                default:
                    break;
            }
        }
    }
    /**
     * Update the containerId based on the current placementType.
     */
    updateContainerId(containerId) {
        if (this.placementType === "today") {
            this.containerId = containerId !== null && containerId !== void 0 ? containerId : null;
            if (serverData.isDefinedNonNull(this.containerId)) {
                this.clickFields["iAdContainerId"] = this.containerId;
                this.fastImpressionFields["iAdContainerId"] = this.containerId;
            }
        }
        else {
            this.containerId =
                this.placementType === null ? null : IAdSearchInformation.containerIdFromType(this.placementType);
            if (serverData.isDefinedNonNull(this.containerId)) {
                this.pageFields["iAdContainerId"] = this.containerId;
                this.clickFields["iAdContainerId"] = this.containerId;
                this.fastImpressionFields["iAdContainerId"] = this.containerId;
            }
        }
    }
    /**
     * @param slotIndex The slot index to look for a container Id for
     * @returns The container Id for the given slot index, based off the slot infos
     */
    containerIdForSlotIndex(slotIndex) {
        if (isNothing(slotIndex) || isNothing(this.slotInfo)) {
            return null;
        }
        for (const slotInfo of this.slotInfo) {
            for (const slot of slotInfo.slots) {
                if (slot.slotIndex === slotIndex) {
                    return slotInfo.containerId;
                }
            }
        }
        return this.containerId;
    }
    apply(objectGraph, adData) {
        if (isNothing(adData) || serverData.isNullOrEmpty(adData)) {
            return;
        }
        const iAdAdamId = adData.id;
        const iAdConfigurationDictionary = attributeAsDictionary(adData, "iad");
        this._iAdAdamId = iAdAdamId;
        if (iAdConfigurationDictionary) {
            this.impressionsFields[EventLinter.hasIAdData] = true;
            this.clickFields[EventLinter.hasIAdData] = true;
            const impressionId = metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["impressionId"]);
            this.fastImpressionFields["iAdImpressionId"] = impressionId;
            this.clickFields["iAdImpressionId"] = impressionId;
            const metadata = metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["metadata"]);
            this.clickFields["iAdMetadata"] = metadata;
            this.fastImpressionFields["iAdMetadata"] = metadata;
            // Ads boldly populate `adamId` on pages. This is correct.
            this.pageFields["adamId"] = iAdAdamId;
            this.pageFields["iAd"] = {
                iAdFormat: metricsUtil.sanitizedMetricsDictionary(serverData.asInterface(serverData.asJSONValue(iAdConfigurationDictionary), "format")),
                iAdAlgoId: metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["algoId"]),
                iAdImpressionId: metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["impressionId"]),
                iAdMetadata: metricsUtil.emptyStringIfNullOrUndefined(iAdConfigurationDictionary["metadata"]),
            };
            const productVariantData = productVariantDataForData(objectGraph, adData);
            this.updateIAdMetricsFieldsForProductVariantData(productVariantData, this.clickFields);
            if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
                if (objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
                    const selectedCustomCreativeId = getSelectedCustomCreativeId(adData);
                    if (this.placementType === "today") {
                        this.updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, this.fastImpressionFields);
                    }
                    else {
                        this.updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, this.impressionsFields);
                    }
                    this.updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, this.clickFields);
                }
            }
            Object.assign(this.iAdClickEventFields, iAdConfigurationDictionary);
            this._iAdApplied = true;
            // Clear out any prior missed opportunity reason if we have an ad.
            this.setMissedOpportunity(objectGraph, undefined, this.placementType);
        }
        // Update slot info after ad is applied.
        this.updateSlotInfo();
        if (serverData.isDefinedNonNullNonEmpty(this.slotInfo)) {
            this.pageFields["iAdSlotInfo"] = this.slotInfo;
            this.clickFields["iAdSlotInfo"] = this.slotInfo;
            this.impressionsFields["iAdSlotInfo"] = this.slotInfo;
        }
    }
    /**
     * Apply the click fields that were attached to the page request we're following.
     * This will generally be following on from click on a lockup with ad data attached, where we want to pull
     * some of the click fields from ad to be applied to this new page data.
     *
     * The goal here is to attach enough metadata into the incoming page that we can attribute any offer action
     * to the ad that was tapped on to reach this page. Whilst doing this we don't want to attach too much
     * ad data to the page, as this can start to interfere with metrics for other ad placements. This results in
     * us manually inserting and removing fields to achieve the right balance.
     * @param iAdAdamId The adamId for the ad.
     * @param fields The set of fields to apply to our clickFields.
     */
    applyClickFieldsFromPageRequest(iAdAdamId, fields) {
        this._iAdApplied = true;
        this._iAdAdamId = iAdAdamId;
        Object.assign(this.clickFields, fields);
        // We don't want to assign any of the fields to the page event, as this looks like an ad is being shown
        // on the page we're navigating to.
        Object.keys(this.pageFields).forEach((field) => delete this.pageFields[field]);
    }
    setSpecifiedAlignedRegionUsed(didUseSpecifiedCreative) {
        this.fastImpressionFields["iAdIsSpecifiedCreativeUsed"] = didUseSpecifiedCreative;
        this.clickFields["iAdIsSpecifiedCreativeUsed"] = didUseSpecifiedCreative;
    }
    /**
     * Set the template type for the page.
     */
    setTemplateType(templateType) {
        this.pageFields["iAdTemplateType"] = templateType;
        this.impressionsFields["iAdTemplateType"] = templateType;
        this.clickFields["iAdTemplateType"] = templateType;
    }
    setMissedOpportunity(objectGraph, reason, placementType) {
        this.missedOpportunityReason = reason;
        // Only set in page and impressions fields if reason is non-null.
        if (serverData.isDefinedNonNull(reason)) {
            this.clickFields["iAdMissedOpportunityReason"] = reason;
            // Chainlink placements don't expect "iAdMissedOpportunityReason" at the top level of impressions or page events.
            switch (this.placementType) {
                case "today":
                case "productPageYMAL":
                case "productPageYMALDuringDownload":
                    break;
                default:
                    this.pageFields["iAdMissedOpportunityReason"] = reason;
                    this.impressionsFields["iAdMissedOpportunityReason"] = reason;
                    break;
            }
        }
        else {
            delete this.clickFields["iAdMissedOpportunityReason"];
            // Generally, we just want to remove the missed opportunity reason if one isn't set.
            // The 'productPageYMALDuringDownload' placement is a special case where it's placed on the page
            // subsequent to a previous placement, and metrics is updated via the pageChange metrics.
            // This means that we need to send `null` as the value for this placement, in order to clear
            // any possible missed opportunity value from the placement it's replacing.
            switch (placementType) {
                case "productPageYMALDuringDownload":
                    this.pageFields["iAdMissedOpportunityReason"] = null;
                    this.impressionsFields["iAdMissedOpportunityReason"] = null;
                    break;
                default:
                    delete this.pageFields["iAdMissedOpportunityReason"];
                    delete this.impressionsFields["iAdMissedOpportunityReason"];
                    break;
            }
        }
        // Only set in page and impressions fields if reason is non-null.
        if (serverData.isDefinedNonNull(reason)) {
            this.pageFields["iAdMissedOpportunityReason"] = reason;
            this.impressionsFields["iAdMissedOpportunityReason"] = reason;
        }
        else {
            delete this.pageFields["iAdMissedOpportunityReason"];
            delete this.impressionsFields["iAdMissedOpportunityReason"];
        }
        // Update slotInfo with missed opportunity reason.
        this.updateSlotInfo();
        if (serverData.isDefinedNonNullNonEmpty(this.slotInfo)) {
            this.pageFields["iAdSlotInfo"] = this.slotInfo;
            this.clickFields["iAdSlotInfo"] = this.slotInfo;
            this.impressionsFields["iAdSlotInfo"] = this.slotInfo;
        }
    }
    placementIdFromType(type) {
        switch (type) {
            case "searchLanding":
                return "APPSTORE_SEARCH_LANDING_PAGE";
            case "searchResults":
                return "APPSTORE_SEARCH_RESULT_PAGE";
            case "today":
                return "APPSTORE_TODAY_TAB";
            case "productPageYMAL":
                return "APPSTORE_PRODUCT_PAGE";
            case "productPageYMALDuringDownload":
                return "APPSTORE_PRODUCT_PAGE_DOWNLOAD";
            default:
                throw new Error(`This method should never be called with value: ${type}`);
        }
    }
    static placementTypeFromPlacementId(objectGraph, id) {
        switch (id) {
            case "APPSTORE_SEARCH_LANDING_PAGE":
                return "searchLanding";
            case "APPSTORE_SEARCH_RESULT_PAGE":
                return "searchResults";
            case "APPSTORE_TODAY_TAB":
                return "today";
            case "APPSTORE_PRODUCT_PAGE":
                return "productPageYMAL";
            case "APPSTORE_PRODUCT_PAGE_DOWNLOAD":
                return "productPageYMALDuringDownload";
            default:
                objectGraph.console.log(`Failed to get placementType from placementId ${id}. Defaulting to searchResults`);
                // For legacy reasons we fall back to `seachResults` when nothing is provided.
                // Being the first ad slot, in the past this has been assumed as the "default".
                return "searchResults";
        }
    }
    /**
     * Get a containerId value for a given placement type to attach to the ad's metrics.
     * @param type The AdPlacementType for the current ad.
     * @returns A string value for containerId, if the placement has one. Otherwise null.
     */
    static containerIdFromType(type) {
        switch (type) {
            case "productPageYMAL":
                return "customers-also-bought-apps";
            case "productPageYMALDuringDownload":
                return "customers-also-bought-apps-download";
            case "today":
                return null; // Today page has variable containerIds that are updated programatically
            default:
                return null;
        }
    }
    /**
     * Return the fast impressions metrics fields for an item using the location tracker.
     *
     * `fastImpressionFields` are the items inside the impressions array of metrics events. Although some fields are stable,
     * these are unique to a given position, so we must use that position information to generate the right fields.
     *
     * @param locationTracker A LocationTracker used to identify the relevant information to attach to the metrics fields.
     * @param adSlotOverride The ad slot to use for the metrics fields. If not provided, the ad slot will be inferred from
     * the location tracker. This is needed for today where the ad can span different shelves so the position is going to be 0 for multiple ads
     * @returns Relevant metrics fields for the item in an impressions array.
     */
    fastImpressionsFieldsForCurrentItem(locationTracker, adSlotOverride) {
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
            case "today":
                let position;
                if (isSome(adSlotOverride)) {
                    position = adSlotOverride;
                }
                else {
                    const location = metricsLocation.currentLocation(locationTracker);
                    // If the current location is within a todayCard, we want to extract the location of the card, not the location
                    // of this lockup within the card.
                    if (location !== null && location.locationType === "todayCard") {
                        position = metricsLocation.previousPosition(locationTracker);
                    }
                    else {
                        position = metricsLocation.currentPosition(locationTracker);
                    }
                }
                const sharedFields = shallowCopyOf(this.fastImpressionFields);
                sharedFields["iAdSlotId"] = `${this.containerIdForSlotIndex(position)}_${position}`;
                if (position !== this.adjustedSlotIndex) {
                    // If the current position is not where the ad is, we only want a subset of the tracked fields.
                    const allowedFields = ["iAdEligible", "iAdPlacementId", "iAdContainerId", "iAdSlotId"];
                    Object.keys(sharedFields).forEach((key) => {
                        if (!allowedFields.includes(key)) {
                            delete sharedFields[key];
                        }
                    });
                }
                return sharedFields;
            default:
                return this.fastImpressionFields;
        }
    }
    /**
     * Get the adjusted slot index of the ad we're tracking.
     * This is zero-based - Ad Platforms returns slot info to us one-based but we always adjust it before using it anywhere.
     */
    get adjustedSlotIndex() {
        var _a;
        const positionInfoSlotIndex = (_a = this.positionInfo) === null || _a === void 0 ? void 0 : _a.slot;
        // The slot as provided by ad platforms is one-based - adjust it so we're working with zero-based numbers.
        if (serverData.isDefinedNonNull(positionInfoSlotIndex)) {
            return positionInfoSlotIndex - 1;
        }
        return null;
    }
    /**
     * Update the existing slot information now that new data has been applied.
     */
    updateSlotInfo() {
        if (isNothing(this.slotInfo)) {
            return;
        }
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
                for (const containerSlotInfo of this.slotInfo) {
                    for (const slot of containerSlotInfo.slots) {
                        slot.hasAdData = this.iAdIsPresent;
                        if (serverData.isDefinedNonNull(this.missedOpportunityReason)) {
                            slot.missedOpportunityReason = this.missedOpportunityReason;
                        }
                    }
                }
                break;
            case "today":
                const adjustedSlotIndex = this.adjustedSlotIndex;
                for (const containerSlotInfo of this.slotInfo) {
                    for (const slot of containerSlotInfo.slots) {
                        // Check whether the current index matches our slot information.
                        // If so, an ad is intended to be placed here.
                        const isAdInCurrentIndex = adjustedSlotIndex === slot.slotIndex;
                        // In order to understand whether a given slot has ad data, we ensure that a usable ad is present,
                        // and the current slot is the one an ad is placed in.
                        const hasAdData = this.iAdIsPresent && isAdInCurrentIndex;
                        // By default, use the missed opportunity reason, if any, stored here.
                        let missedOpportunityReason = this.missedOpportunityReason;
                        // If:
                        // - the ad is not in the current index we're building slot info for, and
                        // - there is a valid slot index
                        // we override the reason to 'NOAD', as it means another eligible slot was filled.
                        if (!isAdInCurrentIndex && serverData.isDefinedNonNull(adjustedSlotIndex)) {
                            missedOpportunityReason = "NOAD";
                        }
                        slot.hasAdData = hasAdData;
                        if (serverData.isDefinedNonNull(missedOpportunityReason)) {
                            slot.missedOpportunityReason = missedOpportunityReason;
                        }
                    }
                }
                break;
            default:
                break;
        }
    }
    /**
     * Modifies the provided MetricsFields for the ProductVariantData specific to fields required for iAds.
     * @param productVariantData The variant data of impressionable data to apply.
     * @param metricsFields An existing set of MetricsFields to modify.
     */
    updateIAdMetricsFieldsForProductVariantData(productVariantData, metricsFields) {
        if (isSome(productVariantData) && productVariantDataHasVariant(productVariantData, "customProductPage")) {
            metricsFields["iAdPageCustomId"] = productVariantData.productPageId;
        }
        else {
            // If a custom product page doesn't exist, we remove the fields.
            delete metricsFields["iAdPageCustomId"];
        }
    }
    /**
     * Modifies the provided MetricsFields to include the custom id for iAds.
     * @param selectedCustomCreativeId The id for the custom creative that we want to set as custom id.
     * @param metricsFields An existing set of MetricsFields to modify.
     */
    updateIAdMetricsFieldsForAlignedRegion(selectedCustomCreativeId, metricsFields) {
        if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
            if (isSome(selectedCustomCreativeId)) {
                metricsFields["iAdCustomId"] = selectedCustomCreativeId;
            }
            else {
                delete metricsFields["iAdCustomId"];
            }
        }
    }
    /**
     * Return the event version for fast impressions based on the placement of the ad.
     * Impressions v5 is being rolled out first for Chainlink. Once all impressions have adopted it, this can be removed.
     */
    get fastImpressionsEventVersion() {
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
            case "today":
                return 5;
            default:
                return 4;
        }
    }
    /**
     * Return whether ad rotation fields should be included on metrics events, based on the current placement type.
     */
    get shouldIncludeAdRotationFields() {
        switch (this.placementType) {
            case "productPageYMAL":
            case "productPageYMALDuringDownload":
            case "today":
                return false;
            case "searchLanding":
            case "searchResults":
                return true;
            default:
                return true;
        }
    }
}
/**
 * Return the container id to use for a today feed item
 * @param slotIndex The slot index that we found a TodayItem for
 * @param isAdSlot Whether this slotIndex is the slot the ad is in
 * @param todayItem The today item that this card is contained in, this could be a single item or a story group item
 * @returns The configuration to use when parsing a today card
 */
export function iAdContainerIdForSlotInTodayItem(slotIndex, isAdSlot, todayItem) {
    if (isNothing(todayItem)) {
        return "0";
    }
    switch (todayItem.type) {
        case FlattenedTodayItemType.EditorialItemGroup:
            const storyGroupHasMultipleItems = todayItem.containedAdSlots.length > 1;
            const slotIndexIsFirstOrLastInStoryGroup = !storyGroupHasMultipleItems ||
                slotIndex === todayItem.containedAdSlots[0] ||
                slotIndex === todayItem.containedAdSlots[todayItem.containedAdSlots.length - 1];
            if (isAdSlot && slotIndexIsFirstOrLastInStoryGroup) {
                // If the story group has fewer than 2 items, that means theres not valid slot **in** the actual story group
                // to place the ad, so its going to be at the top level. Or if there are multiple items, but the slot is the
                // first or last item there is no reason to pull the ad into the story group, it should be top level.
                return "0";
            }
            else {
                // Using the `data` from the todayItem here and not the todayCardData, because
                // this will be the data for the containing story gorup
                return "0";
            }
        default:
            return "0";
    }
}
/**
 * Create the iAdInformation for a given page if necessary
 * @param objectGraph The dependency graph for the app store
 * @param pageId The id of the page this iAd info is being used for
 * @param response The MAPI response for this page
 * @param positionInfo The position information on where the ad will attempted to be inserted
 * @param flattenedTodayFeed The flattened to today feed if this is for a today page
 * @returns The iAdInformation to be used in a metrics page information object
 */
export function iAdInformationFromMediaApiResponse(objectGraph, pageId, response, positionInfo = null, flattenedTodayFeed = null) {
    var _a;
    /** <rdar://problem/33764430> Toro: iAd is missing AD_OPEN figaro event when tapping on ad and pressing OPEN through the product page */
    const iAdClickInfoString = serverData.asString(response, iAdURLParameterStringToken);
    if (isNothing(iAdClickInfoString)) {
        return null;
    }
    const iAdClickInfo = JSON.parse(iAdClickInfoString);
    // rdar://72607206 (Gibraltar: Tech Debt: Population of iAd fields in Product Page)
    const placementType = IAdSearchInformation.placementTypeFromPlacementId(objectGraph, serverData.asString(iAdClickInfo, "iAdPlacementId"));
    const iAdInfo = new IAdSearchInformation(objectGraph, placementType, IAdSearchInformation.createInitialSlotInfos(objectGraph, placementType, positionInfo, flattenedTodayFeed), (_a = serverData.asString(iAdClickInfo, "iAdId")) !== null && _a !== void 0 ? _a : undefined, undefined, undefined, positionInfo);
    iAdInfo.applyClickFieldsFromPageRequest(pageId !== null && pageId !== void 0 ? pageId : undefined, iAdClickInfo);
    return iAdInfo;
}
/** @public */
export class MetricsPageInformation {
    constructor(base = {}) {
        this.baseFields = base;
    }
}
//# sourceMappingURL=models.js.map