//
//  search-ads.ts
//  AppStoreKit
//
//  Created by Joel Parsons on 24/October/2019
//  Copyright (c) 2016 Apple Inc. All rights reserved.
//
import { isSome } from "@jet/environment";
import * as models from "../../api/models";
import { ads } from "../../api/typings/constants";
import * as serverData from "../../foundation/json-parsing/server-data";
import * as mediaAttributes from "../../foundation/media/attributes";
import { shallowCopyOf } from "../../foundation/util/objects";
import { isAdLocalizationValid } from "../ads/ad-common";
import * as client from "../../foundation/wrappers/client";
import * as contentAttributes from "../content/attributes";
import * as content from "../content/content";
import * as filtering from "../filtering";
import * as lockups from "../lockups/lockups";
import * as metricsHelpersLocation from "../metrics/helpers/location";
import { customCreativeArtworkFromData, customCreativeVideoFromData } from "./custom-creative";
import { platformAttributeAsDictionary } from "../../foundation/media/platform-attributes";
import { searchResultWillUseAppEventDisplay } from "./content/search-results";
/**
 * Data passed from native for requesting Sponsored Search
 */
export class SponsoredSearchRequestData {
    constructor(data, appStoreClientRequestId) {
        if (!data) {
            return;
        }
        this.appStoreClientRequestId = appStoreClientRequestId;
        this.iAdId = data["iAdId"];
        this.sponsoredSearchRequestData = data["dataBlob"];
        this.routingInfo = data["iAdRoutingInfo"];
        this.canary = data["canary"];
    }
    validAdRequest() {
        const hasRequestData = this.sponsoredSearchRequestData && this.sponsoredSearchRequestData.length > 0;
        const hasRoutingInfo = this.routingInfo && this.routingInfo.length > 0;
        return hasRequestData && hasRoutingInfo;
    }
}
const searchVideoConfiguration = {
    canPlayFullScreen: false,
    playbackControls: {},
};
export function adsResultFromSearchResults(objectGraph, advertDatum, resultsDatum, requestMetadata, metricsOptions, installedStates, appStates, searchExperimentsData, personalizationDataContainer) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
    const advertsSearchResult = new models.AdvertsSearchResult();
    const isNetworkConstrained = (_a = requestMetadata.requestDescriptor.isNetworkConstrained) !== null && _a !== void 0 ? _a : false;
    const advertMetricsOptions = {
        id: "ad_container",
        kind: "iosSoftware",
        softwareType: null,
        targetType: null,
        title: "ad_container",
        pageInformation: metricsOptions.pageInformation,
        locationTracker: metricsOptions.locationTracker,
        idType: "sequential",
    };
    /**
     * # Why do we push ad_container?
     * We push an identifier for element that doesn't exist (`ad_container`) to prevent impression indices of organic search results
     * being affected by having multiple ads built for ad-rotation.
     *
     * It is expected that:
     * - `ad_container` will not impresss, since it doesn't exist
     * - `parentImpressionId` will not be set expected.
     */
    metricsHelpersLocation.pushContentLocation(objectGraph, advertMetricsOptions, "ad_container");
    if (serverData.isNullOrEmpty(advertDatum)) {
        return {
            result: advertsSearchResult,
        };
    }
    const firstSearchResult = resultsDatum[0];
    let firstAdComputedStyle;
    const adIdsString = advertDatum
        .filter(serverData.isDefinedNonNull)
        .map((ad) => `[${ad.id}]`)
        .join(", ");
    const adString = `Adverts received from ad server: ${adIdsString}`;
    adLogger(objectGraph, adString);
    let isFirstAd = true;
    for (const ad of advertDatum) {
        if (serverData.isNull(ad)) {
            continue;
        }
        if (filtering.shouldFilter(objectGraph, ad)) {
            adLogger(objectGraph, `[${ad.id}] filtered by shouldFilter() - app probably not supported on current os or device`);
            continue;
        }
        const isDupe = adIsDupe(ad.id, firstSearchResult === null || firstSearchResult === void 0 ? void 0 : firstSearchResult.id, installedStates);
        // Extract the iAd data dictionary based on the first organic search result.
        const adDataType = iAdDataTypeForAdvert(firstSearchResult, isDupe);
        ad.attributes["iad"] = iadAttributesForType(ad, adDataType);
        if (serverData.isNullOrEmpty(ad.attributes["iad"])) {
            adLogger(objectGraph, `[${ad.id}] filtered because no appropriate iAd dictionary was found. (Probably a server issue if hitting this)`);
            continue;
        }
        const adLockupOptions = {
            metricsOptions: {
                pageInformation: metricsOptions.pageInformation,
                locationTracker: metricsOptions.locationTracker,
                targetType: "card",
                isAdvert: true,
            },
            hideZeroRatings: true,
            artworkUseCase: 8 /* content.ArtworkUseCase.SearchIcon */,
            isNetworkConstrained: isNetworkConstrained,
            canDisplayArcadeOfferButton: content.shelfContentTypeCanDisplayArcadeOfferButtons(objectGraph, "mixedMediaLockup"),
        };
        const iAdData = contentAttributes.contentAttributeAsDictionary(objectGraph, ad, "iad");
        const iAdAllowsMedia = serverData.asBooleanOrFalse(iAdData, "format.images");
        let creativeHasArtworkToDisplay = false;
        if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
            const customCreativeData = platformAttributeAsDictionary(ad, contentAttributes.bestAttributePlatformFromData(objectGraph, ad), "creativeAttributes");
            const customCreativeArtwork = customCreativeArtworkFromData(objectGraph, ad, customCreativeData);
            const customCreativeVideo = customCreativeVideoFromData(objectGraph, ad, customCreativeData, searchVideoConfiguration);
            creativeHasArtworkToDisplay = isSome(customCreativeArtwork) || isSome(customCreativeVideo);
        }
        const noPreviousAdStyle = serverData.isNullOrEmpty(firstAdComputedStyle);
        const temporaryAdLockup = lockups.mixedMediaAdLockupFromData(objectGraph, ad, adLockupOptions, searchVideoConfiguration, searchExperimentsData);
        const platformLockupMedia = platformMediaForLockup(temporaryAdLockup);
        // Adverts should only rotate between the same display style. The first advert dictates the display style
        // for the rest of the adverts. Once we have an ad display style locked in we try to create the next ads in
        // a style compatible with the first. If it can't satisfy any compatible styles, it gets thrown away.
        const screenshotsDisplayStyle = creativeHasArtworkToDisplay
            ? "four-screenshots"
            : (_b = searchExperimentsData === null || searchExperimentsData === void 0 ? void 0 : searchExperimentsData.displayStyle) === null || _b === void 0 ? void 0 : _b.screenshots;
        const computedAdDisplayStyle = resolvedAdDisplayStyleForMedia(objectGraph, platformLockupMedia, ad.id, adDataType, iAdAllowsMedia, firstAdComputedStyle, screenshotsDisplayStyle, firstSearchResult, installedStates, appStates, metricsOptions, personalizationDataContainer);
        if (serverData.isNull(computedAdDisplayStyle)) {
            adLogger(objectGraph, `[${ad.id}] will not be displayed because we could not create an ad style compatible with ${debugDescriptionForStyle(firstAdComputedStyle)}`);
            continue;
        }
        // Check the localization is valid for the ad with the selected display style.
        // We do this _before_ storing the style (if this is the first ad) to avoid setting
        // a style based on an ad that can't be shown.
        if (!isAdLocalizationValid(objectGraph, ad, null, computedAdDisplayStyle.style)) {
            adLogger(objectGraph, `[${ad.id}] filtered because localization is not available`);
            continue;
        }
        if (noPreviousAdStyle) {
            // For the first advert we calculate a display style based on whether it is a dupe of the first organic result
            // and what media is available for display for the app
            adLogger(objectGraph, `[${ad.id}] first ad dictates ad display style of: ${debugDescriptionForStyle(computedAdDisplayStyle)}`);
            firstAdComputedStyle = computedAdDisplayStyle;
        }
        else {
            adLogger(objectGraph, `[${ad.id}] will be displayed because it is compatible with the display style of: ${debugDescriptionForStyle(computedAdDisplayStyle)}, which is the same height as display style: ${debugDescriptionForStyle(firstAdComputedStyle)}`);
        }
        metricsOptions.pageInformation.iAdInfo.apply(objectGraph, ad);
        // Set the template type before creating the lockup to ensure the click event has the right data.
        (_c = metricsOptions.pageInformation.iAdInfo) === null || _c === void 0 ? void 0 : _c.setTemplateType(computedAdDisplayStyle.style);
        let adResultLockup = lockups.mixedMediaAdLockupFromData(objectGraph, ad, adLockupOptions, searchVideoConfiguration, searchExperimentsData);
        adResultLockup = modifyLockupToMatchAdDisplayStyle(adResultLockup, computedAdDisplayStyle, isDupe, isFirstAd);
        if (objectGraph.props.enabled("advertSlotReporting")) {
            (_d = adResultLockup.searchAdOpportunity) === null || _d === void 0 ? void 0 : _d.setTemplateType(computedAdDisplayStyle.style);
        }
        else {
            (_e = adResultLockup.searchAd) === null || _e === void 0 ? void 0 : _e.setTemplateType(computedAdDisplayStyle.style);
        }
        if (computedAdDisplayStyle.style === "TEXT" /* SearchAdDisplayStyle.TEXT */) {
            const iAdTextKey = mediaAttributes.attributeAsString(ad, "iad.format.text");
            if (iAdTextKey !== "none") {
                let advertisingText;
                if (iAdTextKey === "description") {
                    advertisingText = contentAttributes.contentAttributeAsString(objectGraph, ad, "description.standard");
                }
                else {
                    advertisingText = contentAttributes.contentAttributeAsString(objectGraph, ad, iAdTextKey);
                }
                const searchAd = (_f = adResultLockup.searchAd) !== null && _f !== void 0 ? _f : (_g = adResultLockup.searchAdOpportunity) === null || _g === void 0 ? void 0 : _g.searchAd;
                if (serverData.isDefinedNonNull(searchAd) && serverData.isDefinedNonNull(advertisingText)) {
                    searchAd.advertisingText = advertisingText;
                }
            }
            advertsSearchResult.displaysScreenshots = false;
        }
        if (serverData.isDefinedNonNullNonEmpty(adResultLockup)) {
            const duplicatePosition = findOrganicLockupPosition(resultsDatum, adResultLockup.adamId);
            if (isSome(duplicatePosition) && !creativeHasArtworkToDisplay) {
                if (objectGraph.props.enabled("advertSlotReporting")) {
                    (_h = adResultLockup.searchAdOpportunity) === null || _h === void 0 ? void 0 : _h.setDuplicatePosition(duplicatePosition);
                }
                else {
                    (_j = adResultLockup.searchAd) === null || _j === void 0 ? void 0 : _j.setDuplicatePosition(duplicatePosition);
                }
            }
            advertsSearchResult.lockups.push(adResultLockup);
            metricsHelpersLocation.nextPosition(metricsOptions.locationTracker);
            isFirstAd = false;
        }
    }
    // Always pop the `ad_container` location.
    metricsHelpersLocation.popLocation(metricsOptions.locationTracker);
    // There are situations where all ads are filtered out - check the results have at least one item prior to modifying metrics data.
    if (serverData.isDefinedNonNullNonEmpty(advertsSearchResult.lockups)) {
        // Once all the lockups are built re-set the page information to use the iAd Info of the first advert
        // This (probably) happens to ensure that the page event accurately reflects what's being presented
        // to the user at first view.
        const firstAd = advertDatum[0];
        metricsOptions.pageInformation.iAdInfo.apply(objectGraph, firstAd);
        metricsHelpersLocation.nextPosition(metricsOptions.locationTracker);
    }
    if (firstAdComputedStyle) {
        (_k = metricsOptions.pageInformation.iAdInfo) === null || _k === void 0 ? void 0 : _k.setTemplateType(firstAdComputedStyle.style);
    }
    else {
        (_l = metricsOptions.pageInformation.iAdInfo) === null || _l === void 0 ? void 0 : _l.setTemplateType(null);
    }
    advertsSearchResult.condensedBehavior = "never";
    return {
        result: advertsSearchResult,
        displayStyle: firstAdComputedStyle === null || firstAdComputedStyle === void 0 ? void 0 : firstAdComputedStyle.style,
    };
}
/**
 * Determines whether or not the ad is considered a dupe of the first organic result.
 * @param adID The adaimID of the ad result
 * @param firstResultID The adamID of the first organic result
 * @param installedState A mapping of adamIDs to device app install state to determine if the ad/first result is on the user's device
 * @returns whether the ad and result are dupes of the same app and that the first result will not condensed as a condensed first result
 * will have no bearing on dupe status or display.
 */
