//
//  lockups.ts
//  AppStoreKit
//
//  Created by Jonathan Ellenbogen on 11/15/16.
//  Copyright (c) 2016 Apple Inc. All rights reserved.
//
import * as validation from "@jet/environment/json/validation";
import { isNothing, isSome } from "@jet/environment/types/optional";
import { makeBundlePageIntent } from "../../api/intents/bundle-page-intent";
import { makeEditorialPageIntentByID } from "../../api/intents/editorial/editorial-page-intent";
import { makeGroupingPageIntentByID } from "../../api/intents/grouping-page-intent";
import { makeProductPageIntent } from "../../api/intents/product-page-intent";
import { makeRoutableArticlePageIntent, } from "../../api/intents/routable-article-page-intent";
import * as models from "../../api/models";
import { AdvertActionMetrics, } from "../../api/models/metrics/advert-action-metrics";
import * as productPageUtil from "../../common/product-page/product-page-util";
import * as serverData from "../../foundation/json-parsing/server-data";
import { editorialCardFromData } from "../../foundation/media/associations";
import * as mediaAttributes from "../../foundation/media/attributes";
import * as mediaPlatformAttributes from "../../foundation/media/platform-attributes";
import { platformAttributeAsDictionary } from "../../foundation/media/platform-attributes";
import * as mediaRelationship from "../../foundation/media/relationships";
import * as mediaUrlBuilder from "../../foundation/media/url-builder";
import { BuyParameters } from "../../foundation/metrics/buy-parameters";
import * as http from "../../foundation/network/http";
import { InAppPurchaseInstallPageParameters, Parameters, Path, ProductPageParameters, Protocol, } from "../../foundation/network/url-constants";
import * as urls from "../../foundation/network/urls";
import * as color from "../../foundation/util/color-util";
import * as objects from "../../foundation/util/objects";
import * as gameModels from "../../gameservicesui/src/api/data-models/game";
import { makeClickMetrics } from "../../gameservicesui/src/utility/metrics";
import * as adCommon from "../ads/ad-common";
import * as appEvent from "../app-promotions/app-event";
import * as appPromotionsCommon from "../app-promotions/app-promotions-common";
import * as arcadeCommon from "../arcade/arcade-common";
import * as arcadeUpsell from "../arcade/arcade-upsell";
import * as mediaUrlMapping from "../builders/url-mapping";
import * as ageRatings from "../content/age-ratings";
import { createArtworkForResource } from "../content/artwork/artwork";
import * as contentAttributes from "../content/attributes";
import * as content from "../content/content";
import * as flowPreview from "../content/flow-preview";
import { isMediaDark } from "../editorial-pages/editorial-media-util";
import * as editorialComponentMediaUtil from "../editorial-pages/editorial-page-component-media-util";
import { makeEditorialPageURL } from "../editorial-pages/editorial-page-intent-controller-utils";
import * as filtering from "../filtering";
import { makeGroupingPageCanonicalURL } from "../grouping/grouping-page-url";
import * as externalDeepLink from "../linking/external-deep-link";
import { getLocale } from "../locale";
import * as metricsHelpersClicks from "../metrics/helpers/clicks";
import * as metricsHelpersImpressions from "../metrics/helpers/impressions";
import * as metricsHelpersLocation from "../metrics/helpers/location";
import * as metricsHelpersModels from "../metrics/helpers/models";
import * as metricsUtil from "../metrics/helpers/util";
import * as offerFormatting from "../offers/offer-formatting";
import * as offers from "../offers/offers";
import { getPlatform, inferPreviewPlatformFromDeviceFamilies } from "../preview-platform";
import * as variants from "../product-page/product-page-variants";
import * as reviews from "../product-page/reviews";
import { customCreativeArtworkFromData, customCreativeVideoFromData } from "../search/custom-creative";
import * as metadataRibbon from "../search/metadata-ribbon/metadata-ribbon";
import * as searchTagsRibbon from "../search/metadata-ribbon/search-tags-ribbon";
import * as sharing from "../sharing";
import { makeRoutableArticlePageCanonicalUrl } from "../today/routable-article-page-url-utils";
import * as adLockup from "./ad-lockups";
import { reportingDestinationFromMetricsOptions } from "./ad-lockups";
export const forTesting = {
    copyDataIntoLockup,
};
export function offerTypeForMediaType(objectGraph, type, supportsArcade) {
    switch (type) {
        case "in-apps":
            return supportsArcade ? "arcade" : "inAppPurchase";
        default:
            return supportsArcade ? "arcadeApp" : "app";
    }
}
function isArcadeLockupWordmarkSupported(objectGraph) {
    if (objectGraph.client.isTV && objectGraph.host.clientIdentifier === "com.apple.Arcade") {
        return false;
    }
    else {
        return true;
    }
}
/**
 * Takes the metadata ribbon items type candidates from the `SearchExperimentData` and determines which should be shown in each slot,
 * prioritizing the natural order from the candidates arrays
 * @param objectGraph The ObjectGraph instance for the App Store
 * @param data The data representing the mixed media lockup data
 * @param lockup The lockup object the metadata object will be added to
 * @param searchExperimentData The experiment data for the search experiments, to use its displayStyle object
 */
function copyMetadataRibbonInfoIntoLockup(objectGraph, data, lockup, searchExperimentDataForPage, options) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
    if (serverData.isNullOrEmpty(data)) {
        return;
    }
    lockup.showMetadataInformationInLockup = objectGraph.bag.isLLMSearchTagsEnabled || objectGraph.client.isPad;
    /// This needs to be guarded against iPhone as natively this will affect all iOS UI components, including iPad.
    if (!objectGraph.client.isPhone) {
        return;
    }
    /// If the individual lockup has a metadata ribbon field, that takes precedence over the page level metadata ribbon
    const searchExperimentDataForLockup = serverData.asDictionary(data, "meta");
    const metadataRibbonItemSlots = (_b = (_a = searchExperimentDataForLockup === null || searchExperimentDataForLockup === void 0 ? void 0 : searchExperimentDataForLockup.displayStyle) === null || _a === void 0 ? void 0 : _a.metadataRibbon) !== null && _b !== void 0 ? _b : (_c = searchExperimentDataForPage === null || searchExperimentDataForPage === void 0 ? void 0 : searchExperimentDataForPage.displayStyle) === null || _c === void 0 ? void 0 : _c.metadataRibbon;
    let llmMetadataRibbonItems = [];
    if (objectGraph.bag.isLLMSearchTagsEnabled) {
        llmMetadataRibbonItems =
            (_g = (_e = (_d = searchExperimentDataForLockup === null || searchExperimentDataForLockup === void 0 ? void 0 : searchExperimentDataForLockup.displayStyle) === null || _d === void 0 ? void 0 : _d.llmRibbon) !== null && _e !== void 0 ? _e : (_f = searchExperimentDataForLockup === null || searchExperimentDataForLockup === void 0 ? void 0 : searchExperimentDataForLockup.displayStyle) === null || _f === void 0 ? void 0 : _f.metadataRibbon) !== null && _g !== void 0 ? _g : (_h = searchExperimentDataForPage === null || searchExperimentDataForPage === void 0 ? void 0 : searchExperimentDataForPage.displayStyle) === null || _h === void 0 ? void 0 : _h.metadataRibbon;
    }
    else {
        llmMetadataRibbonItems =
            (_k = (_j = searchExperimentDataForLockup === null || searchExperimentDataForLockup === void 0 ? void 0 : searchExperimentDataForLockup.displayStyle) === null || _j === void 0 ? void 0 : _j.metadataRibbon) !== null && _k !== void 0 ? _k : (_l = searchExperimentDataForPage === null || searchExperimentDataForPage === void 0 ? void 0 : searchExperimentDataForPage.displayStyle) === null || _l === void 0 ? void 0 : _l.metadataRibbon;
    }
    const metadataRibbonItems = metadataRibbon.createMetadataRibbonItemsForLockup(objectGraph, data, lockup, metadataRibbonItemSlots, options);
    const searchTagsRibbonItems = searchTagsRibbon.createSearchTagsRibbonItemsForLockup(objectGraph, data, lockup, llmMetadataRibbonItems, options);
    lockup.metadataRibbonItems = metadataRibbonItems;
    lockup.searchTagRibbonItems = searchTagsRibbonItems;
    lockup.shouldEvenlyDistributeRibbonItems = !objectGraph.bag.isLLMSearchTagsEnabled;
}
/**
 * Configures all the data into the lockup object
 * @param objectGraph The ObjectGraph instance for the App Store
 * @param data The branch IAP used by the contingent offer
 * @param lockup The lockup object the metadata object will be added to
 * @param options Lockup configuration options
 * @param isParentAppFree A flag used when creating an IAP lockup with missing parent app data in its relationships property.
 */