function adIsDupe(adID, firstResultID, installedState) {
    const isResultInstalled = installedState && installedState[firstResultID];
    const isDupeResult = adID && firstResultID && adID === firstResultID;
    return isDupeResult && !isResultInstalled;
}
// endregion
// region Ad Display Styles
/**
 * Resolves a SearchAdDisplayStyle for the given ad media and the search results context (the first organic search result).
 * If the ad is a dupe of the first organic search result, we attempt to create two lockups with "full creative" to ensure there is enough
 * media to show both.
 * @param media the media from which to calculate the style.
 * @param adId the id of the ad, for logging purposes.
 * @param adDataType the ad type, based on the first search result.
 * @param firstAdComputedStyle a previously computed `SearchAdDisplayStyleContainer`, if any, to maintain compatibility.
 * @param firstSearchResult The first organic search result which we informs how to handle DUP logic
 * @param installStates A mapping of adamIDs to their respective install states that is used to determine if the app is currently installed by the user
 * @param appStates A mapping of adamIDs to their respective app state to determine if the ad/first result has been installed by the user in the past
 * @param metricsOptions Metrics options for built models.
 * @param personalizationDataContainer The data container to use for personalizing the data.
 * @returns a `SearchAdDisplayStyleContainer` with a style compatible with the first search result, if one can be found.
 */