function copyDataIntoLockup(objectGraph, data, lockup, options, parentData, copyAdditionalDataIntoLockup) {
    if (!data) {
        return;
    }
    validation.context("copyDataIntoLockup", () => {
        var _a, _b, _c, _d, _e, _f;
        const isPreorder = mediaAttributes.attributeAsBooleanOrFalse(data, "isPreorder");
        // Drop this item if it's not a preorder, but contained in a pre-order exclusive shelf.
        if (options.isContainedInPreorderExclusiveShelf && !isPreorder) {
            return null;
        }
        const attributePlatform = (_a = options === null || options === void 0 ? void 0 : options.attributePlatformOverride) !== null && _a !== void 0 ? _a : contentAttributes.defaultAttributePlatform(objectGraph);
        const productVariantData = variants.productVariantDataForData(objectGraph, data);
        lockup.productVariantID = variants.productVariantIDForVariantData(productVariantData);
        options.metricsOptions.productVariantData = productVariantData;
        lockup.adamId = data.id;
        const bundleId = contentAttributes.contentAttributeAsString(objectGraph, data, "bundleId", options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        lockup.bundleId = bundleId;
        lockup.decorations = [];
        let clientIdentifierOverride = null;
        if (options && options.clientIdentifierOverride) {
            clientIdentifierOverride = options.clientIdentifierOverride;
        }
        lockup.icon = content.iconFromData(objectGraph, data, {
            useCase: options.artworkUseCase,
            withJoeColorPlaceholder: options.useJoeColorIconPlaceholder,
            joeColorPlaceholderSelectionLogic: options.joeColorPlaceholderSelectionLogic,
            overrideTextColorKey: options.overrideArtworkTextColorKey,
        }, clientIdentifierOverride, productVariantData, options.attributePlatformOverride);
        if (options && options.titleObjectPath) {
            lockup.title = contentAttributes.contentAttributeAsString(objectGraph, data, options.titleObjectPath, options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        }
        else {
            lockup.title = mediaAttributes.attributeAsString(data, "name");
        }
        if (objectGraph.client.isWeb) {
            lockup.isIOSBinaryMacOSCompatible = mediaAttributes.attributeAsBooleanOrFalse(data, "isIOSBinaryMacOSCompatible");
        }
        // Only use an ad override language if this is an ad.
        lockup.useAdsLocale = options.metricsOptions.isAdvert && isSome(objectGraph.bag.adsOverrideLanguage);
        // Don't display "Apple Arcade" on a lockup if it's not an arcade app or we're running in the
        // apple arcade app (tvOS)
        const isArcadeLockup = content.isArcadeSupported(objectGraph, data, options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        if (isArcadeLockup && isArcadeLockupWordmarkSupported(objectGraph) && !options.shouldHideArcadeHeader) {
            // tvOS uses a 'pill' decoration over artwork to display apple arcade
            // while everywhere else just uses the heading text slot to display a string
            if (objectGraph.client.isTV) {
                lockup.decorations.push("arcade");
            }
            else {
                lockup.heading = options.metricsOptions.isAdvert
                    ? objectGraph.adsLoc.string("Lockup.Heading.Arcade")
                    : objectGraph.loc.string("Lockup.Heading.Arcade");
            }
        }
        if (options.shouldShowFriendsPlayingShowcase) {
            lockup.decorations.push("showcaseFriendsPlaying");
        }
        // Subtitle
        const allowMultilineTertiaryLabel = !isArcadeLockup && !isPreorder && ((_b = options.isMultilineTertiaryTitleAllowed) !== null && _b !== void 0 ? _b : true);
        if (!options.isSubtitleHidden && !isBadgeMultilineFromData(objectGraph, data, allowMultilineTertiaryLabel)) {
            lockup.subtitle = subtitleFromData(objectGraph, data, options);
        }
        // Tertiary badge
        lockup.tertiaryTitle = badgeFromData(objectGraph, data, allowMultilineTertiaryLabel, options.hideCompatibilityBadge);
        lockup.tertiaryTitleAction = badgeActionFromData(objectGraph, data);
        lockup.tertiaryTitleArtwork = badgeArtworkFromData(objectGraph, data);
        lockup.developerTagline = contentAttributes.contentAttributeAsString(objectGraph, data, "subtitle", options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        lockup.editorialTagline = content.notesFromData(objectGraph, data, "tagline", false, options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        lockup.editorialDescription = content.notesFromData(objectGraph, data, "standard", false, options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        lockup.shortEditorialDescription = content.notesFromData(objectGraph, data, "short", false, options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        lockup.ageRating = ageRatings.name(objectGraph, data, true);
        lockup.productDescription = contentAttributes.contentAttributeAsString(objectGraph, data, "description.standard", options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        if ((_c = options === null || options === void 0 ? void 0 : options.shouldShowSupportedPlatformLabel) !== null && _c !== void 0 ? _c : false) {
            copySupportedPlatformLabelIntoLockup(objectGraph, data, lockup, options);
        }
        if (!reviews.shouldSuppressReviews(objectGraph, data)) {
            const ratingCount = mediaAttributes.attributeAsNumber(data, "userRating.ratingCount");
            if (ratingCount > 0 || !(options && options.hideZeroRatings)) {
                lockup.rating = mediaAttributes.attributeAsNumber(data, "userRating.value");
                const count = mediaAttributes.attributeAsNumber(data, "userRating.ratingCount");
                const adsOverrideLanguage = options.metricsOptions.isAdvert
                    ? objectGraph.bag.adsOverrideLanguage
                    : null;
                lockup.ratingCount = objectGraph.loc.formattedCountForPreferredLocale(objectGraph, count, adsOverrideLanguage);
            }
        }
        const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, data, options.metricsOptions, options.metricsClickOptions);
        metricsHelpersLocation.pushContentLocation(objectGraph, metricsClickOptions, lockup.title);
        const offerData = offers.offerDataFromData(objectGraph, data, options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        const includeBetaApps = (_d = options === null || options === void 0 ? void 0 : options.includeBetaApps) !== null && _d !== void 0 ? _d : false;
        const metricsPlatformDisplayStyle = metricsUtil.metricsPlatformDisplayStyleFromData(objectGraph, data, lockup.icon, clientIdentifierOverride);
        const offerButtonMetricsClickOptions = objects.shallowCopyOf(metricsClickOptions);
        const offerAction = offers.offerActionFromOfferData(objectGraph, offerData, data, isPreorder, includeBetaApps, metricsPlatformDisplayStyle, offerButtonMetricsClickOptions, "default", options === null || options === void 0 ? void 0 : options.referrerData);
        const allOfferDiscountData = serverData.asArrayOrEmpty(offerData, "discounts");
        let buttonAction = offers.wrapOfferActionIfNeeded(objectGraph, offerAction, data, isPreorder, offerButtonMetricsClickOptions, "default", clientIdentifierOverride, options.shouldNavigateToProductPage);
        const cppDeeplinkUrl = contentAttributes.customAttributeAsString(objectGraph, data, productVariantData, "customDeepLink", attributePlatform);
        const isAdvert = adLockup.isAdvert(objectGraph, data);
        // CPP deeplinks used for ads have a requirement to not be used when targeting criteria is too narrow.
        const isCppDeeplinkingEnabled = !isAdvert || adLockup.isCppDeeplinkEnabledForAdvert(data);
        const hasCppDeepLink = isCppDeeplinkingEnabled && (cppDeeplinkUrl === null || cppDeeplinkUrl === void 0 ? void 0 : cppDeeplinkUrl.length) > 0;
        const hasExternalDeepLink = ((_e = options === null || options === void 0 ? void 0 : options.externalDeepLinkUrl) === null || _e === void 0 ? void 0 : _e.length) > 0;
        let deepLinkUrl;
        if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
            if (objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
                const customCreativeDeepLinkUrl = adLockup.getCustomCreativeDeepLinkUrl(data);
                const isCustomCreativeDeeplinkingEnabled = isAdvert && adLockup.isCustomCreativeDeeplinkEnabledForAdvert(data);
                const hasCustomCreativeDeepLink = isCustomCreativeDeeplinkingEnabled && (customCreativeDeepLinkUrl === null || customCreativeDeepLinkUrl === void 0 ? void 0 : customCreativeDeepLinkUrl.length) > 0;
                if (hasCustomCreativeDeepLink) {
                    deepLinkUrl = customCreativeDeepLinkUrl;
                }
                else if (hasCppDeepLink) {
                    deepLinkUrl = cppDeeplinkUrl;
                }
                else if (hasExternalDeepLink) {
                    deepLinkUrl = options.externalDeepLinkUrl;
                }
            }
            else if (hasCppDeepLink || hasExternalDeepLink) {
                deepLinkUrl = hasCppDeepLink ? cppDeeplinkUrl : options.externalDeepLinkUrl;
            }
        }
        else if (hasCppDeepLink || hasExternalDeepLink) {
            deepLinkUrl = hasCppDeepLink ? cppDeeplinkUrl : options.externalDeepLinkUrl;
        }
        if (isSome(deepLinkUrl)) {
            // Configure cross link as well as deep link action.
            buttonAction = externalDeepLink.deepLinkActionWrappingAction(objectGraph, buttonAction, offerAction.adamId, bundleId, deepLinkUrl, includeBetaApps, offerButtonMetricsClickOptions);
            // Configure cross link title and subtitle.
            if (((_f = options.crossLinkSubtitle) === null || _f === void 0 ? void 0 : _f.length) > 0) {
                lockup.crossLinkTitle = objectGraph.loc.uppercased(mediaAttributes.attributeAsString(data, "name"));
                lockup.crossLinkSubtitle = options.crossLinkSubtitle;
            }
        }
        lockup.buttonAction = buttonAction;
        // Beta apps
        lockup.includeBetaApps = includeBetaApps;
        lockup.developerName = mediaAttributes.attributeAsString(data, "artistName");
        if (isNothing(lockup.developerName)) {
            // Some newer APIs use `developerName` instead of `artistName`.
            lockup.developerName = mediaAttributes.attributeAsString(data, "developerName");
        }
        // Ensure we grab its children.
        lockup.children = childrenFromLockupData(objectGraph, data, options);
        if (isSome(copyAdditionalDataIntoLockup)) {
            copyAdditionalDataIntoLockup();
        }
        metricsHelpersLocation.popLocation(options.metricsOptions.locationTracker);
        const shareSheetData = sharing.shareSheetDataForProductFromProductData(objectGraph, data, clientIdentifierOverride);
        if (shareSheetData) {
            const shareMenuAction = new models.BlankAction();
            const shareMetricsOptions = objects.shallowCopyOf(metricsClickOptions);
            shareMetricsOptions.actionType = "share";
            shareMetricsOptions.targetType = "lockup";
            metricsHelpersClicks.addClickEventToAction(objectGraph, shareMenuAction, shareMetricsOptions);
            const shareMenuData = new models.LockupContextMenuData();
            shareMenuData.shareAction = shareMenuAction;
            shareMenuData.shareSheetData = shareSheetData;
            lockup.contextMenuData = shareMenuData;
        }
        const resolvedParentData = parentData !== null && parentData !== void 0 ? parentData : parentDataFromInAppData(objectGraph, data);
        let isParentAppFree = false;
        if (resolvedParentData) {
            const parentOfferData = offers.offerDataFromData(objectGraph, resolvedParentData);
            const parentPrice = offers.priceFromOfferData(objectGraph, parentOfferData);
            isParentAppFree = !(parentPrice > 0);
        }
        const offerType = offerTypeForMediaType(objectGraph, data.type, isArcadeLockup);
        if (options) {
            lockup.offerDisplayProperties = offers.displayPropertiesFromOfferAction(objectGraph, offerAction, offerType, data, isPreorder, options.isContainedInPreorderExclusiveShelf, options.offerStyle, options.offerEnvironment, allOfferDiscountData[0], isParentAppFree, "default", options.shouldNavigateToProductPage, options.metricsOptions.isAdvert, null, options.parentAppData, options.isBuyDisallowed);
        }
        else {
            lockup.offerDisplayProperties = offers.displayPropertiesFromOfferAction(objectGraph, offerAction, offerType, data, isPreorder, options.isContainedInPreorderExclusiveShelf, null, null, allOfferDiscountData[0], isParentAppFree, "default");
        }
        if (!options || !options.skipDefaultClickAction) {
            if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
                lockup.clickAction = productActionFromData(objectGraph, data, metricsClickOptions, {
                    clientIdentifierOverride: clientIdentifierOverride,
                    productVariantData: productVariantData,
                    alignedRegionDeepLinkUrl: adLockup.getCustomCreativeDeepLinkUrl(data),
                    isCppDeepLinkEligible: isCppDeeplinkingEnabled,
                });
            }
            else {
                lockup.clickAction = productActionFromData(objectGraph, data, metricsClickOptions, {
                    clientIdentifierOverride: clientIdentifierOverride,
                    productVariantData: productVariantData,
                    isCppDeepLinkEligible: isCppDeeplinkingEnabled,
                });
            }
        }
        if (options && options.ordinal) {
            lockup.ordinal = options.ordinal;
        }
        const editorialBadgeInfo = contentAttributes.contentAttributeAsDictionary(objectGraph, data, "editorialBadgeInfo", options === null || options === void 0 ? void 0 : options.attributePlatformOverride);
        const supportsVisionOSCompatibleIOSBinary = content.supportsVisionOSCompatibleIOSBinaryFromData(objectGraph, data);
        if (editorialBadgeInfo && !supportsVisionOSCompatibleIOSBinary) {
            const badgeType = serverData.asString(editorialBadgeInfo, "editorialBadgeType");
            const hasEditorsChoiceBadge = badgeType && badgeType === "editorialPriority";
            lockup.isEditorsChoice = hasEditorsChoiceBadge;
        }
        // Flow preview actions
        if (!isAdvert) {
            lockup.flowPreviewActionsConfiguration = flowPreview.flowPreviewActionsConfigurationForProductFromData(objectGraph, data, false, clientIdentifierOverride, lockup.clickAction, options.metricsOptions, metricsClickOptions, options.externalDeepLinkUrl, lockup.subtitle, lockup.title);
        }
        const metricsImpressionOptions = metricsHelpersImpressions.impressionOptionsForLockup(objectGraph, data, lockup, metricsPlatformDisplayStyle, options.metricsOptions, options.canDisplayArcadeOfferButton);
        metricsHelpersImpressions.addImpressionFields(objectGraph, lockup, metricsImpressionOptions);
    });
}
/**
 * Configures the tertiary icons and label for showing unsupported apps in a bundle.
 * @param objectGraph The ObjectGraph instance for the App Store
 * @param data The branch IAP used by the contingent offer
 * @param lockup The lockup object the metadata object will be added to
 * @param options Lockup configuration options
 */
function copySupportedPlatformLabelIntoLockup(objectGraph, data, lockup, options) {
    const appPlatforms = content.supportedAppPlatformsFromData(objectGraph, data);
    const supportsMacOSCompatibleIOSBinary = content.supportsMacOSCompatibleIOSBinaryFromData(objectGraph, data, objectGraph.client.isMac);
    const supportsVisionOSCompatibleBinary = content.supportsVisionOSCompatibleIOSBinaryFromData(objectGraph, data);
    const isBuyable = content.buyableOnDevice(objectGraph, data, appPlatforms, objectGraph.client.deviceType, supportsMacOSCompatibleIOSBinary, supportsVisionOSCompatibleBinary);
    /// If its buyable no need to display compatibility label
    if (isBuyable) {
        return;
    }
    const additionalPlatforms = appPlatforms.filter((platform) => platform !== objectGraph.client.deviceType);
    if (additionalPlatforms.length === 0) {
        return;
    }
    lockup.tertiaryTitleIcons = additionalPlatforms.map((platform) => content.systemImageNameForAppPlatform(platform));
    if (additionalPlatforms.length === 1) {
        const platformTitle = content.appPlatformTitle(objectGraph, additionalPlatforms[0]);
        lockup.tertiaryTitle = objectGraph.loc
            .string("AppStore.Bundles.ProductPage.OnlyAvailable.Message")
            .replace("@@platform@@", platformTitle);
    }
    else {
        // We only support showing up to 2 platforms. Grab the first 2
        const platformTitle1 = content.appPlatformTitle(objectGraph, additionalPlatforms[0]);
        const platformTitle2 = content.appPlatformTitle(objectGraph, additionalPlatforms[1]);
        lockup.tertiaryTitle = objectGraph.loc
            .string("AppStore.Bundles.ProductPage.AvailableOnTwo.Message")
            .replace("@@platform1@@", platformTitle1)
            .replace("@@platform2@@", platformTitle2);
    }
}
export function childrenFromLockupData(objectGraph, data, options) {
    const childrenRelationship = mediaRelationship.relationship(data, "apps");
    if (childrenRelationship) {
        const listOptions = {
            lockupOptions: {
                ...options,
                shouldCreateScreenshotsLockup: options === null || options === void 0 ? void 0 : options.shouldIncludeScreenshotsForChildren,
            },
            filter: 0 /* filtering.Filter.None */,
        };
        return lockupsFromDataContainer(objectGraph, childrenRelationship, listOptions);
    }
    return null;
}
function copyDataIntoInAppPurchaseLockup(objectGraph, data, lockup, options) {
    if (!data) {
        return;
    }
    validation.context("copyDataIntoInAppPurchaseLockup", () => {
        var _a;
        const parentData = (_a = parentDataFromInAppData(objectGraph, data)) !== null && _a !== void 0 ? _a : options.parentAppData;
        const isStreamlinedBuy = mediaAttributes.attributeAsBooleanOrFalse(parentData, "supportsStreamlinedBuy");
        const iapData = iapDataFromData(objectGraph, data);
        data = iapData;
        copyDataIntoLockup(objectGraph, data, lockup, options, parentData);
        lockup.productIdentifier = mediaAttributes.attributeAsString(data, "offerName");
        lockup.parent = lockupFromData(objectGraph, parentData, options);
        lockup.description = mediaAttributes.attributeAsString(data, "description.standard");
        lockup.isVisibleByDefault = mediaAttributes.attributeAsBooleanOrFalse(data, "isMerchandisedVisibleByDefault");
        lockup.isSubscription = mediaAttributes.attributeAsBooleanOrFalse(data, "isSubscription");
        const offerData = offers.offerDataFromData(objectGraph, data);
        const discountOfferData = serverData.asArrayOrEmpty(offerData, "discounts");
        lockup.offerDisplayProperties.hasDiscount = discountOfferData.length > 0;
        lockup.offerDisplayProperties.subscriptionFamilyId = mediaAttributes.attributeAsString(data, "subscriptionFamilyId");
        // Action for App Install (needed in case the parent app is not installed)
        const installRequiredAction = new models.FlowAction("inAppPurchaseInstall");
        installRequiredAction.presentationContext = "presentModalFormSheet";
        const installRequiredActionUrl = configureIAPInstallPageUrl(objectGraph, lockup.adamId, parentData.id);
        installRequiredAction.pageUrl = installRequiredActionUrl;
        // Install Page
        const sidepackInstallPage = new models.InAppPurchaseInstallPage();
        sidepackInstallPage.parentLockup = objects.shallowCopyOf(lockup.parent);
        sidepackInstallPage.lockup = objects.shallowCopyOf(lockup);
        sidepackInstallPage.preInstallOfferDescription = offerFormatting.installPagePreInstallTrialDescription(objectGraph, offerData);
        installRequiredAction.pageData = sidepackInstallPage;
        const productIdentifier = mediaAttributes.attributeAsString(data, "offerName");
        const parentBundleId = contentAttributes.contentAttributeAsString(objectGraph, parentData, "bundleId");
        const firstVersionSupportingMerchIAP = mediaAttributes.attributeAsString(parentData, "firstVersionSupportingInAppPurchaseApi");
        const hasDiscountedOffer = serverData.isDefinedNonNullNonEmpty(discountedOfferFromData(data));
        const metricsOptions = metricsHelpersImpressions.impressionOptionsForLockup(objectGraph, data, lockup, "iap", options.metricsOptions, options.canDisplayArcadeOfferButton);
        metricsHelpersLocation.pushContentLocation(objectGraph, metricsOptions, lockup.title);
        if (isStreamlinedBuy && hasDiscountedOffer) {
            const inAppPurchaseAction = new models.InAppPurchaseAction(productIdentifier, parentData.id, parentBundleId, lockup.parent.buttonAction);
            if (lockup.parent) {
                inAppPurchaseAction.appTitle = lockup.parent.title;
            }
            inAppPurchaseAction.productTitle = lockup.title;
            inAppPurchaseAction.streamlineBuyAction = streamlineBuyActionForIAP(objectGraph, data, parentData, lockup, options);
            lockup.buttonAction = inAppPurchaseAction;
            lockup.subtitle = mediaAttributes.attributeAsString(parentData, "name");
        }
        else if (firstVersionSupportingMerchIAP) {
            const inAppPurchaseAction = new models.InAppPurchaseAction(productIdentifier, parentData.id, parentBundleId, installRequiredAction, firstVersionSupportingMerchIAP);
            if (lockup.parent) {
                inAppPurchaseAction.appTitle = lockup.parent.title;
            }
            inAppPurchaseAction.productTitle = lockup.title;
            const clickOptions = {
                ...options.metricsOptions,
                id: lockup.adamId,
                idType: "its_id",
                actionDetails: { parentAdamId: parentData.id },
            };
            metricsHelpersClicks.addClickEventToAction(objectGraph, inAppPurchaseAction, clickOptions);
            lockup.buttonAction = inAppPurchaseAction;
        }
        else {
            const alert = new models.AlertAction("default");
            alert.title = objectGraph.loc.string("SEED_IN_APP_UNSUPPORTED_MESSAGE_OPTION_1");
            alert.message = "";
            alert.isCancelable = true;
            lockup.buttonAction = alert;
        }
        metricsHelpersLocation.popLocation(options.metricsOptions.locationTracker);
        // Click action to the product page
        if (!options || !options.skipDefaultClickAction) {
            const clickAction = iAPActionFromData(objectGraph, data, metricsOptions);
            lockup.clickAction = clickAction;
            lockup.productAction = clickAction;
        }
        metricsHelpersImpressions.addImpressionFieldsToInAppPurchaseLockup(objectGraph, lockup, metricsOptions);
    }, "item.offer.buyParams");
}
/**
 * Creates the discounted buy action for the streamline buy Action
 * @param objectGraph The ObjectGraph instance for the App Store
 * @param data The branch IAP used by the contingent offer
 * @param parentData The branch app used by the contingent offer
 * @param lockup The lockup object the metadata object will be added to
 * @param options Lockup configuration options
 */
function streamlineBuyActionForIAP(objectGraph, data, parentData, lockup, options) {
    var _a;
    const clickOptions = {
        ...options.metricsOptions,
        id: parentData.id,
        targetId: parentData.id,
        idType: "its_id",
        actionDetails: { parentAdamId: parentData.id },
    };
    const metricsPlatformDisplayStyle = metricsUtil.metricsPlatformDisplayStyleFromData(objectGraph, data, lockup.icon, options.clientIdentifierOverride);
    const offerButtonMetricsClickOptions = objects.shallowCopyOf(clickOptions);
    const discountedOfferData = discountedOfferFromData(data);
    const offerData = offers.offerDataFromData(objectGraph, data);
    const appOfferData = offers.offerDataFromData(objectGraph, parentData);
    const appBuyParams = new BuyParameters(serverData.asString(appOfferData, "buyParams"));
    let currentBuyParams = (_a = serverData.asString(discountedOfferData, "buyParams")) !== null && _a !== void 0 ? _a : serverData.asString(offerData, "buyParams");
    currentBuyParams += `&appAdamId=${serverData.asString(parentData, "id")}`;
    currentBuyParams += `&appExtVrsId=${appBuyParams.get("appExtVrsId", "")}`;
    currentBuyParams += `&bid=${contentAttributes.contentAttributeAsString(objectGraph, parentData, "bundleId")}`;
    currentBuyParams += `&bvrs=1.0`;
    currentBuyParams += `&offerName=${contentAttributes.contentAttributeAsString(objectGraph, data, "offerName")}`;
    const offerId = serverData.asString(discountedOfferData, "offerId");
    if (serverData.isDefinedNonNullNonEmpty(offerId)) {
        currentBuyParams += `&adHocOfferId=${offerId}`;
    }
    offerData["buyParams"] = currentBuyParams;
    const subscribeAction = offers.offerActionFromOfferData(objectGraph, offerData, data, false, false, metricsPlatformDisplayStyle, offerButtonMetricsClickOptions, "default", options === null || options === void 0 ? void 0 : options.referrerData, false, parentData.id);
    return subscribeAction;
}
function copyDataIntoTrailersLockup(objectGraph, data, lockup, videoConfiguration, options) {
    if (!data) {
        return;
    }
    validation.context("copyDataIntoTrailersLockup", () => {
        copyDataIntoLockup(objectGraph, data, lockup, options);
        lockup.trailers = content.trailersFromData(objectGraph, data, videoConfiguration, options.metricsOptions, lockup.adamId);
    });
}
function copyMediaIntoMixedMediaLockup(objectGraph, data, lockup, videoConfiguration, options, cropCode) {
    var _a;
    if (!data) {
        return;
    }
    if (options.isNetworkConstrained) {
        return;
    }
    const isAd = (_a = options.metricsOptions.isAdvert) !== null && _a !== void 0 ? _a : false;
    validation.context("copyMediaIntoMixedMediaLockup", () => {
        if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
            if (objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
                const customCreativeData = platformAttributeAsDictionary(data, contentAttributes.bestAttributePlatformFromData(objectGraph, data), "creativeAttributes");
                if (isSome(customCreativeData)) {
                    lockup.alignedRegionArtwork = customCreativeArtworkFromData(objectGraph, data, customCreativeData, cropCode);
                    lockup.alignedRegionVideo = customCreativeVideoFromData(objectGraph, data, customCreativeData, videoConfiguration, cropCode);
                }
            }
        }
        lockup.screenshots = content.screenshotsFromData(objectGraph, data, 4 /* content.ArtworkUseCase.LockupScreenshots */, null, options.clientIdentifierOverride, null, isAd, cropCode);
        const firstScreenshots = lockup.screenshots[0];
        lockup.trailers = [];
        const trailers = content.trailersFromData(objectGraph, data, videoConfiguration, options.metricsOptions, lockup.adamId, isAd, cropCode);
        if (serverData.isDefinedNonNull(trailers)) {
            if (serverData.isNullOrEmpty(firstScreenshots) ||
                trailers.mediaPlatform.isEqualTo(firstScreenshots.mediaPlatform)) {
                lockup.trailers.push(trailers);
            }
        }
    });
}
/**
 * Copy the data contained in a platform response into a `ScreenshotsLockup`.
 * @param data      The store platform response data to read from.
 * @param lockup    The ScreenshotsLockup data model to copy store platform data to
 * @param options   Options customizing what data is needed.
 */
function copyDataIntoScreenshotsLockup(objectGraph, data, lockup, options) {
    if (!data) {
        return;
    }
    validation.context("copyDataIntoScreenshotsLockup", () => {
        copyDataIntoLockup(objectGraph, data, lockup, options);
        lockup.screenshots = content.screenshotsFromData(objectGraph, data, 4 /* content.ArtworkUseCase.LockupScreenshots */, null, options.clientIdentifierOverride);
    });
}
/**
 * Copy the data contained in a platform response into a `PosterLockup`.
 * @param data      The store platform response data to read from.
 * @param lockup    The PosterLockup data model to copy store platform data to
 * @param options   Options customizing what data is needed.
 */
function copyDataIntoPosterLockup(objectGraph, data, lockup, options) {
    if (!data) {
        return;
    }
    validation.context("copyDataIntoPosterLockup", () => {
        copyDataIntoLockup(objectGraph, data, lockup, options);
        lockup.epicHeading = content.posterEpicHeadingArtworkFromData(objectGraph, data);
        lockup.posterArtwork = content.posterArtworkFromData(objectGraph, data);
        lockup.posterVideo = content.posterEditorialVideoFromData(objectGraph, data, 19 /* content.ArtworkUseCase.GroupingHero */);
        if (lockup.offerDisplayProperties) {
            lockup.offerDisplayProperties = lockup.offerDisplayProperties.newOfferDisplayPropertiesChangingAppearance(false, "white", "lightOverArtwork");
        }
        if (lockup.posterVideo) {
            lockup.isDark = color.isDarkColor(lockup.posterVideo.preview.backgroundColor);
        }
        else if (lockup.posterArtwork) {
            lockup.isDark = color.isDarkColor(lockup.posterArtwork.backgroundColor);
        }
        else {
            lockup.isDark = false;
        }
        const supportsArcade = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, data, "supportsArcade");
        const isPreorder = contentAttributes.contentAttributeAsBooleanOrFalse(objectGraph, data, "isPreorder");
        if (isPreorder) {
            const fallbackString = supportsArcade ? objectGraph.loc.string("Offer.Label.ComingSoon") : null;
            let preorderDate;
            if (objectGraph.client.isVision || objectGraph.client.isWeb) {
                preorderDate = content.dynamicPreorderDateFromData(objectGraph, data, fallbackString);
            }
            else {
                preorderDate = objectGraph.loc.uppercased(content.dynamicPreorderDateFromData(objectGraph, data, fallbackString));
            }
            if (isSome(preorderDate)) {
                lockup.footerText = preorderDate;
            }
        }
    });
}
export function subtitleFromData(objectGraph, data, lockupOptions = null) {
    if (isNothing(data)) {
        return null;
    }
    return validation.context("subtitleFromData", () => {
        let subtitle;
        if (lockupOptions && lockupOptions.subtitleObjectPath) {
            subtitle = contentAttributes.contentAttributeAsString(objectGraph, data, lockupOptions.subtitleObjectPath, lockupOptions === null || lockupOptions === void 0 ? void 0 : lockupOptions.attributePlatformOverride);
        }
        if (serverData.isNullOrEmpty(subtitle)) {
            subtitle = contentAttributes.contentAttributeAsString(objectGraph, data, "subtitle", lockupOptions === null || lockupOptions === void 0 ? void 0 : lockupOptions.attributePlatformOverride);
        }
        if (subtitle) {
            return subtitle;
        }
        else {
            return categoryFromData(objectGraph, data, lockupOptions);
        }
    });
}
/**
 * Creates the compability badge text for showing on a lockup.
 *
 * @param objectGraph Current object graph
 * @param data Product data
 * @returns Built badge text, or null
 */
export function badgeFromData(objectGraph, data, allowMultiline = false, hideCompatibilityBadge) {
    return validation.context("badgeFromData", () => {
        if (hideCompatibilityBadge) {
            return null;
        }
        const doesClientSupportMacOSCompatibleIOSBinary = objectGraph.client.isMac || objectGraph.client.isWeb;
        const supportsMacOSCompatibleIOSBinary = content.supportsMacOSCompatibleIOSBinaryFromData(objectGraph, data, doesClientSupportMacOSCompatibleIOSBinary);
        const supportsVisionOSCompatibleBinary = content.supportsVisionOSCompatibleIOSBinaryFromData(objectGraph, data);
        if (supportsMacOSCompatibleIOSBinary || supportsVisionOSCompatibleBinary) {
            // 1. Build loc string base.
            let locKey = "";
            const appPlatforms = content.supportedAppPlatformsFromData(objectGraph, data);
            if (content.supportsPlatform(appPlatforms, "pad")) {
                locKey = "Platform.DesignedForPad";
            }
            else if (content.supportsPlatform(appPlatforms, "phone")) {
                locKey = "Platform.DesignedForPhone";
            }
            if ((locKey === null || locKey === void 0 ? void 0 : locKey.length) > 0) {
                if (supportsMacOSCompatibleIOSBinary) {
                    // 2. Check if app is verified.
                    const isVerifiedForAppleSiliconMac = isVerifiedForAppleSiliconMacFromData(objectGraph, data);
                    if (!isVerifiedForAppleSiliconMac) {
                        locKey += ".NotVerified";
                        // 3. Use expanded two-line not verified badge.
                        if (allowMultiline) {
                            locKey += ".Expanded";
                        }
                    }
                }
                return objectGraph.loc.string(locKey);
            }
        }
        return null;
    });
}
/**
 * Creates the compability badge artwork for showing on a lockup.
 *
 * @param objectGraph Current object graph
 * @param data Product data
 * @returns An iPhone/iPad symbol artwork, or null
 */
export function badgeArtworkFromData(objectGraph, data) {
    const supportsVisionOSCompatibleBinary = content.supportsVisionOSCompatibleIOSBinaryFromData(objectGraph, data);
    if (supportsVisionOSCompatibleBinary && objectGraph.client.isVision) {
        const appPlatforms = content.supportedAppPlatformsFromData(objectGraph, data);
        if (content.supportsPlatform(appPlatforms, "pad")) {
            return createArtworkForResource(objectGraph, "systemimage://ipad.landscape");
        }
        else if (content.supportsPlatform(appPlatforms, "phone")) {
            return createArtworkForResource(objectGraph, "systemimage://iphone");
        }
    }
    return null;
}
export function badgeActionFromData(objectGraph, data) {
    return validation.context("badgeActionFromData", () => {
        // Add action for unverified macOS apps.
        const supportsMacOSCompatibleIOSBinary = content.supportsMacOSCompatibleIOSBinaryFromData(objectGraph, data, objectGraph.client.isMac);
        if (!supportsMacOSCompatibleIOSBinary) {
            return null;
        }
        const isVerifiedForAppleSiliconMac = isVerifiedForAppleSiliconMacFromData(objectGraph, data);
        if (isVerifiedForAppleSiliconMac) {
            return null;
        }
        const appPlatforms = content.supportedAppPlatformsFromData(objectGraph, data);
        if (!content.supportsPlatform(appPlatforms, "pad") && !content.supportsPlatform(appPlatforms, "phone")) {
            return null;
        }
        const linkAction = new models.FlowAction("article");
        linkAction.pageUrl = `https://apps.apple.com/story/id${objectGraph.bag.appleSiliconMacUnverifiedBadgeEditorialItemId}`;
        return linkAction;
    });
}
/// Does the badge require multiple lines?
export function isBadgeMultilineFromData(objectGraph, data, allowMultiline) {
    return validation.context("isBadgeMultilineFromData", () => {
        if (!allowMultiline) {
            return false;
        }
        const supportsMacOSCompatibleIOSBinary = content.supportsMacOSCompatibleIOSBinaryFromData(objectGraph, data, objectGraph.client.isMac);
        if (!supportsMacOSCompatibleIOSBinary) {
            return false;
        }
        const isVerifiedForAppleSiliconMac = isVerifiedForAppleSiliconMacFromData(objectGraph, data);
        if (isVerifiedForAppleSiliconMac) {
            return false;
        }
        const appPlatforms = content.supportedAppPlatformsFromData(objectGraph, data);
        return content.supportsPlatform(appPlatforms, "pad") || content.supportsPlatform(appPlatforms, "phone");
    });
}
function isVerifiedForAppleSiliconMacFromData(objectGraph, data) {
    // Don't badge unverified apps when Apple Silicon support is not enabled.
    if (!objectGraph.appleSilicon.isSupportEnabled) {
        return true;
    }
    // Check response.
    const isVerifiedForAppleSiliconMac = contentAttributes.contentAttributeAsBoolean(objectGraph, data, "isVerifiedForAppleSiliconMac", "ios");
    if (serverData.isDefinedNonNull(isVerifiedForAppleSiliconMac)) {
        return isVerifiedForAppleSiliconMac;
    }
    return false;
}
export function categoryFromData(objectGraph, data, lockupOptions = null) {
    return validation.context("categoryFromData", () => {
        // genreDisplayName is the preferred field to use for the determining the category name
        // It should always be available, but we have fallbacks below just in case.
        const genreName = contentAttributes.contentAttributeAsString(objectGraph, data, "genreDisplayName", lockupOptions === null || lockupOptions === void 0 ? void 0 : lockupOptions.attributePlatformOverride);
        if ((genreName === null || genreName === void 0 ? void 0 : genreName.length) > 0) {
            return genreName;
        }
        // Fallback to interating over the genres list
        const genres = mediaRelationship.relationshipCollection(data, "genres");
        if (genres.length > 0) {
            let candidateGenre = genres[0];
            // If the primary genre is Games, search for the first sub-genre of games
            const gamesId = 6014 /* constants.GenreIds.Games */.toString();
            if (candidateGenre.id === gamesId) {
                for (const genre of genres) {
                    const parentGenreId = mediaAttributes.attributeAsString(genre, "parentId");
                    if (genre.id !== gamesId && parentGenreId === gamesId) {
                        candidateGenre = genre;
                        break;
                    }
                }
            }
            return mediaAttributes.attributeAsString(candidateGenre, "name");
        }
        else {
            // Fallback to genreNames
            const genreNames = mediaAttributes.attributeAsArrayOrEmpty(data, "genreNames");
            return genreNames.length > 0 ? genreNames[0] : null;
        }
    });
}
/**
 * Configures an internal URL for use as the pageUrl of an 'install' FlowAction,
 * using the provided parameters.
 * @param inAppPurchaseAdamId  The adamId for the lockup to appear in the header.
 * @param parentAdamId   The adamId for the lockup that is being offered.
 * @returns A fully configured internal URL for fetching an app install page.
 */
export function configureIAPInstallPageUrl(objectGraph, inAppPurchaseAdamId, parentAdamId) {
    const parameters = new http.FormBuilder()
        .param(Parameters.id, parentAdamId)
        .param(InAppPurchaseInstallPageParameters.inAppPurchaseId, inAppPurchaseAdamId)
        .build();
    return `${Protocol.internal}:/${Path.product}/${Path.install}/?${parameters}`;
}
/**
 * Configures an internal url to be used as the url for a clickAction from an IAP
 * lockup to a product page.
 * @param productUrl            The URL for the product.
 * @param inAppPurchaseAdamId   The adamId for the lockup from which the click occurs.
 * @param inAppPurchaseType     The type for the in-app purchase.
 * @returns A fully configured internal URL for a product click action via IAP.
 */
export function configureProductUrlFromInAppPurchase(objectGraph, productUrl, inAppProductIdentifier, isSubscription) {
    const parameters = new http.FormBuilder()
        .param(ProductPageParameters.url, productUrl)
        .param(Parameters.offerName, inAppProductIdentifier)
        .param(ProductPageParameters.isSubscription, isSubscription.toString())
        .build();
    return `${Protocol.internal}:/${Path.product}/${Path.lookup}/?${parameters}`;
}
export function lockupFromData(objectGraph, data, options) {
    return validation.context("lockupFromData", () => {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j;
        if (!data) {
            validation.unexpectedNull("ignoredValue", "data");
            return null;
        }
        // Must setup iAdInfo before any builder methods.
        const isAd = adLockup.isAdvert(objectGraph, data);
        options.metricsOptions.isAdvert = isAd;
        const isAdEligible = adCommon.isAdEligible((_b = (_a = options.metricsOptions.pageInformation) === null || _a === void 0 ? void 0 : _a.iAdInfo) === null || _b === void 0 ? void 0 : _b.placementType, options.metricsOptions.locationTracker);
        options.metricsOptions.isAdEligible = isAdEligible;
        if (isAd || isAdEligible) {
            (_d = (_c = options.metricsOptions.pageInformation) === null || _c === void 0 ? void 0 : _c.iAdInfo) === null || _d === void 0 ? void 0 : _d.apply(objectGraph, data);
        }
        if (isAd) {
            (_f = (_e = options.metricsOptions.pageInformation) === null || _e === void 0 ? void 0 : _e.iAdInfo) === null || _f === void 0 ? void 0 : _f.setTemplateType("APPLOCKUP");
        }
        if (!mediaAttributes.hasAttributes(data)) {
            return null;
        }
        switch (data.type) {
            case "in-apps":
                options.offerEnvironment = "widthConstrainedLockup";
                return inAppPurchaseLockupFromData(objectGraph, data, options);
            case "app-events":
                const parentAppData = mediaRelationship.relationshipData(objectGraph, data, "app");
                if (serverData.isNullOrEmpty(parentAppData)) {
                    return null;
                }
                const appLockup = new models.Lockup();
                copyDataIntoLockup(objectGraph, parentAppData, appLockup, options);
                return appLockup;
            case "contingent-items":
            case "offer-items":
                return appPromotionOfferLockupFromData(objectGraph, data, options);
            default:
                const lockup = new models.Lockup();
                copyDataIntoLockup(objectGraph, data, lockup, options);
                if (isSome((_g = options.metricsOptions.pageInformation) === null || _g === void 0 ? void 0 : _g.iAdInfo)) {
                    if (isAd || isAdEligible) {
                        adLockup.performAdOverridesforLockup(objectGraph, data, lockup, options.metricsOptions);
                    }
                    if (isAd) {
                        if (objectGraph.props.enabled("advertSlotReporting")) {
                            (_h = lockup.searchAdOpportunity) === null || _h === void 0 ? void 0 : _h.setTemplateType("APPLOCKUP");
                        }
                        else {
                            (_j = lockup.searchAd) === null || _j === void 0 ? void 0 : _j.setTemplateType("APPLOCKUP");
                        }
                    }
                }
                return lockup;
        }
    });
}
/**
 * Configures a app promotion lockup with a contingent-items or offer-items object
 * @param objectGraph The App Store Object Graph.
 * @param data The data for the app to go into the lockup.
 * @param options A set of options customising the lockup.
 * @returns A Lockup with the desired configuration.
 */
export function appPromotionOfferLockupFromData(objectGraph, data, options) {
    return validation.context("appPromotionOfferLockupFromData", () => {
        var _a, _b, _c;
        const parentData = (_a = parentDataFromInAppData(objectGraph, data)) !== null && _a !== void 0 ? _a : options.parentAppData;
        const iapData = iapDataFromData(objectGraph, data);
        const supportsStreamlinedBuy = mediaAttributes.attributeAsBooleanOrFalse(parentData, "supportsStreamlinedBuy");
        if (supportsStreamlinedBuy) {
            const lockup = inAppPurchaseLockupFromData(objectGraph, data, options);
            lockup.offerDisplayProperties.titles["standard"] = objectGraph.loc.string("OfferButton.Title.Subscribe");
            lockup.offerDisplayProperties.isStreamlinedBuy = true;
            // Override any discounts to force showing "Subscribe" title on the offer button.
            lockup.offerDisplayProperties.hasDiscount = false;
            // Setup the artwork
            const rawArtwork = mediaAttributes.attributeAsDictionary(iapData, "artwork");
            const backupArtwork = appPromotionsCommon.artworkFromPlatformData(objectGraph, parentData, "artwork");
            const iapArtwork = content.artworkFromApiArtwork(objectGraph, rawArtwork, {
                useCase: options.artworkUseCase,
                withJoeColorPlaceholder: options.useJoeColorIconPlaceholder,
                style: "iap",
                overrideTextColorKey: options.overrideArtworkTextColorKey,
            });
            lockup.icon = iapArtwork !== null && iapArtwork !== void 0 ? iapArtwork : backupArtwork;
            return lockup;
        }
        else {
            const lockup = new models.Lockup();
            copyDataIntoLockup(objectGraph, parentData, lockup, options);
            const metricsClickOptions = metricsHelpersClicks.clickOptionsForLockup(objectGraph, data, options.metricsOptions, options.metricsClickOptions);
            metricsHelpersLocation.pushContentLocation(objectGraph, metricsClickOptions, lockup.title);
            const parentBundleId = contentAttributes.contentAttributeAsString(objectGraph, parentData, "bundleId");
            const productIdentifier = mediaAttributes.attributeAsString(iapData, "offerName");
            const firstVersionSupportingMerchIAP = mediaAttributes.attributeAsString(parentData, "firstVersionSupportingInAppPurchaseApi");
            const offerAction = new models.InAppPurchaseAction(productIdentifier, parentData.id, parentBundleId, lockup.buttonAction, firstVersionSupportingMerchIAP);
            offerAction.appTitle = (_b = mediaAttributes.attributeAsString(parentData, "name")) !== null && _b !== void 0 ? _b : "";
            offerAction.productTitle = (_c = mediaAttributes.attributeAsString(iapData, "name")) !== null && _c !== void 0 ? _c : "";
            if (data.type === "offer-items") {
                const discountedOffer = discountedOfferFromData(iapData);
                const offerId = serverData.asString(discountedOffer, "offerId");
                if (isSome(offerId) && offerId.length > 0) {
                    offerAction.additionalBuyParams = "adHocOfferId=" + offerId;
                }
            }
            else {
                // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
                offerAction.additionalBuyParams = "contingentItemId=" + data.id;
            }
            lockup.buttonAction = offerAction;
            metricsHelpersLocation.popLocation(options.metricsOptions.locationTracker);
            return lockup;
        }
    });
}
export function inAppPurchaseLockupFromData(objectGraph, data, options) {
    return validation.context("inAppPurchaseLockupFromData", () => {
        const lockup = new models.InAppPurchaseLockup();
        copyDataIntoInAppPurchaseLockup(objectGraph, data, lockup, options);
        return lockup;
    });
}
/**
 * Create a new `ScreenshotsLockup` from a platform response data blob.
 * @param data  The platform response data to read from.
 * @param options   Options customizing what data the returned store item will contain.
 * @returns A new `Lockup` object.
 */
export function screenshotsLockupFromData(objectGraph, data, options) {
    return validation.context("screenshotsLockupFromData", () => {
        const lockup = new models.ScreenshotsLockup();
        copyDataIntoScreenshotsLockup(objectGraph, data, lockup, options);
        return lockup;
    });
}
/**
 * Create a new `PosterLockup` from a platform response data blob.
 * @param data  The platform response data to read from.
 * @param options   Options customizing what data the returned store item will contain.
 * @returns A new `Lockup` object.
 */
export function posterLockupFromData(objectGraph, data, options) {
    return validation.context("posterLockupFromData", () => {
        const lockup = new models.PosterLockup();
        copyDataIntoPosterLockup(objectGraph, data, lockup, options);
        return lockup;
    });
}
/**
 *
 * @param data
 * @param options
 * @param videoConfiguration
 * @returns {TrailersLockup}
 */
export function trailersLockupFromData(objectGraph, data, options, videoConfiguration) {
    return validation.context("trailersLockupFromData", () => {
        const lockup = new models.TrailersLockup();
        copyDataIntoTrailersLockup(objectGraph, data, lockup, videoConfiguration, options);
        return lockup;
    });
}
/**
 *Build a mixed media lockup from given data
 * @param objectGraph
 * @param data
 * @param options
 * @param videoConfiguration
 * @param searchExperimentsData
 * @param cropCode The crop code to use for the media
 * @returns {MixedMediaLockup}
 */
export function mixedMediaLockupFromData(objectGraph, data, options, videoConfiguration, searchExperimentsData = null, cropCode) {
    return validation.context("mixedMediaLockupFromData", () => {
        const lockup = new models.MixedMediaLockup();
        copyDataIntoLockup(objectGraph, data, lockup, options, null, () => {
            copyMetadataRibbonInfoIntoLockup(objectGraph, data, lockup, searchExperimentsData, options);
            copyMediaIntoMixedMediaLockup(objectGraph, data, lockup, videoConfiguration, options, cropCode);
            copyScreenshotsDisplayStyleIntoMixedMediaLockup(objectGraph, data, lockup, searchExperimentsData);
        });
        return lockup;
    });
}
/**
 * Create am image lockup for shelfContents to display within a grouping shelf
 * @param objectGraph
 * @param mediaApiData shelfContents to create lockup for.
 * @param lockupOptions The options needed to customize this lockup
 * @param collectionShelfDisplayStyle
 */
export function imageLockupFromData(objectGraph, mediaApiData, lockupOptions, collectionDisplayStyle) {
    const isEditorialApiData = mediaApiData.type === "editorial-items";
    const lockupData = isEditorialApiData
        ? mediaRelationship.relationshipData(objectGraph, mediaApiData, "primary-content")
        : mediaApiData;
    const lockup = lockupFromData(objectGraph, lockupData, lockupOptions);
    // Determine which artwork to use, giving priority to an attached EI first, and then falling back
    // to the lockup artwork. Currently we only use the attached EI on visionOS.
    const attributePlatform = contentAttributes.bestAttributePlatformFromData(objectGraph, lockupData);
    const lockupEditorialMediaData = editorialComponentMediaUtil.editorialMediaDataFromData(objectGraph, lockupData, collectionDisplayStyle);
    const articleEditorialMediaData = editorialComponentMediaUtil.editorialMediaDataFromData(objectGraph, mediaApiData, collectionDisplayStyle);
    const lockupArtwork = lockupEditorialMediaData === null || lockupEditorialMediaData === void 0 ? void 0 : lockupEditorialMediaData.artwork;
    const articleArtwork = articleEditorialMediaData === null || articleEditorialMediaData === void 0 ? void 0 : articleEditorialMediaData.artwork;
    let artwork;
    let isDark;
    if (isSome(articleArtwork) && objectGraph.client.isVision) {
        isDark = isMediaDark(objectGraph, articleEditorialMediaData);
        artwork = articleArtwork;
    }
    else if (isSome(lockupArtwork)) {
        isDark = isMediaDark(objectGraph, lockupEditorialMediaData);
        artwork = lockupArtwork;
    }
    if (isSome(artwork) && isSome(lockup)) {
        const imageLockup = new models.ImageLockup(artwork, lockup, null, null, isDark);
        imageLockup.caption = mediaPlatformAttributes.platformAttributeAsString(lockupData, attributePlatform, "editorialNotes.badge");
        if (isSome(imageLockup.caption) && objectGraph.client.isVision) {
            imageLockup.caption = objectGraph.loc.uppercased(imageLockup.caption);
        }
        imageLockup.title =
            mediaPlatformAttributes.platformAttributeAsString(lockupData, attributePlatform, "editorialNotes.tagline") || mediaAttributes.attributeAsString(lockupData, "genreDisplayName");
        imageLockup.impressionMetrics = lockup.impressionMetrics;
        return imageLockup;
    }
    else {
        return null;
    }
}
function copyScreenshotsDisplayStyleIntoMixedMediaLockup(objectGraph, data, lockup, searchExperimentsData) {
    var _a;
    if (!objectGraph.client.isPhone) {
        return;
    }
    const displayStyle = serverData.asString(data.meta, "imageLockupFromData");
    if (serverData.isDefinedNonNull(displayStyle)) {
        lockup.screenshotsDisplayStyle = displayStyle;
    }
    else if (serverData.isDefinedNonNull((_a = searchExperimentsData === null || searchExperimentsData === void 0 ? void 0 : searchExperimentsData.displayStyle) === null || _a === void 0 ? void 0 : _a.screenshots)) {
        lockup.screenshotsDisplayStyle = searchExperimentsData.displayStyle.screenshots;
    }
}
/**
 * Create a mixed media lockup, with some specific ad-related modifications, from the supplied data.
 * @param objectGraph The App Store Object Graph.
 * @param data The data for the app to go into the lockup.
 * @param options A set of options customising the lockup.
 * @param videoConfiguration A configuration object for any videos in the lockup.
 * @param searchExperimentsData Data for any search results experiments being run currently.
 * @param applyAdOfferDisplayProperties Whether to apply the default ad OfferDisplayProperties. Some callers of this function want to enforce their own offer styling.
 * @returns A MixedMediaLockup with the desired configuration.
 */
export function mixedMediaAdLockupFromData(objectGraph, data, options, videoConfiguration, searchExperimentsData, applyAdOfferDisplayProperties = true) {
    return validation.context("mixedMediaAdLockupFromData", () => {
        const lockup = new models.MixedMediaLockup();
        if (!mediaAttributes.attributeAsBooleanOrFalse(data, "iad.format.images")) {
            copyDataIntoLockup(objectGraph, data, lockup, options, null, () => {
                copyMetadataRibbonInfoIntoLockup(objectGraph, data, lockup, searchExperimentsData, options);
            });
            lockup.screenshots = [];
        }
        else {
            copyMediaIntoMixedMediaLockup(objectGraph, data, lockup, videoConfiguration, options);
            adLockup.performAssetOverridesForMixedMediaAdLockupIfNeeded(objectGraph, data, lockup, options.metricsOptions);
            copyDataIntoLockup(objectGraph, data, lockup, options, null, () => {
                copyScreenshotsDisplayStyleIntoMixedMediaLockup(objectGraph, data, lockup, searchExperimentsData);
                copyMetadataRibbonInfoIntoLockup(objectGraph, data, lockup, searchExperimentsData, options);
            });
        }
        adLockup.performAdOverridesforLockup(objectGraph, data, lockup, options.metricsOptions, applyAdOfferDisplayProperties);
        return lockup;
    });
}
/**
 * Create an lockup that describes the arcade service lockup from upsell data.
 * This lockup:
 * - Has a title, e.g. "Apple Arcade".
 * - May have a subtitle text, e.g. "Play 100+ games".
 * - Does *NOT* have a `clickAction` on itself (There is no Arcade product page).
 * - Has two actions for each subscription state.
 */
export function arcadeLockupFromData(objectGraph, upsellData, metricsOptions, context, offerStyle, offerEnvironment) {
    return validation.context("arcadeLockupFromData", () => {
        const data = upsellData.marketingItemData;
        const lockup = new models.ArcadeLockup();
        lockup.title = objectGraph.loc.string("ARCADE_LOCKUP_TITLE", "Apple Arcade");
        const marketingItemData = upsellData.marketingItemData;
        metricsOptions = {
            ...metricsOptions,
            mercuryMetricsData: metricsUtil.marketingItemTopLevelBaseFieldsFromData(objectGraph, marketingItemData),
        };
        // This `name` attribute contains strings like "Some Games. \nAll you can play.". Weird.
        // We trim newlines for arcade service footer lockup since the multiline text looks ugly.
        let subtitle = arcadeUpsell.descriptionFromData(objectGraph, data);
        if ((subtitle === null || subtitle === void 0 ? void 0 : subtitle.length) > 0) {
            // This subtitle string on the upsell data blob is shared by many different views. However, editorial has a tendency to program explicit newlines, since some views look better with them (notably sheets).
            // For the arcade lockup on some platforms, e.g. iOS, the string appears in a vertically constrained space and we ideally want to fit in 1 line if possible, We'll trim for now... Long term we need an editorial notes key
            // for strings w/o newlines for this use case.
            const platformIgnoresSubtitleNewlines = objectGraph.host.isiOS;
            subtitle = platformIgnoresSubtitleNewlines ? subtitle.replace(/\n/g, " ") : subtitle;
            lockup.nonsubscribedSubtitle = subtitle;
            lockup.subscribedSubtitle = subtitle;
        }
        // Unsubscribed state action
        let unsubscribedAction;
        const unsubscribedActionTitle = arcadeUpsell.callToActionLabelFromData(objectGraph, data);
        const arcadeLockupInNavBarEnabled = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.client.isVision;
        if (arcadeLockupInNavBarEnabled) {
            // This is configured as an `ArcadeAction` directly if the pricing token is present.
            unsubscribedAction = arcadeUpsell.arcadeOfferButtonActionFromData(objectGraph, upsellData.marketingItemData, context, metricsOptions);
            unsubscribedAction.title = unsubscribedActionTitle;
        }
        else if ((unsubscribedActionTitle === null || unsubscribedActionTitle === void 0 ? void 0 : unsubscribedActionTitle.length) > 0) {
            unsubscribedAction = arcadeCommon.arcadeSubscribePageFlowAction(objectGraph, models.marketingItemContextFromString("editorialItemCanvas"), null, null, {
                ...metricsOptions,
                id: data.id,
            });
            unsubscribedAction.title = unsubscribedActionTitle;
        }
        else {
            // If Upsell data is misconfigured and missing description, default to opening Arcade app for unsubscribed state.
            unsubscribedAction = arcadeCommon.openArcadeMainAction(objectGraph, metricsOptions.pageInformation, metricsOptions.locationTracker);
            if (preprocessor.GAMES_TARGET) {
                unsubscribedAction.title = objectGraph.loc.string("OfferButton.Arcade.Title.Explore");
            }
            else {
                unsubscribedAction.title = objectGraph.loc.string("ARCADE_ACTION_TITLE_EXPLORE", "EXPLORE");
            }
        }
        lockup.unsubscribedButtonAction = unsubscribedAction;
        // Subscribed state action
        const subscribedAction = arcadeCommon.openArcadeMainAction(objectGraph, metricsOptions.pageInformation, metricsOptions.locationTracker);
        if (preprocessor.GAMES_TARGET) {
            subscribedAction.title = objectGraph.loc.string("OfferButton.Arcade.Title.Explore");
        }
        else {
            subscribedAction.title = objectGraph.loc.string("ARCADE_ACTION_TITLE_EXPLORE", "EXPLORE");
        }
        lockup.subscribedButtonAction = subscribedAction;
        // Impressions:
        const metricsImpressionOptions = metricsHelpersImpressions.impressionOptions(objectGraph, upsellData.marketingItemData, lockup.title, metricsOptions);
        metricsImpressionOptions.displaysArcadeUpsell = true;
        // If no targetType is provided, set the correct value for the platform.
        if (serverData.isNullOrEmpty(metricsImpressionOptions.targetType)) {
            metricsImpressionOptions.targetType = objectGraph.client.isVision ? "lockupSmall" : "lockup";
        }
        metricsHelpersImpressions.addImpressionFields(objectGraph, lockup, metricsImpressionOptions);
        const displayProperties = new models.OfferDisplayProperties("arcade", objectGraph.bag.arcadeAppAdamId, null, offerStyle, null, offerEnvironment, null, null, null, null, null, null, null, null, null, null, null, null, objectGraph.bag.arcadeProductFamilyId);
        if (preprocessor.GAMES_TARGET) {
            displayProperties.titles["subscribed"] = objectGraph.loc.string("OfferButton.Arcade.Title.Explore");
        }
        else {
            displayProperties.titles["subscribed"] = objectGraph.loc.string("ARCADE_ACTION_TITLE_EXPLORE", "EXPLORE");
        }
        lockup.offerDisplayProperties = displayProperties;
        return lockup;
    });
}
export function lockupsFromDataContainer(objectGraph, dataContainer, options) {
    if (serverData.isNull(dataContainer)) {
        return [];
    }
    return lockupsFromData(objectGraph, dataContainer.data, options);
}
export function lockupsFromData(objectGraph, dataArray, options) {
    return validation.context("lockupsFromData", () => {
        var _a;
        if (!dataArray) {
            return [];
        }
        const items = [];
        let isDeferring = false;
        for (let index = 0; index < dataArray.length; index++) {
            if (isDeferring) {
                break;
            }
            const lockupData = dataArray[index];
            if (!mediaAttributes.hasAttributes(lockupData)) {
                if (options.contentUnavailable) {
                    isDeferring = options.contentUnavailable(index, lockupData);
                }
                continue;
            }
            const lockupOptions = options.lockupOptions;
            let filter = 80894 /* filtering.Filter.All */;
            if (options.includeOrdinals) {
                const ordinal = options.ordinalDirection === "descending" ? dataArray.length - index : index + 1;
                lockupOptions.ordinal = objectGraph.loc.decimal(ordinal).toString();
            }
            if (serverData.isDefinedNonNull(options.filter)) {
                filter = options.filter;
            }
            if (filtering.shouldFilter(objectGraph, lockupData, filter) && !options.shouldShowOnUnsupportedPlatform) {
                continue;
            }
            const lockup = ((_a = options.lockupOptions.shouldCreateScreenshotsLockup) !== null && _a !== void 0 ? _a : false)
                ? screenshotsLockupFromData(objectGraph, lockupData, lockupOptions)
                : lockupFromData(objectGraph, lockupData, lockupOptions);
            if (serverData.isNull(lockup) || !lockup.isValid()) {
                continue;
            }
            items.push(lockup);
            metricsHelpersLocation.nextPosition(options.lockupOptions.metricsOptions.locationTracker);
        }
        return items;
    });
}
export function screenshotsLockupsFromData(objectGraph, dataArray, options) {
    return validation.context("screenshotsLockupsFromData", () => {
        if (!dataArray) {
            return [];
        }
        const items = [];
        for (let index = 0; index < dataArray.length; index++) {
            const lockupData = dataArray[index];
            if (serverData.isNull(lockupData.attributes)) {
                if (options.contentUnavailable) {
                    options.contentUnavailable(index, lockupData);
                }
                continue;
            }
            const lockupOptions = options.lockupOptions;
            let filter = 80894 /* filtering.Filter.All */;
            if (serverData.isDefinedNonNull(options.filter)) {
                filter = options.filter;
            }
            if (filtering.shouldFilter(objectGraph, lockupData, filter)) {
                continue;
            }
            const lockup = screenshotsLockupFromData(objectGraph, lockupData, lockupOptions);
            if (!lockup.isValid()) {
                continue;
            }
            items.push(lockup);
            metricsHelpersLocation.nextPosition(options.lockupOptions.metricsOptions.locationTracker);
        }
        return items;
    });
}
/**
 * Create an action for the provided data
 * @param objectGraph The App Store Object Graph.
 * @param data The data to create the action for.
 * @param metricsOptions The metrics options to use for the action.
 * @param clientIdentifierOverride A client identifier override, if any.
 * @param externalDeepLinkUrl A custom deeplink URL, if any.
 * @param isCppDeepLinkEligible Whether the action should be eligible for CPP Deep Links, if any. Used for restricting deep links on ads.
 * @returns A configured action for the provided data.
 */
export function actionFromData(objectGraph, data, metricsOptions, clientIdentifierOverride, externalDeepLinkUrl = null, isCppDeepLinkEligible) {
    return validation.context(`actionFromData: ${data.type}`, () => {
        switch (data.type) {
            case "apps":
            case "app-bundles": {
                return productActionFromData(objectGraph, data, metricsOptions, {
                    clientIdentifierOverride: clientIdentifierOverride,
                    externalDeepLinkUrl: externalDeepLinkUrl,
                    isCppDeepLinkEligible: isCppDeepLinkEligible,
                });
            }
            case "in-apps": {
                return iAPActionFromData(objectGraph, data, metricsOptions);
            }
            case "editorial-items": {
                return editorialItemActionFromData(objectGraph, data, metricsOptions, clientIdentifierOverride);
            }
            case "tags":
            case "editorial-pages": {
                return editorialPageActionFromData(objectGraph, data, metricsOptions);
            }
            case "multiple-system-operators":
                return msoActionFromData(objectGraph, data, metricsOptions);
            case "groupings":
                return groupingActionFromData(objectGraph, data, metricsOptions);
            case "developers":
            default: {
                return genericActionFromData(objectGraph, data, metricsOptions);
            }
        }
    });
}
function iAPActionFromData(objectGraph, data, options) {
    return validation.context("iAPActionFromData", () => {
        var _a;
        const parentData = parentDataFromInAppData(objectGraph, data);
        if (!parentData) {
            return null;
        }
        const clickAction = new models.FlowAction("product");
        const parentUrl = urls.URL.from(mediaAttributes.attributeAsString(parentData, "url"));
        // Attach the productVariantID/cppId/ppid to the parent URL, if it exists.
        const productVariantData = (_a = options.productVariantData) !== null && _a !== void 0 ? _a : variants.productVariantDataForData(objectGraph, parentData);
        const productVariantID = variants.productVariantIDForVariantData(productVariantData);
        if (serverData.isDefinedNonNull(productVariantID)) {
            parentUrl.param(Parameters.productVariantID, productVariantID);
        }
        const parentUrlString = parentUrl.toString();
        const inAppProductIdentifier = mediaAttributes.attributeAsString(data, "offerName");
        const isSubscription = mediaAttributes.attributeAsBooleanOrFalse(data, "isSubscription");
        clickAction.pageUrl = configureProductUrlFromInAppPurchase(objectGraph, parentUrlString, inAppProductIdentifier, isSubscription);
        clickAction.title = mediaAttributes.attributeAsString(data, "name");
        metricsHelpersClicks.addClickEventToAction(objectGraph, clickAction, options);
        return clickAction;
    });
}
export function editorialItemActionTypeFromData(objectGraph, data) {
    if (serverData.isNullOrEmpty(data)) {
        return 0 /* EditorialItemActionType.Unknown */;
    }
    return validation.context("editorialItemActionFromData", () => {
        const link = mediaAttributes.attributeAsDictionary(data, "link");
        if (linkIsExternal(link)) {
            return 1 /* EditorialItemActionType.ExternalLink */;
        }
        const substyle = mediaAttributes.attributeAsString(data, "displaySubStyle");
        const isListArticle = substyle === "List" || substyle === "NumberedList";
        if (mediaAttributes.attributeAsBooleanOrFalse(data, "isCanvasAvailable") || isListArticle) {
            return 2 /* EditorialItemActionType.Article */;
        }
        // This is a bit of a workaround to get the product data from `primary-content`, if it's available, for app events
        // as this is where it's currently made available for an EI.
        const primaryContent = mediaRelationship.relationshipCollection(data, "primary-content");
        const cardContents = mediaRelationship.relationshipCollection(data, "card-contents");
        const isPrimaryContentProduct = serverData.isDefinedNonNullNonEmpty(primaryContent) && primaryContent.length === 1;
        const isProduct = serverData.isDefinedNonNullNonEmpty(cardContents) && cardContents.length === 1;
        if ((isProduct && cardContents[0].type === "app-events") ||
            (isPrimaryContentProduct && primaryContent[0].type === "app-events")) {
            return 4 /* EditorialItemActionType.AppEvent */;
        }
        if (isProduct) {
            return 3 /* EditorialItemActionType.Product */;
        }
        if ((objectGraph.client.isVision || preprocessor.GAMES_TARGET) && isSome(data.href) && data.href.length > 0) {
            return 5 /* EditorialItemActionType.Href */;
        }
        return 0 /* EditorialItemActionType.Unknown */;
    });
}
/**
 * Create an action for an editorial story
 * @param objectGraph The App Store Object Graph.
 * @param data The data for the editorial story
 * @param options A set of options customising the lockup.
 * @param clientIdentifierOverride A client identifier override.
 * @param articleRecoMetricsData The recommendation metrics data for the editorial item
 * @param todayCardConfig The config options for the editorial Item
 * @param canvasFilter The canvas filter for this editorial item, used to fetch a specific story
 * @returns A click action for an editorial item
 */
export function editorialItemActionFromData(objectGraph, data, options, clientIdentifierOverride, articleRecoMetricsData, todayCardConfig) {
    if (serverData.isNullOrEmpty(data)) {
        return null;
    }
    return validation.context("editorialItemActionFromData", () => {
        var _a;
        let flowDestination;
        let pageUrlString;
        let destinationIntent;
        let presentation;
        switch (editorialItemActionTypeFromData(objectGraph, data)) {
            case 1 /* EditorialItemActionType.ExternalLink */:
                return editorialItemExternalLinkActionFromData(objectGraph, data, options);
            case 2 /* EditorialItemActionType.Article */:
                flowDestination = "article";
                const pageUrl = urls.URL.from(mediaAttributes.attributeAsString(data, "url"));
                if (serverData.isDefinedNonNull(articleRecoMetricsData)) {
                    pageUrl.param(Parameters.recoMetrics, JSON.stringify(articleRecoMetricsData));
                }
                if (objectGraph.client.isiOS && isSome(todayCardConfig) && !todayCardConfig.isHorizontalShelfContext) {
                    pageUrl.param(Parameters.todayCardConfig, JSON.stringify(todayCardConfig));
                }
                const editorialCardId = (_a = editorialCardFromData(data)) === null || _a === void 0 ? void 0 : _a.id;
                if (isSome(editorialCardId)) {
                    pageUrl.param(Parameters.editorialCardId, editorialCardId);
                }
                pageUrlString = pageUrl.build();
                if (objectGraph.client.isWeb) {
                    destinationIntent = makeRoutableArticlePageIntent({
                        ...getLocale(objectGraph),
                        ...getPlatform(objectGraph),
                        id: data.id,
                    });
                    pageUrlString = makeRoutableArticlePageCanonicalUrl(objectGraph, destinationIntent);
                }
                if (preprocessor.GAMES_TARGET) {
                    // For Luck Seed1 we always show stories as modal across all platform,
                    // in the future we should follow App Store and push them on Mac,
                    // according to the specs https://quip-apple.com/CEplADSB1MJR
                    // Need to support proper dual column layout first:
                    // rdar://141251194 ([Seed 3] [Stories] Dynamic page layout (single and dual column))
                    if (objectGraph.host.isMac) {
                        presentation = "stackPush";
                    }
                    else {
                        presentation = "sheetPresent";
                    }
                }
                break;
            case 3 /* EditorialItemActionType.Product */:
                const productData = mediaRelationship.relationshipCollection(data, "card-contents")[0];
                return actionFromData(objectGraph, productData, options, clientIdentifierOverride);
            case 4 /* EditorialItemActionType.AppEvent */:
                // This is a bit of a workaround to get the product data from `primary-content`, if it's available, for app events
                // as this is where it's currently made available for an EI.
                const primaryContent = mediaRelationship.relationshipCollection(data, "primary-content");
                const isPrimaryContentProduct = serverData.isDefinedNonNullNonEmpty(primaryContent) && primaryContent.length === 1;
                const appEventData = isPrimaryContentProduct
                    ? primaryContent[0]
                    : mediaRelationship.relationshipCollection(data, "card-contents")[0];
                const eventProductData = mediaRelationship.relationshipData(objectGraph, appEventData, "app");
                if (serverData.isNullOrEmpty(eventProductData)) {
                    return null;
                }
                const appEventOrDate = appEvent.appEventOrPromotionStartDateFromData(objectGraph, appEventData, eventProductData, false, false, "dark", "infer", false, options, false, true, null, false, false);
                // Return early if we received a Date, as this means the App Event shouldn't be accessible yet.
                if (serverData.isNull(appEventOrDate) || appEventOrDate instanceof Date) {
                    return null;
                }
                return appPromotionsCommon.detailPageClickActionFromData(objectGraph, appEventData, eventProductData, appEventOrDate, options, true);
            case 5 /* EditorialItemActionType.Href */:
                flowDestination = "page";
                pageUrlString = mediaUrlMapping.hrefToRoutableUrl(objectGraph, data.href);
                break;
            default:
                flowDestination = "unknown";
                const link = mediaAttributes.attributeAsDictionary(data, "link");
                if (objectGraph.client.isWeb) {
                    destinationIntent = makeEditorialPageIntentByID({
                        ...getLocale(objectGraph),
                        ...getPlatform(objectGraph),
                        id: data.id,
                    });
                    pageUrlString = makeEditorialPageURL(objectGraph, destinationIntent);
                }
                else {
                    pageUrlString = serverData.asString(link, "url");
                }
        }
        if (isNothing(pageUrlString)) {
            return null;
        }
        const action = new models.FlowAction(flowDestination);
        action.pageUrl = pageUrlString;
        if (isSome(presentation)) {
            action.presentation = presentation;
        }
        let title = content.notesFromData(objectGraph, data, "name");
        if (serverData.isNull(title)) {
            title = serverData.asString(data, "label");
        }
        action.title = title;
        if (destinationIntent) {
            action.destination = destinationIntent;
        }
        metricsHelpersClicks.addClickEventToAction(objectGraph, action, options);
        return action;
    });
}
/**
 * Creates a flow action to display a product page.
 * @param objectGraph Dependency soup
 * @param data The media api data to create the action from.
 * @param metricsOptions Metrics dependencies
 * @param options.clientIdentifierOverride The preferred client identifier to use for the product page.
 * @param options.externalDeepLinkUrl An external deep link for the propuct to be used when opening the app, if any.
 * @param options.isCppDeepLinkEligible Whether a CPP deep link can be used for this lockup, if available.
 * @param options.productVariantData A variant to use. This can be populated as an optimization to avoid re-resolving the same variant data, e.g. in a product page.
 * @returns A `FlowAction` object pointing to a product page.
 */
function productActionFromData(objectGraph, data, metricsOptions, options) {
    var _a, _b, _c, _d;
    if (!data) {
        return null;
    }
    const clientIdentifierOverride = (_a = options.clientIdentifierOverride) !== null && _a !== void 0 ? _a : null;
    const externalDeepLinkUrl = (_b = options.externalDeepLinkUrl) !== null && _b !== void 0 ? _b : null;
    const isCppDeepLinkEligible = (_c = options.isCppDeepLinkEligible) !== null && _c !== void 0 ? _c : false;
    const isCppDeepLinkDisabled = !isCppDeepLinkEligible;
    const productVariantData = (_d = options.productVariantData) !== null && _d !== void 0 ? _d : variants.productVariantDataForData(objectGraph, data);
    return validation.context("productActionFromData", () => {
        var _a, _b, _c;
        let productUrl = mediaAttributes.attributeAsString(data, "url");
        if (!productUrl) {
            validation.unexpectedNull("ignoredValue", "string", "url");
            return null;
        }
        let productPageOptions = {};
        const url = new urls.URL(productUrl);
        if (metricsOptions.isAdvert) {
            const lineItem = serverData.asString(data, "iad.lineItem");
            if (lineItem !== null && lineItem.length > 0) {
                url.param(metricsHelpersModels.iAdURLLineItemParameterStringToken, lineItem);
            }
            const iAdClickFields = (_a = metricsOptions.pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.clickFields;
            url.param(metricsHelpersModels.iAdURLParameterStringToken, JSON.stringify(iAdClickFields));
            productPageOptions = {
                iAdClickFields: serverData.asJSONData(iAdClickFields),
                iAdLineItem: lineItem,
            };
            const instanceId = adCommon.advertInstanceIdForData(objectGraph, data);
            if (isSome(instanceId)) {
                const advertType = content.isArcadeSupported(objectGraph, data)
                    ? "arcadeApp"
                    : "standardApp";
                const reportingDestination = reportingDestinationFromMetricsOptions(objectGraph, metricsOptions.pageInformation);
                const bundleId = mediaAttributes.attributeAsString(data, "platformAttributes.ios.bundleId");
                const isPreorder = mediaAttributes.attributeAsBooleanOrFalse(data, "isPreorder");
                const purchaseType = isPreorder ? "preorder" : "standard";
                const dismissAdActionMetrics = new AdvertActionMetrics(instanceId, data.id, bundleId, advertType, "productPageDismissed", purchaseType, reportingDestination);
                url.param(metricsHelpersModels.iAdDismissAdActionMetricsParameterStringToken, JSON.stringify(dismissAdActionMetrics));
                productPageOptions.iAdDismissAdActionMetrics = serverData.asJSONData(dismissAdActionMetrics);
            }
        }
        const productVariantID = variants.productVariantIDForVariantData(productVariantData);
        if (serverData.isDefinedNonNull(productVariantID)) {
            url.param(Parameters.productVariantID, productVariantID);
        }
        if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
            if (objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
                const alignedRegionDeeplinkUrl = adLockup.getCustomCreativeDeepLinkUrl(data);
                if (isSome(alignedRegionDeeplinkUrl)) {
                    url.param(externalDeepLink.alignedRegionDeepLinkQueryParameter, alignedRegionDeeplinkUrl);
                }
                else {
                    const tapDestinationCppId = adLockup.getTapDestinationIdForAdvert(data);
                    if (isSome(tapDestinationCppId)) {
                        url.param(Parameters.productVariantID, tapDestinationCppId);
                    }
                }
            }
        }
        productPageOptions.externalDeepLinkUrl = externalDeepLinkUrl;
        if (serverData.isDefinedNonNull(externalDeepLinkUrl)) {
            url.param(externalDeepLink.externalDeepLinkQueryParameter, externalDeepLinkUrl);
        }
        const platformFromIntent = getPlatform(objectGraph).platform;
        const platformInferredFromData = safelyInferPlatformFromData(objectGraph, data);
        if (objectGraph.client.isWeb) {
            // For the web client, we add the platform query parameter if the active intent's platform
            // differs from the "default" platform inferred from the app's data.
            if (platformFromIntent && platformFromIntent !== platformInferredFromData) {
                url.param("platform", platformFromIntent);
            }
        }
        else {
            // For non-web clients, propagate CPP, search term, and client identifier parms, which are not needed for web.
            productPageOptions.isCppDeepLinkDisabled = isCppDeepLinkDisabled;
            url.param(externalDeepLink.cppDeepLinkDisabledQueryParameter, isCppDeepLinkDisabled.toString());
            // Add a searchTerm to the product URL to propagate to purchases on the subsequent product page.
            const searchTerm = (_c = (_b = metricsOptions.pageInformation) === null || _b === void 0 ? void 0 : _b.searchTermContext) === null || _c === void 0 ? void 0 : _c.term;
            if (isSome(searchTerm)) {
                url.param("searchTerm", searchTerm);
            }
            productPageOptions.clientIdentifierOverride = clientIdentifierOverride;
            if ((clientIdentifierOverride === null || clientIdentifierOverride === void 0 ? void 0 : clientIdentifierOverride.length) > 0) {
                url.param("clientIdentifierOverride", clientIdentifierOverride);
            }
        }
        productUrl = url.toString();
        if (preprocessor.GAMES_TARGET) {
            return gameModels.viewGameActionWithMediaAPIData(data, objectGraph, makeClickMetrics(objectGraph, data.id, "lockup", "navigate", [data.id]));
        }
        const action = new models.FlowAction("product");
        action.pageUrl = productUrl;
        action.pageData = productPageUtil.createProductPageSidePackFromResponse(objectGraph, data, productPageOptions);
        action.title = mediaAttributes.attributeAsString(data, "name");
        if (metricsOptions && metricsOptions.pageInformation) {
            action.referrerUrl = metricsOptions.pageInformation.pageUrl;
        }
        // Extract and add additional options for pre-orders
        const isPreorder = mediaAttributes.attributeAsBooleanOrFalse(data, "isPreorder");
        if (isPreorder) {
            metricsHelpersClicks.addClickEventToAction(objectGraph, action, {
                ...metricsOptions,
                offerType: "preorder",
                offerReleaseDate: offers.expectedReleaseDateFromData(objectGraph, data),
            }, true);
        }
        else {
            metricsHelpersClicks.addClickEventToAction(objectGraph, action, metricsOptions, true);
        }
        const productId = serverData.asString(data, "id");
        if (isSome(productId)) {
            action.destination =
                data.type === "app-bundles"
                    ? makeBundlePageIntent({
                        ...getLocale(objectGraph),
                        id: productId,
                    })
                    : makeProductPageIntent({
                        ...getLocale(objectGraph),
                        platform: inferPreviewPlatform(objectGraph, data),
                        id: productId,
                    });
        }
        return action;
    });
}
/**
 * Safely infers a preview platform from media data, handling any potential errors.
 */
function safelyInferPlatformFromData(objectGraph, data) {
    try {
        return inferPreviewPlatformFromDeviceFamilies(objectGraph, { data: [data] });
    }
    catch (error) {
        objectGraph.console.error(`Error inferring preview platform from data: ${error}`);
        return undefined;
    }
}
function inferPreviewPlatform(objectGraph, data) {
    const platformFromIntent = getPlatform(objectGraph).platform;
    if (platformFromIntent) {
        return platformFromIntent;
    }
    return safelyInferPlatformFromData(objectGraph, data);
}
/**
 * Creates a flow action to display a grouping page
 * @param data The media api data to create the action from.
 * @returns A `FlowAction` object pointing to a grouping page.
 */
function groupingActionFromData(objectGraph, data, options) {
    if (!data) {
        return null;
    }
    return validation.context("groupingActionFromData", () => {
        if (!data.href) {
            validation.unexpectedNull("ignoredValue", "string", "href");
            return null;
        }
        const action = new models.FlowAction("page");
        if (objectGraph.client.isWeb) {
            const destination = makeGroupingPageIntentByID({
                ...getLocale(objectGraph),
                ...getPlatform(objectGraph),
                id: data.id,
            });
            action.destination = destination;
            action.pageUrl = makeGroupingPageCanonicalURL(objectGraph, destination);
        }
        else {
            action.pageUrl = mediaUrlBuilder
                .buildURLFromRequest(objectGraph, mediaUrlMapping.mediaApiGroupingURLFromHref(objectGraph, data.href))
                .toString();
        }
        action.title = nameFromGroupingData(objectGraph, data);
        metricsHelpersClicks.addClickEventToAction(objectGraph, action, options);
        return action;
    });
}
/**
 * Creates a flow action to display a mso-page.
 * @param data The media api data to create the action from.
 * @returns A `FlowAction` object pointing to a mso page.
 */
function msoActionFromData(objectGraph, data, options) {
    if (!data) {
        return null;
    }
    return validation.context("msoActionFromPlatformData", () => {
        const roomUrl = mediaAttributes.attributeAsString(data, "url");
        if (!roomUrl) {
            validation.unexpectedNull("ignoredValue", "string", "url");
            return null;
        }
        const action = new models.FlowAction("mso");
        action.pageUrl = roomUrl;
        action.title = mediaAttributes.attributeAsString(data, "name");
        metricsHelpersClicks.addClickEventToAction(objectGraph, action, options);
        return action;
    });
}
/**
 * Creates a flow action to display an editorial page.
 *
 * editorial-pages asset type does not have a url attribute, so we have to synthesize one
 * - The "web" client uses the URL defined by the `EditorialPageIntentController`
 * - Other clients use the `href` attribute
 *
 * @param data The media api data to create the action from.
 * @param options The required options passed to the lockup creation method.
 * @returns A `FlowAction` object pointing to a product page.
 */
function editorialPageActionFromData(objectGraph, data, options) {
    if (!data) {
        return null;
    }
    return validation.context("editorialPageActionFromData", () => {
        const action = new models.FlowAction("page");
        if (objectGraph.client.isWeb) {
            const editorialPageIntent = makeEditorialPageIntentByID({
                ...getLocale(objectGraph),
                ...getPlatform(objectGraph),
                id: data.id,
            });
            action.destination = editorialPageIntent;
            action.pageUrl = makeEditorialPageURL(objectGraph, editorialPageIntent);
        }
        else {
            const href = data.href;
            if (serverData.isNullOrEmpty(href)) {
                validation.unexpectedNull("ignoredValue", "string", "href");
                return null;
            }
            action.pageUrl = mediaUrlMapping.hrefToRoutableUrl(objectGraph, href);
        }
        action.title = content.editorialNotesFromData(objectGraph, data, "name");
        if (serverData.isNullOrEmpty(action.title)) {
            action.title = mediaAttributes.attributeAsString(data, "name");
        }
        metricsHelpersClicks.addClickEventToAction(objectGraph, action, options);
        return action;
    });
}
/**
 * Creates a flow action to display a generic page
 * @param data The media api data to create the action from.
 * @returns A `FlowAction` object pointing to a product page.
 */
function genericActionFromData(objectGraph, data, options) {
    if (!data) {
        return null;
    }
    return validation.context("genericActionFromData", () => {
        const type = serverData.asString(data, "type");
        const url = mediaAttributes.attributeAsString(data, "url");
        if (!url) {
            validation.unexpectedNull("ignoredValue", "string", "url");
            return null;
        }
        const action = new models.FlowAction("page");
        action.pageUrl = url;
        if (type === "groupings") {
            action.title = nameFromGroupingData(objectGraph, data);
        }
        else {
            action.title = mediaAttributes.attributeAsString(data, "name");
        }
        metricsHelpersClicks.addClickEventToAction(objectGraph, action, options);
        return action;
    });
}
/**
 * Whether or not link blob is to external url.
 * @param link JSON blob for 'link'
 * @returns boolean whether url is external link.
 */
function linkIsExternal(link) {
    const target = serverData.asString(link, "target");
    return target && target === "external";
}
/**
 * Create an `ExternalUrlAction` for an External Link card.
 * The title of the action is either the url domain or provided short description.
 * @param data Media Api data
 * @returns A configured `ExternalUrlAction` for card.
 */
export function editorialItemExternalLinkActionFromData(objectGraph, data, options) {
    return validation.context("editorialItemExternalLinkActionFromData", () => {
        const link = mediaAttributes.attributeAsDictionary(data, "link");
        const urlString = serverData.asString(link, "url");
        const action = new models.ExternalUrlAction(urlString);
        // Title is short description or url domain
        const linkDescription = content.notesFromData(objectGraph, data, "short");
        if (linkDescription) {
            action.title = linkDescription;
        }
        else {
            const url = new urls.URL(urlString);
            action.title = url.host;
        }
        metricsHelpersClicks.addClickEventToAction(objectGraph, action, options);
        return action;
    });
}
/**
 * Creates a shallow copy of the provided lockups array, overriding any of the properties
 * supplied. This function makes sure to override the properties non-destructively; that
 * is, the overrides are only applied to the copied array's lockups.
 * @param lockups The lockups to copy.
 * @param overrideStyle The style to apply for the copied lockups' offerDisplayProperties.
 * @param filterAds Whether to filter ads. In some places where we add ads to a set of lockups, showing that ad in a copied location isn't supported.
 * @returns {Lockup[]}
 */
export function shallowCopyLockupsOverridingProperties(objectGraph, lockups, overrideStyle, filterAds = false) {
    var _a, _b;
    if (!lockups) {
        return null;
    }
    // Store a count of the removed ads so we know how many items we've filtered from the beginning of the list of lockups.
    let removedAdCount = 0;
    const copies = [];
    for (const lockup of lockups) {
        if (filterAds && serverData.isDefinedNonNull((_a = lockup.searchAd) !== null && _a !== void 0 ? _a : (_b = lockup.searchAdOpportunity) === null || _b === void 0 ? void 0 : _b.searchAd)) {
            removedAdCount += 1;
            continue;
        }
        const copy = objects.shallowCopyOf(lockup);
        if (overrideStyle && copy.offerDisplayProperties) {
            copy.offerDisplayProperties = copy.offerDisplayProperties.newOfferDisplayPropertiesChangingAppearance(false, overrideStyle);
        }
        /// Adjust the impression index of the copy by the number of ads that have been removed prior to this item.
        if (removedAdCount > 0) {
            // Take a copy of the impressionMetrics.fields, as the copy is only shallow and we don't want to affect the original.
            const impressionMetricsFields = objects.shallowCopyOf(copy.impressionMetrics.fields);
            const impressionIndex = serverData.asNumber(impressionMetricsFields.impressionIndex);
            if (isSome(impressionIndex)) {
                impressionMetricsFields.impressionIndex = impressionIndex - removedAdCount;
                copy.impressionMetrics = new models.ImpressionMetrics(impressionMetricsFields, copy.impressionMetrics.id, copy.impressionMetrics.custom);
            }
        }
        copies.push(copy);
    }
    return copies;
}
/**
 * Generates a name from a grouping media api resource
 * @param data The grouping data from a genre response
 * @return {string} A string of the grouping page's name
 */
export function nameFromGroupingData(objectGraph, data) {
    const genreNames = mediaAttributes.attributeAsArrayOrEmpty(data, "genreNames");
    if (serverData.isDefinedNonNullNonEmpty(genreNames)) {
        return genreNames[0];
    }
    else {
        return mediaAttributes.attributeAsString(data, "name");
    }
}
export function deviceHasCapabilitiesFromData(objectGraph, data) {
    if (!data) {
        return false;
    }
    if (objectGraph.client.isWatch || objectGraph.client.isWeb || objectGraph.client.isCompanionVisionApp) {
        // For the "watch" client, opt out of capability checks until we land <rdar://47322408>.
        // For the "web" client, we treat it like it can run anything, since we can't infer actual capabilites.
        // For the companion app, we can't call down to the remote device to check its capabilities,
        // so we defer this check to later on.
        return true;
    }
    const requiredCapabilitiesString = content.requiredCapabilitiesFromData(objectGraph, data, objectGraph.appleSilicon.isSupportEnabled);
    if (serverData.isNullOrEmpty(requiredCapabilitiesString)) {
        // If we don't have any capabilities, this is vacuously true.
        return true;
    }
    const splitCapabilities = requiredCapabilitiesString.split(" ");
    const supportsVisionOSCompatibleIOSBinary = content.supportsVisionOSCompatibleIOSBinaryFromData(objectGraph, data);
    return objectGraph.client.deviceHasCapabilitiesIncludingCompatibilityCheckIsVisionOSCompatibleIOSApp(splitCapabilities, supportsVisionOSCompatibleIOSBinary);
}
/**
 * Finds the IAP data from an app promotion
 * @param data The app promotion data
 * @returns data of the IAP
 */
export function iapDataFromData(objectGraph, data) {
    if (!data) {
        return null;
    }
    switch (data.type) {
        case "contingent-items":
            return mediaRelationship.relationshipData(objectGraph, data, "branch");
        case "offer-items":
            return mediaRelationship.relationshipData(objectGraph, data, "salables");
        default:
            break;
    }
    return data;
}
/**
 * Finds the discounted offer data from an IAP.
 * @param data The IAP data
 * @returns the discounted offer
 */
export function discountedOfferFromData(data) {
    if (!data) {
        return null;
    }
    const contingentOffer = serverData.asDictionary(data, "meta.contingentItemOffer");
    if (serverData.isDefinedNonNullNonEmpty(contingentOffer)) {
        return contingentOffer;
    }
    const winbackOffer = serverData.asDictionary(data, "meta.discountOffer");
    if (serverData.isDefinedNonNullNonEmpty(winbackOffer)) {
        return winbackOffer;
    }
    return null;
}
/**
 * Finds the parent app data from a app promotion.
 * @param data The contingent-offer or offer-item data
 * @returns the parent app data
 */
export function parentDataFromInAppData(objectGraph, data) {
    if (!data) {
        return null;
    }
    switch (data.type) {
        case "contingent-items":
            return mediaRelationship.relationshipData(objectGraph, data, "branch-app");
        case "offer-items":
            const iapData = mediaRelationship.relationshipData(objectGraph, data, "salables");
            return mediaRelationship.relationshipData(objectGraph, iapData, "app");
        default:
            break;
    }
    return mediaRelationship.relationshipData(objectGraph, data, "app");
}
export function cleanupArcadeDownloadPackLockupMetricsIfNeeded(lockup, objectGraph) {
    if (!objectGraph.bag.arcadeDownloadPacksMetricsEventsEnabled) {
        lockup.clickAction.actionMetrics.clearAll();
        lockup.buttonAction.actionMetrics.clearAll();
        if (lockup.buttonAction instanceof models.OfferStateAction) {
            lockup.buttonAction.defaultAction.actionMetrics.clearAll();
        }
    }
    if (!objectGraph.bag.arcadeDownloadPacksImpressionEventsEnabled) {
        lockup.impressionMetrics = null;
    }
}
//# sourceMappingURL=lockups.js.map