function resolvedAdDisplayStyleForMedia(objectGraph, media, adId, adDataType, iAdAllowsMedia, firstAdComputedStyle, screenshotsDisplayStyle, firstSearchResult, installStates, appStates, metricsOptions, personalizationDataContainer) {
    const isFirstAd = serverData.isNullOrEmpty(firstAdComputedStyle);
    const adDisplayStyle = getAdDisplayStyleForMedia(objectGraph, media, adId, iAdAllowsMedia, firstAdComputedStyle, screenshotsDisplayStyle);
    if (serverData.isNull(adDisplayStyle)) {
        return null;
    }
    const searchAdDisplayContainer = {
        platform: media.mediaPlatformUsedForDisplayStyle,
        style: adDisplayStyle,
    };
    adLogger(objectGraph, `[${adId}] tentatively resolved to: ${debugDescriptionForStyle(searchAdDisplayContainer)}`);
    if (adDataType === "DUP" /* iAdDataType.DUPE_AD */) {
        removeUsedMediaForAdDisplayStyle(adDisplayStyle, media);
        const organicSearchResultDisplayStyle = getAdDisplayStyleForMedia(objectGraph, media, adId, iAdAllowsMedia, null, screenshotsDisplayStyle);
        const organicHasFullCreative = isDisplayStyleFullCreativeForOrganic(objectGraph, organicSearchResultDisplayStyle, screenshotsDisplayStyle);
        const organicWillUseAppEvent = searchResultWillUseAppEventDisplay(objectGraph, firstSearchResult, installStates, appStates, metricsOptions, personalizationDataContainer);
        if ((organicHasFullCreative || organicWillUseAppEvent) && isFirstAd) {
            adLogger(objectGraph, `[${adId}] Organic Dupe would be full creative as ${organicSearchResultDisplayStyle} so choosing tentative style for ad`);
            return searchAdDisplayContainer;
        }
        else if (organicHasFullCreative &&
            !isFirstAd &&
            isProposedAdStyleCompatible(adDisplayStyle, firstAdComputedStyle)) {
            adLogger(objectGraph, `[${adId}] Organic Dupe would be a full creative, but ad is not the first so returning compatible style with first ${adDisplayStyle}`);
            return searchAdDisplayContainer;
        }
        else if (isProposedAdStyleCompatible("UI1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_IMAGE_ONE_ASSET */, firstAdComputedStyle)) {
            adLogger(objectGraph, `[${adId}] tentative style would not yield full creative for organic result so returning UNRESTRICTED_IMAGE_ONE_ASSET`);
            return {
                style: "UI1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_IMAGE_ONE_ASSET */,
            };
        }
        else if (isProposedAdStyleCompatible("UV1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_VIDEO_ONE_ASSET */, firstAdComputedStyle)) {
            adLogger(objectGraph, `[${adId}] tentative style would not yield full creative for organic result so returning UNRESTRICTED_VIDEO_ONE_ASSET`);
            return {
                style: "UV1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_VIDEO_ONE_ASSET */,
            };
        }
        else if (isProposedAdStyleCompatible("TEXT" /* SearchAdDisplayStyle.TEXT */, firstAdComputedStyle)) {
            adLogger(objectGraph, `[${adId}] tentative style would not yield full creative for organic result so returning TEXT`);
            return {
                style: "TEXT" /* SearchAdDisplayStyle.TEXT */,
            };
        }
        else {
            adLogger(objectGraph, `[${adId}] tentative style would not yield full creative for organic result and first style is not compatible with TEXT so skipping ad`);
            return null;
        }
    }
    else if (serverData.isDefinedNonNull(firstAdComputedStyle) &&
        firstAdComputedStyle.style === "TEXT" /* SearchAdDisplayStyle.TEXT */) {
        adLogger(objectGraph, `[${adId}] tentative style would be filtered since the first ad has style: ${debugDescriptionForStyle(firstAdComputedStyle)}, so returning TEXT`);
        return {
            style: "TEXT" /* SearchAdDisplayStyle.TEXT */,
        };
    }
    return searchAdDisplayContainer;
}
function allowsFourScreenshots(screenshotsDisplayStyle) {
    if (!serverData.isDefinedNonNull(screenshotsDisplayStyle)) {
        return false;
    }
    return screenshotsDisplayStyle === "four-screenshots";
}
/**
 * Finds the position of a specific lockup within the organic search results
 * @param searchResults List of search results
 * @param adResult The adamId of the app to search for
 * @returns The 0-indexed position in the list that contains the lockup. If the organic results doesn't contain the lockup, this instead returns null.
 */
function findOrganicLockupPosition(searchResults, adamId) {
    const index = searchResults.findIndex((datum) => datum.id === adamId);
    return index === -1 ? null : index;
}
/**
 * Returns a suitable style for a given set of lockup media.
 * If a firstComputedAdStyle is provided, we check to confirm the proposed style is compatible before selecting that new style.
 * @param media a set of media from which to calculate a style.
 * @param adId the id of the ad, for logging purposes.
 * @param iAdAllowsMedia whether the iAd Data has enabled media for the given ad.
 * @param firstComputedAdStyle whether the iAd Data has enabled media for the given ad.
 * @returns the preferred display style from the provided set of media.
 */
function getAdDisplayStyleForMedia(objectGraph, media, adId, iAdAllowsMedia, firstComputedAdStyle, screenshotsDisplayStyle) {
    if (!iAdAllowsMedia) {
        adLogger(objectGraph, `[${adId}] is not allowed to display media because of iAd configuration.`);
        return "TEXT" /* SearchAdDisplayStyle.TEXT */;
    }
    if (media.mediaPlatformUsedForDisplayStyle &&
        firstComputedAdStyle &&
        firstComputedAdStyle.mediaPlatform &&
        !media.mediaPlatformUsedForDisplayStyle.isEqualTo(firstComputedAdStyle.mediaPlatform)) {
        adLogger(objectGraph, `[${adId}] filtered because media is derived from: ${media.mediaPlatformUsedForDisplayStyle.mediaType}, but first ad media is derived from: ${firstComputedAdStyle.mediaPlatform.mediaType}`);
        return null;
    }
    let displayStyle;
    let firstVideoPreview = null;
    if (serverData.isDefinedNonNullNonEmpty(media.videos)) {
        const firstVideo = media.videos[0];
        firstVideoPreview = firstVideo.preview;
    }
    if (isSome(media.alignedRegionArtwork) &&
        isProposedAdStyleCompatible("UI1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_IMAGE_ONE_ASSET */, firstComputedAdStyle)) {
        displayStyle = "UI1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_IMAGE_ONE_ASSET */;
    }
    else if (isSome(media.alignedRegionVideo) &&
        isProposedAdStyleCompatible("UV1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_VIDEO_ONE_ASSET */, firstComputedAdStyle)) {
        displayStyle = "UV1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_VIDEO_ONE_ASSET */;
    }
    else if (serverData.isDefinedNonNullNonEmpty(firstVideoPreview) &&
        firstVideoPreview.isLandscape() &&
        isProposedAdStyleCompatible("LV1" /* SearchAdDisplayStyle.LANDSCAPE_VIDEO_ONE_ASSET */, firstComputedAdStyle)) {
        // If first trailer's preview is landscape the lockup will render as landscape video
        displayStyle = "LV1" /* SearchAdDisplayStyle.LANDSCAPE_VIDEO_ONE_ASSET */;
    }
    else if (serverData.isDefinedNonNullNonEmpty(firstVideoPreview) &&
        firstVideoPreview.isPortrait() &&
        allowsFourScreenshots(screenshotsDisplayStyle) &&
        isProposedAdStyleCompatible("PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */, firstComputedAdStyle)) {
        // The assumption here is that all combinations of portrait video are compatible with each other.
        // If this changes in future, this logic will need to be revisited.
        // If first trailer's preview is portrait the lockup will render as portrait with portrait images filled in
        if (serverData.isDefinedNonNullNonEmpty(media.portraitScreenshots) && media.portraitScreenshots.length >= 3) {
            displayStyle = "PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */;
        }
        else if (serverData.isDefinedNonNullNonEmpty(media.portraitScreenshots) &&
            media.portraitScreenshots.length >= 2) {
            displayStyle = "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */;
        }
        else if (serverData.isDefinedNonNullNonEmpty(media.portraitScreenshots)) {
            displayStyle = "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */;
        }
        else {
            displayStyle = "PV1" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_ONE_ASSET */;
        }
    }
    else if (serverData.isDefinedNonNullNonEmpty(firstVideoPreview) &&
        firstVideoPreview.isPortrait() &&
        isProposedAdStyleCompatible("PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */, firstComputedAdStyle)) {
        // The assumption here is that all combinations of portrait video are compatible with each other.
        // If this changes in future, this logic will need to be revisited.
        // If first trailer's preview is portrait the lockup will render as portrait with portrait images filled in
        if (serverData.isDefinedNonNullNonEmpty(media.portraitScreenshots) && media.portraitScreenshots.length >= 2) {
            displayStyle = "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */;
        }
        else if (serverData.isDefinedNonNullNonEmpty(media.portraitScreenshots)) {
            displayStyle = "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */;
        }
        else {
            displayStyle = "PV1" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_ONE_ASSET */;
        }
    }
    else if (serverData.isDefinedNonNullNonEmpty(media.landscapeScreenshots) &&
        isProposedAdStyleCompatible("LI1" /* SearchAdDisplayStyle.LANDSCAPE_IMAGE_ONE_ASSET */, firstComputedAdStyle)) {
        displayStyle = "LI1" /* SearchAdDisplayStyle.LANDSCAPE_IMAGE_ONE_ASSET */;
    }
    else if (serverData.isDefinedNonNullNonEmpty(media.portraitScreenshots) &&
        allowsFourScreenshots(screenshotsDisplayStyle) &&
        isProposedAdStyleCompatible("PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */, firstComputedAdStyle)) {
        // The assumption here is that all combinations of portrait video are compatible with each other.
        // If this changes in future, this logic will need to be revisited.
        if (media.portraitScreenshots.length >= 4) {
            displayStyle = "PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */;
        }
        else if (media.portraitScreenshots.length >= 3) {
            displayStyle = "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */;
        }
        else if (media.portraitScreenshots.length >= 2) {
            displayStyle = "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */;
        }
        else {
            displayStyle = "PI1" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_ONE_ASSET */;
        }
    }
    else if (serverData.isDefinedNonNullNonEmpty(media.portraitScreenshots) &&
        isProposedAdStyleCompatible("PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */, firstComputedAdStyle)) {
        // The assumption here is that all combinations of portrait video are compatible with each other.
        // If this changes in future, this logic will need to be revisited.
        if (media.portraitScreenshots.length >= 3) {
            displayStyle = "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */;
        }
        else if (media.portraitScreenshots.length >= 2) {
            displayStyle = "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */;
        }
        else {
            displayStyle = "PI1" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_ONE_ASSET */;
        }
    }
    else if (isProposedAdStyleCompatible("TEXT" /* SearchAdDisplayStyle.TEXT */, firstComputedAdStyle)) {
        displayStyle = "TEXT" /* SearchAdDisplayStyle.TEXT */;
    }
    else {
        adLogger(objectGraph, `[${adId}] filtered because we could not create a compatible style for the first style of: ${debugDescriptionForStyle(firstComputedAdStyle)}`);
        return null;
    }
    if (completePortraitMediaCount(objectGraph, screenshotsDisplayStyle) === 2) {
        if (displayStyle === "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */) {
            displayStyle = "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */;
        }
        else if (displayStyle === "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */) {
            displayStyle = "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */;
        }
    }
    return displayStyle;
}
/**
 * Removes media used by the nominated displayStyle.
 * Used in the case of a dupe where we need to confirm we have "full creative" for a subsequent presentation of a lockup.
 * @param lockup the lockup to modify
 * @param displayStyle the display style previously calculated as suitable for the lockup
 */
function removeUsedMediaForAdDisplayStyle(displayStyle, media) {
    // In the case of any video assets being used, as per the implementation in `getAdDisplayStyleForMedia`,
    // we always pick the first video, so whether it's portrait or landscape just remove the first.
    switch (displayStyle) {
        case "PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */:
            // We don't want to splice here as we might need these for reuse on dupe ads
            if (media.portraitScreenshots.length <= 5) {
                media.portraitScreenshots.splice(0, 4);
            }
            break;
        case "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */:
            media.portraitScreenshots.splice(0, 3);
            break;
        case "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */:
            media.portraitScreenshots.splice(0, 2);
            break;
        case "PI1" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_ONE_ASSET */:
            media.portraitScreenshots.splice(0, 1);
            break;
        case "PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */:
            // We will keep the video splice as the video is always first, so it will never be reused between the ad and organic
            media.videos.splice(0, 1);
            // We don't want to splice here as we might need these for reuse on dupe ads
            if (media.portraitScreenshots.length <= 4) {
                media.portraitScreenshots.splice(0, 3);
            }
            break;
        case "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */:
            media.videos.splice(0, 1);
            media.portraitScreenshots.splice(0, 2);
            break;
        case "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */:
            media.videos.splice(0, 1);
            media.portraitScreenshots.splice(0, 1);
            break;
        case "LI1" /* SearchAdDisplayStyle.LANDSCAPE_IMAGE_ONE_ASSET */:
            media.landscapeScreenshots.splice(0, 1);
            break;
        case "PV1" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_ONE_ASSET */:
        case "LV1" /* SearchAdDisplayStyle.LANDSCAPE_VIDEO_ONE_ASSET */:
            media.videos.splice(0, 1);
            break;
        default:
            break;
    }
}
/**
 * A function to determine whether a calculated organic search display style is considered "full creative".
 * @param displayStyle the display style calculated for an organic dupe result to validate.
 * @returns whether the given display style is considered "full creative" for an organic result.
 */
function isDisplayStyleFullCreativeForOrganic(objectGraph, displayStyle, screenshotsDisplayStyle) {
    switch (displayStyle) {
        case "PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */:
        case "PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */:
            return true;
        case "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */:
        case "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */:
            return completePortraitMediaCount(objectGraph, screenshotsDisplayStyle) === 3;
        case "LI1" /* SearchAdDisplayStyle.LANDSCAPE_IMAGE_ONE_ASSET */:
        case "LV1" /* SearchAdDisplayStyle.LANDSCAPE_VIDEO_ONE_ASSET */:
            return true;
        case "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */:
        case "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */:
            return completePortraitMediaCount(objectGraph) === 2;
        default:
            return false;
    }
}
/**
 * Indicates whether the proposed ad style is compatible with a previously computed ad style.
 * If there is no previously computed style, we return true as it's assumed the new style is compatible.
 * @param proposedAdStyle
 * @param firstAdComputedStyle
 * @returns a boolean indicating whether the styles are compatible.
 */
function isProposedAdStyleCompatible(proposedAdStyle, firstComputedAdStyle) {
    if (serverData.isNull(firstComputedAdStyle)) {
        return true;
    }
    let areStylesCompatible = true;
    switch (proposedAdStyle) {
        case "TEXT" /* SearchAdDisplayStyle.TEXT */:
            areStylesCompatible = firstComputedAdStyle.style === "TEXT" /* SearchAdDisplayStyle.TEXT */;
            break;
        case "LV1" /* SearchAdDisplayStyle.LANDSCAPE_VIDEO_ONE_ASSET */:
        case "LI1" /* SearchAdDisplayStyle.LANDSCAPE_IMAGE_ONE_ASSET */:
            areStylesCompatible =
                firstComputedAdStyle.style === "LV1" /* SearchAdDisplayStyle.LANDSCAPE_VIDEO_ONE_ASSET */ ||
                    firstComputedAdStyle.style === "LI1" /* SearchAdDisplayStyle.LANDSCAPE_IMAGE_ONE_ASSET */;
            break;
        case "PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */:
        case "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */:
        case "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */:
        case "PI1" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_ONE_ASSET */:
        case "PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */:
        case "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */:
        case "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */:
        case "PV1" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_ONE_ASSET */:
            areStylesCompatible =
                firstComputedAdStyle.style === "PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */ ||
                    firstComputedAdStyle.style === "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */ ||
                    firstComputedAdStyle.style === "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */ ||
                    firstComputedAdStyle.style === "PI1" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_ONE_ASSET */ ||
                    firstComputedAdStyle.style === "PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */ ||
                    firstComputedAdStyle.style === "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */ ||
                    firstComputedAdStyle.style === "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */ ||
                    firstComputedAdStyle.style === "PV1" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_ONE_ASSET */;
            break;
        case "UI1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_IMAGE_ONE_ASSET */:
            areStylesCompatible = firstComputedAdStyle.style === "UI1_3x2" /* SearchAdDisplayStyle.UNRESTRICTED_IMAGE_ONE_ASSET */;
            break;
        default:
            areStylesCompatible = false;
    }
    return areStylesCompatible;
}
/**
 * Extract the best media for the platform from the lockup.
 * @param lockup a lockup created from which we should extract the media.
 * @returns the best media for the platform.
 */
function platformMediaForLockup(lockup) {
    let mediaPlatformUsedForDisplayStyle = null;
    // This works on the assumption that when a lockup is created screenshots and trailers are sorted
    // and the first in the array of each is what we attempt to display.
    const firstTrailer = lockup.trailers[0];
    let videos = null;
    if (serverData.isDefinedNonNullNonEmpty(firstTrailer)) {
        firstTrailer.videos.sort((a, b) => {
            return artworkSortingFunction(a.preview, b.preview);
        });
        videos = firstTrailer.videos;
        mediaPlatformUsedForDisplayStyle = firstTrailer.mediaPlatform;
    }
    // Grab the first array of platform screenshots, the one the search ad will display.
    // Additional elements in this array might be for other platforms we wouldn't
    // display (e.g. if current device is an iPhone then [1] might be iPad screenshots)
    const platformScreenshots = lockup.screenshots[0];
    // Split the screenshots into portrait and landscape arrays.
    // Landscape is preferred, so we use those first before portrait images, but we also need to know
    // of any portrait screenshots so we can fill in any space left after a portrait video, which is
    // preferred over a landscape screenshot.
    const portraitScreenshots = [];
    const landscapeScreenshots = [];
    if (serverData.isDefinedNonNullNonEmpty(platformScreenshots)) {
        platformScreenshots.artwork.forEach((artwork) => {
            if (artwork.isPortrait()) {
                portraitScreenshots.push(artwork);
            }
            else {
                landscapeScreenshots.push(artwork);
            }
        });
        mediaPlatformUsedForDisplayStyle = platformScreenshots.mediaPlatform;
    }
    return {
        portraitScreenshots: portraitScreenshots,
        landscapeScreenshots: landscapeScreenshots,
        alignedRegionArtwork: lockup.alignedRegionArtwork,
        alignedRegionVideo: lockup.alignedRegionVideo,
        videos: videos,
        mediaPlatformUsedForDisplayStyle: mediaPlatformUsedForDisplayStyle,
    };
}
// endregion
function iadAttributesForType(data, type) {
    let iAdDictionaryToUse = null;
    const iAdOptions = mediaAttributes.attributeAsDictionary(data, "iads");
    const iAdJSONStringToUse = serverData.asString(iAdOptions, type);
    if (iAdJSONStringToUse && iAdJSONStringToUse.length) {
        iAdDictionaryToUse = JSON.parse(iAdJSONStringToUse);
    }
    return iAdDictionaryToUse;
}
function iAdDataTypeForAdvert(firstSearchResult, isDupe) {
    if (serverData.isNullOrEmpty(firstSearchResult)) {
        return "NOORGANIC" /* iAdDataType.NO_ORGANIC_RESULTS */;
    }
    if (isDupe) {
        return "DUP" /* iAdDataType.DUPE_AD */;
    }
    return "NORMAL" /* iAdDataType.NORMAL */;
}
/**
 * Removes unused media from the provided ad lockup, matching the selected ad display style.
 * @param ad the ad lockup from which media should be removed.
 * @param searchAdDisplayStyle the selected `SearchAdDisplayStyle` to match.
 * @param isDupe if the given ad is a dupe of the first organic search result.
 * @param isFirstAd if the given ad is the first search ad.
 * @returns
 */
function modifyLockupToMatchAdDisplayStyle(ad, searchAdDisplayStyle, isDupe, isFirstAd) {
    var _a, _b;
    // Derive whether the ad lockup has a CPP (custom product page/ppid) that's being used for it's assets.
    const hasCPP = serverData.isDefinedNonNullNonEmpty((_b = (_a = ad.impressionMetrics) === null || _a === void 0 ? void 0 : _a.fields) === null || _b === void 0 ? void 0 : _b.pageCustomId);
    // If this ad lockup is a duplicate of the first organic search result and is not the first received ad,
    // the organic search result gets the "first choice" of media. This flag indicates whether we should keep
    // the "first set" or "second set" of media for this ad lockup.
    // This logic does *not* apply if the ad is using a CPP. This is because the CPP from an ad only gets applied
    // to an organic dupe result where the matching ad is in the first position. If not, the ad and organic are
    // using a different set of assets, so there's no need to prefer one with the "first" or "second" set of media.
    const wantsSecondSetOfMedia = isDupe && !isFirstAd && !hasCPP;
    if (serverData.isDefinedNonNullNonEmpty(ad.trailers)) {
        const firstTrailers = ad.trailers.shift();
        firstTrailers.videos.sort((a, b) => {
            return artworkSortingFunction(a.preview, b.preview);
        });
        ad.trailers.unshift(firstTrailers);
    }
    // Split the screenshots into portrait and landscape arrays.
    // When removing screenshots from the array below, we can't just assume the first ones match the orientation
    // we're using. For example, we only use portrait images with a portait video and some landscape images could
    // be mixed in.
    let mediaPlatformUsedForDisplayStyle;
    let portraitScreenshots = [];
    let landscapeScreenshots = [];
    if (serverData.isDefinedNonNullNonEmpty(ad.screenshots)) {
        const firstScreenshots = ad.screenshots.shift();
        firstScreenshots.artwork.forEach((artwork) => {
            if (artwork.isPortrait()) {
                portraitScreenshots.push(artwork);
            }
            else {
                landscapeScreenshots.push(artwork);
            }
        });
        mediaPlatformUsedForDisplayStyle = firstScreenshots.mediaPlatform;
    }
    switch (searchAdDisplayStyle.style) {
        case "PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */:
            // We've previously determined advert can support this type. Remove videos and landscape images so
            // advert only displays portrait images.
            // With 4 portrait assets, we need to make sure that the ads and organic can be full by borrowing some assets from the organic.
            // However, per product, if the app has 5 or less portrait assets, then it will go to the text ad as today.
            // So we need to check for 5 assets as the lower bound (exclusive) and 8 (exclusive) as the upper for
            // the extra work to reuse assets.
            ad.trailers = null;
            landscapeScreenshots = null;
            ad.screenshotsDisplayStyle = "four-screenshots";
            if (wantsSecondSetOfMedia) {
                if (portraitScreenshots.length > 5 && portraitScreenshots.length < 8) {
                    const usedAssets = portraitScreenshots.splice(0, 4);
                    const reuseCount = 4 - portraitScreenshots.length;
                    const reuseAssets = usedAssets.splice(usedAssets.length - reuseCount);
                    portraitScreenshots.unshift(...reuseAssets);
                }
                else {
                    portraitScreenshots.splice(0, 4);
                }
            }
            else {
                portraitScreenshots.splice(4);
            }
            break;
        case "PI3" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_THREE_ASSETS */:
            // We've previously determined advert can support this type. Remove videos and landscape images so
            // advert only displays portrait images.
            ad.trailers = null;
            landscapeScreenshots = null;
            if (wantsSecondSetOfMedia) {
                portraitScreenshots.splice(0, 3);
            }
            else {
                portraitScreenshots.splice(3);
            }
            break;
        case "PI2" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_TWO_ASSETS */:
            // We've previously determined advert can support this type. Remove videos and landscape images so
            // advert only displays portrait images.
            ad.trailers = null;
            landscapeScreenshots = null;
            if (wantsSecondSetOfMedia) {
                portraitScreenshots.splice(0, 2);
            }
            else {
                portraitScreenshots.splice(2);
            }
            break;
        case "PI1" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_ONE_ASSET */:
            // We've previously determined advert can support this type. Remove videos and landscape images so
            // advert only displays portrait images.
            ad.trailers = null;
            landscapeScreenshots = null;
            if (wantsSecondSetOfMedia) {
                portraitScreenshots.splice(0, 1);
            }
            else {
                portraitScreenshots.splice(1);
            }
            break;
        case "LI1" /* SearchAdDisplayStyle.LANDSCAPE_IMAGE_ONE_ASSET */:
            // We've previously determined advert can support this type. Remove videos and portrait images so
            // advert only displays landscape images.
            ad.trailers = null;
            portraitScreenshots = null;
            if (wantsSecondSetOfMedia) {
                landscapeScreenshots.splice(0, 1);
            }
            else {
                landscapeScreenshots.splice(1);
            }
            break;
        case "PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */:
            // We've previously determined advert can support this type. Remove landscape images so
            // advert only displays portrait assets.
            // With 4 portrait assets, we need to make sure that the ads and organic can be full by borrowing some assets from the organic.
            // However, per product, if the app has 5 or less portrait assets, then it will go to the text ad as today.
            // Since this style includes a portrait video, we only need to check for 4 assets (exclusive) as the lower bound and 7 (exclusive) as the upper for
            // the extra work to reuse assets
            landscapeScreenshots = null;
            ad.screenshotsDisplayStyle = "four-screenshots";
            if (wantsSecondSetOfMedia) {
                ad.trailers[0].videos.splice(0, 1);
                if (portraitScreenshots.length > 4 && portraitScreenshots.length < 7) {
                    const usedAssets = portraitScreenshots.splice(0, 3);
                    const reuseCount = 3 - portraitScreenshots.length;
                    const reuseAssets = usedAssets.splice(usedAssets.length - reuseCount);
                    portraitScreenshots.unshift(...reuseAssets);
                }
                else {
                    portraitScreenshots.splice(0, 3);
                }
            }
            else {
                ad.trailers[0].videos.splice(1);
                portraitScreenshots.splice(3);
            }
            break;
        case "PV3" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_THREE_ASSETS */:
            // We've previously determined advert can support this type. Remove landscape images so
            // advert only displays portrait assets.
            landscapeScreenshots = null;
            if (wantsSecondSetOfMedia) {
                ad.trailers[0].videos.splice(0, 1);
                portraitScreenshots.splice(0, 2);
            }
            else {
                ad.trailers[0].videos.splice(1);
                portraitScreenshots.splice(2);
            }
            break;
        case "PV2" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_TWO_ASSETS */:
            // We've previously determined advert can support this type. Remove landscape images so
            // advert only displays portrait assets.
            landscapeScreenshots = null;
            if (wantsSecondSetOfMedia) {
                ad.trailers[0].videos.splice(0, 1);
                portraitScreenshots.splice(0, 1);
            }
            else {
                ad.trailers[0].videos.splice(1);
                portraitScreenshots.splice(1);
            }
            break;
        case "LV1" /* SearchAdDisplayStyle.LANDSCAPE_VIDEO_ONE_ASSET */:
        case "PV1" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_ONE_ASSET */:
            // We've determined advert can support this type. Remove images so only the single video displays
            if (wantsSecondSetOfMedia) {
                ad.trailers[0].videos.splice(0, 1);
            }
            else {
                ad.trailers[0].videos.splice(1);
            }
            landscapeScreenshots = null;
            portraitScreenshots = null;
            break;
        case "TEXT" /* SearchAdDisplayStyle.TEXT */:
            // All adverts can support text only, remove all screenshots and trailers so they display in this style
            ad.trailers = null;
            landscapeScreenshots = null;
            portraitScreenshots = null;
            break;
        default:
            break;
    }
    if (serverData.isDefinedNonNullNonEmpty(ad.trailers)) {
        const firstTrailers = ad.trailers.shift();
        ad.trailers = [firstTrailers];
    }
    // Combine the remaining landscape and portrait screenshots, and set them back on the ad as the only values.
    const allScreenshots = [].concat(...[landscapeScreenshots, portraitScreenshots].filter(serverData.isDefinedNonNull));
    if (serverData.isDefinedNonNullNonEmpty(allScreenshots)) {
        const screenshots = new models.Screenshots(allScreenshots, mediaPlatformUsedForDisplayStyle);
        ad.screenshots = [screenshots];
    }
    else {
        ad.screenshots = null;
    }
    return ad;
}
function completePortraitMediaCount(objectGraph, searchExperimentsData = null) {
    if (objectGraph.client.isPhone) {
        return allowsFourScreenshots(searchExperimentsData) ? 4 : 3;
    }
    else {
        return 2;
    }
}
function debugDescriptionForStyle(styleContainer) {
    if (serverData.isNullOrEmpty(styleContainer)) {
        return "";
    }
    let mediaTypeString = "";
    if (styleContainer && styleContainer.mediaPlatform) {
        mediaTypeString = ` derived from ${styleContainer.mediaPlatform.mediaType} media`;
    }
    return `${styleContainer.style}${mediaTypeString}`;
}
/**
 * Stores a value representing the current setting of the native ad debug logging.
 * This starts as `null`, and the first time we log we ask native to provide the current value.
 * Requires a re-bootstrap to update, so we avoid sending a message back to native on every call
 * if logging is disabled.
 */
let isNativeAdLoggingEnabled = null;
export function adLogger(objectGraph, message) {
    objectGraph.console.log(`[Ads] ${message}`);
    if (objectGraph.client.buildType === "internal" &&
        objectGraph.isAvailable(ads) &&
        serverData.isDefinedNonNull(objectGraph.ads.debugLog)) {
        // If we haven't asked native for whether the debug setting for ad logging is enabled, do it now.
        if (serverData.isNull(isNativeAdLoggingEnabled) &&
            serverData.isDefinedNonNull(objectGraph.ads.isNativeAdLoggingEnabled)) {
            isNativeAdLoggingEnabled = objectGraph.ads.isNativeAdLoggingEnabled();
        }
        if (isNativeAdLoggingEnabled) {
            objectGraph.ads.debugLog(message);
        }
    }
}
const artworkSortingFunction = (a, b) => {
    const aLandscape = a.isLandscape();
    const bLandscape = b.isLandscape();
    if (aLandscape === bLandscape) {
        return 0;
    }
    if (aLandscape) {
        return -1;
    }
    return 1;
};
/**
 * A wrapper around the ad search result to include the ad's displayStyle it will present with
 */
export class SearchAdsDisplayStyleResultContainer {
}
/**
 * Removes any media used in a provided ad search result from the organic search result.
 * @param objectGraph the Object Graph
 * @param adResult the ad search result
 * @param searchResult the organic search result
 * @param searchExperimentsData the metadata for search result experiemnts
 * @param searchAdDisplayStyle the display style for the ad that is matching with the organic
 */
export function dedupeAdMediaFromMatchingResult(objectGraph, adResult, searchResult, searchExperimentsData, searchAdDisplayStyle) {
    var _a;
    // Run de-duping on `AppSearchResult`s and `AppEventSearchResult`s.
    // We specifically run this for `AppEventSearchResult`s because they can be presented in the search results
    // as either a regular search result (ie. MixedMediaLockup) (if the app isn't installed), or as an IAE search
    // result (if it is installed). This means we need to run de-duping in case it appears as a regular result,
    // matching behaviour of the `AppSearchResult`.
    if (!(searchResult instanceof models.AppSearchResult || searchResult instanceof models.AppEventSearchResult)) {
        return;
    }
    const searchLockup = searchResult.lockup;
    const adLockup = adResult.lockups[0];
    if (adLockup.adamId !== searchLockup.adamId) {
        return;
    }
    const usedTemplateUrls = new Set();
    if (serverData.isDefinedNonNullNonEmpty(adLockup.screenshots)) {
        for (const screenshot of adLockup.screenshots[0].artwork) {
            usedTemplateUrls.add(screenshot.template);
        }
    }
    if (serverData.isDefinedNonNullNonEmpty(adLockup.trailers)) {
        for (const video of adLockup.trailers[0].videos) {
            usedTemplateUrls.add(video.preview.template);
        }
    }
    if (serverData.isDefinedNonNullNonEmpty(searchLockup.screenshots)) {
        const filteredArtwork = searchLockup.screenshots[0].artwork.filter((artwork) => {
            return !usedTemplateUrls.has(artwork.template);
        });
        searchLockup.screenshots[0] = new models.Screenshots(filteredArtwork, searchLockup.screenshots[0].mediaPlatform);
    }
    if (serverData.isDefinedNonNullNonEmpty(searchLockup.trailers)) {
        const filteredVideos = searchLockup.trailers[0].videos.filter((video) => {
            return !usedTemplateUrls.has(video.preview.template);
        });
        searchLockup.trailers[0] = new models.Trailers(filteredVideos, searchLockup.trailers[0].mediaPlatform);
    }
    /// Exit early if we don't need to do anything special for the 4 screenshots case
    if (((_a = searchExperimentsData === null || searchExperimentsData === void 0 ? void 0 : searchExperimentsData.displayStyle) === null || _a === void 0 ? void 0 : _a.screenshots) !== "four-screenshots") {
        return;
    }
    // We need to possibly reuse some of the screenshots from the ad to make sure the organic has enough screenshots to show;
    // This is only if we will show 4 portrait assets
    const padSearchLockupScreenshotsToPreferredSize = (totalRequiredArtwork) => {
        const currentScreenshots = searchLockup.screenshots[0].artwork;
        if (currentScreenshots.length >= totalRequiredArtwork) {
            return;
        }
        let screenshotsStillNeededCount = totalRequiredArtwork - currentScreenshots.length;
        const adArtworksToReuse = adLockup.screenshots[0].artwork.slice().reverse();
        for (const artwork of adArtworksToReuse) {
            if (screenshotsStillNeededCount <= 0) {
                return;
            }
            searchLockup.screenshots[0].artwork.unshift(artwork);
            screenshotsStillNeededCount -= 1;
        }
    };
    switch (searchAdDisplayStyle) {
        case "PV4" /* SearchAdDisplayStyle.PORTRAIT_VIDEO_FOUR_ASSETS */:
        case "PI4" /* SearchAdDisplayStyle.PORTRAIT_IMAGE_FOUR_ASSETS */:
            padSearchLockupScreenshotsToPreferredSize(4);
            break;
        default:
            break;
    }
}
/**
 * As part of the CPP implementation for Search Results ads, if the first ad result has a CPP applied, and there is a dupe scenario
 * (ie. the first ad matches the first organic result) the CPP must also be applied to the first organic result.
 * Here we update the CPP data on the organic result to match the first ad result, if applicable.
 * @param objectGraph The object graph
 * @param adData The raw ad data used to build the ad results.
 * @param adResult The built ad result.
 * @param searchResultData The data for the first organic search result.
 * @param installStates A mapping of adamIDs to their respective install states that is used to determine if the app is currently installed by the user
 * @param appStates A mapping of adamIDs to their respective app state to determine if the ad/first result has been installed by the user in the past
 * @param metricsOptions Metrics options for built models.
 * @param personalizationDataContainer The data container to use for personalizing the data.
 */
export function updateDupeOrganicResultCPPData(objectGraph, adData, adResult, searchResultData, installStates, appStates, metricsOptions, personalizationDataContainer) {
    var _a, _b;
    const adLockup = adResult.lockups[0];
    if (adLockup.adamId !== searchResultData.id) {
        return;
    }
    // Get the data for the first ad lockup. This *should* just be the first, but
    // it's possible the first data got dropped when building the ad lockups.
    const dataForAdLockup = adData.find((data) => data.id === adLockup.adamId);
    const shouldSkipUpdatingOrganicCPP = searchResultWillUseAppEventDisplay(objectGraph, searchResultData, installStates, appStates, metricsOptions, personalizationDataContainer);
    if (shouldSkipUpdatingOrganicCPP) {
        return;
    }
    // The first ad lockup matches the first organic result. Apply the Ad cppId to matching organic result.
    updatePPIDInData((_b = (_a = dataForAdLockup === null || dataForAdLockup === void 0 ? void 0 : dataForAdLockup.meta) === null || _a === void 0 ? void 0 : _a.cppData) === null || _b === void 0 ? void 0 : _b["ppid"], searchResultData);
}
/**
 * Update the ppid for the given data.
 * If provided `ppid` is `null`, the field will be deleted.
 * If `cppData` does not already exist on `meta`, it will be created.
 * @param ppid The ppid to update in the data. Can be null, which will delete the field.
 * @param data The data object to update.
 */
function updatePPIDInData(ppid, data) {
    var _a;
    let meta = shallowCopyOf(data.meta);
    if (serverData.isNull(ppid)) {
        (_a = meta === null || meta === void 0 ? void 0 : meta.cppData) === null || _a === void 0 ? true : delete _a["ppid"];
    }
    else {
        if (serverData.isNull(meta)) {
            meta = {};
        }
        if (serverData.isNull(meta.cppData)) {
            meta.cppData = {};
        }
        meta.cppData["ppid"] = ppid;
    }
    data.meta = meta;
}
/**
 * Whether or not platform supports adverts.
 */
export function platformSupportsAdverts(objectGraph) {
    return ((clientIdentifierSupportsAdverts(objectGraph) && objectGraph.host.isiOS) ||
        objectGraph.host.platform === "unknown");
}
function clientIdentifierSupportsAdverts(objectGraph) {
    return (objectGraph.host.clientIdentifier === client.appStoreIdentifier ||
        objectGraph.host.clientIdentifier === client.productPageExtensionIdentifier);
}
//# sourceMappingURL=search-ads.js.map