/**
 * Builder code specific to Ad Lockups
 */
import { isSome } from "@jet/environment";
import { AdInteractionAction, AdTransparencyAction, CompoundAction, Screenshots, SearchAd, SearchAdOpportunity, } from "../../api/models";
import { AdvertActionMetrics, } from "../../api/models/metrics/advert-action-metrics";
import { isDefinedNonNull, isDefinedNonNullNonEmpty, isNull, asBooleanOrFalse, asArray, } from "../../foundation/json-parsing/server-data";
import { attributeAsArrayOrEmpty, attributeAsBooleanOrFalse, attributeAsDictionary, attributeAsString, } from "../../foundation/media/attributes";
import { advertInstanceIdForData } from "../ads/ad-common";
import * as content from "../content/content";
import { addClickEventsToAdLockup } from "../metrics/helpers/clicks";
import { addImpressionsFieldsToAd, impressionOptionsForLockup } from "../metrics/helpers/impressions";
import { metricsPlatformDisplayStyleFromData } from "../metrics/helpers/util";
import { appDataHasVariant } from "../product-page/product-page-variants";
import { adLogger } from "../search/search-ads";
import { asString } from "@apple-media-services/media-api";
// region Shared
/**
 * Whether or not data is for ad.
 *
 * Note that this attribute *may not be initially part of the original response*.
 * - Sponsored Search: Since Ad Rotation, `iad` attribute is populated from contents of `iads` dictionary.
 * - Search Landing: `iad` attribute is populated from contents of `OnDeviceAdvert`.
 *
 * @param data Data to check if it is an ad.
 * @see`iadAttributesForType` and `decorateiAdAttributeFromOnDeviceAd`
 */
export function isAdvert(objectGraph, data) {
    const adDictionary = attributeAsDictionary(data, "iad");
    return isDefinedNonNullNonEmpty(adDictionary);
}
// endregion
// region Data Overrides
/**
 * Populates Ad-specific customizations for given lockup to a **otherwise fully configured ad lockup**
 * @param objectGraph The object graph.
 * @param data Data that will provide the ad data.
 * @param lockup The lockup to modify.
 * @param metricsOptions The lockup metrics options.
 * @param applyAdOfferDisplayProperties Whether to apply the default ad OfferDisplayProperties. Some callers of this function want to enforce their own offer styling.
 */
export function performAdOverridesforLockup(objectGraph, data, lockup, metricsOptions, applyAdOfferDisplayProperties = true) {
    // If the lockup is not an ad, it's ad eligible. Just apply the metrics modifications and return early.
    if (!metricsOptions.isAdvert) {
        performMetricsOverridesForLockup(objectGraph, data, lockup, metricsOptions);
        return;
    }
    let searchAd;
    if (objectGraph.props.enabled("advertSlotReporting")) {
        lockup.searchAdOpportunity = searchAdOpportunityFromData(objectGraph, data, metricsOptions.pageInformation);
        searchAd = lockup.searchAdOpportunity.searchAd;
    }
    else {
        lockup.searchAd = searchAdFromData(objectGraph, data, metricsOptions.pageInformation);
        searchAd = lockup.searchAd;
    }
    /**
     * Advert Action Metrics (AzulC+)
     */
    const advertType = content.isArcadeSupported(objectGraph, data)
        ? "arcadeApp"
        : "standardApp";
    const reportingDestination = reportingDestinationFromMetricsOptions(objectGraph, metricsOptions.pageInformation);
    const bundleId = attributeAsString(data, "platformAttributes.ios.bundleId");
    const isPreorder = attributeAsBooleanOrFalse(data, "isPreorder");
    const purchaseType = isPreorder ? "preorder" : "standard";
    const clickActionAdActionMetrics = new AdvertActionMetrics(searchAd.instanceId, data.id, bundleId, advertType, "advertPressed", purchaseType, reportingDestination);
    lockup.clickAction = attachAdActionMetricsToAction(objectGraph, lockup.clickAction, clickActionAdActionMetrics);
    const buttonActionAdActionMetrics = new AdvertActionMetrics(searchAd.instanceId, data.id, bundleId, advertType, "offerButtonPress", purchaseType, reportingDestination);
    lockup.buttonAction = attachAdActionMetricsToAction(objectGraph, lockup.buttonAction, buttonActionAdActionMetrics);
    lockup.itemBackground = objectGraph.props.enabled("insetAdItemBackground") ? "insetAd" : "ad";
    // Offer button style
    if (lockup.offerDisplayProperties && applyAdOfferDisplayProperties) {
        lockup.offerDisplayProperties = lockup.offerDisplayProperties.newOfferDisplayPropertiesChangingAppearance(false, "colored", "ad");
    }
    // Rating
    if (!attributeAsBooleanOrFalse(data, "iad.format.userRating")) {
        lockup.rating = null;
        lockup.ratingCount = null;
    }
    performMetricsOverridesForLockup(objectGraph, data, lockup, metricsOptions);
}
/**
 * Whether or not an advert is eligible to use CPP deeplinks, regardless of whether or not one exists
 * @param data Data that will provide the ad data.
 */
export function isCppDeeplinkEnabledForAdvert(data) {
    const iAd = attributeAsDictionary(data, "iad");
    const searchAdCppDeepLinkEnabled = asBooleanOrFalse(iAd, "passthroughAdInfo.deepLinkEligible");
    const targetedAdCppDeeplinkingEnabled = asBooleanOrFalse(data, "meta.passthroughAdInfo.deepLinkEligible");
    return searchAdCppDeepLinkEnabled || targetedAdCppDeeplinkingEnabled;
}
/**
 * Whether or not a custom creative advert is eligible to use CPP deeplinks, regardless of whether or not one exists
 * @param data Data that will provide the ad data.
 */
export function isCustomCreativeDeeplinkEnabledForAdvert(data) {
    const extraAdInfo = getExtraAdInfo(data);
    if (isSome(extraAdInfo)) {
        return asBooleanOrFalse(extraAdInfo, "passthroughAdInfo.deepLinkEligible");
    }
    else {
        return asBooleanOrFalse(data, "meta.passthroughAdInfo.deepLinkEligible");
    }
}
/**
 * The custom creative ad deeplink url, if there is one.
 * @param data Data that will provide the ad data.
 */
export function getCustomCreativeDeepLinkUrl(data) {
    const extraAdInfo = getExtraAdInfo(data);
    const creativeDetails = asArray(extraAdInfo, "creativeDetails");
    if (isSome(creativeDetails)) {
        return asString(creativeDetails[0], "deepLink");
    }
    else {
        return asString(data, "meta.alignedRegionDetails.deepLink");
    }
}
/**
 * The tapDestination of the custom creative advert which is also the cppId for the PP.
 * @param data Data that will provide the ad data.
 */
export function getTapDestinationIdForAdvert(data) {
    const extraAdInfo = getExtraAdInfo(data);
    const creativeDetails = asArray(extraAdInfo, "creativeDetails");
    if (isSome(creativeDetails)) {
        return asString(creativeDetails[0], "tapDestination");
    }
    else {
        return asString(data, "meta.alignedRegionDetails.tapDestination");
    }
}
/**
 * Local function to extract the extraAdInfo from the meta in the data.
 * @param data Data that will provide the ad data.
 */
function getExtraAdInfo(data) {
    const extraAdInfoString = asString(data, "meta.extraAdInfo");
    if (isSome(extraAdInfoString)) {
        return JSON.parse(extraAdInfoString);
    }
    return null;
}
/**
 * Perform overrides for metrics for Ad Lockups
 * @param data Data for ad lockup
 * @param lockup Lockup to apply overrides to
 * @param baseMetricsOptions Base metrics options to extend.
 */
function performMetricsOverridesForLockup(objectGraph, data, lockup, baseMetricsOptions) {
    const platformDisplayStyle = metricsPlatformDisplayStyleFromData(objectGraph, data, lockup.icon, null);
    const impressionsMetricOptions = impressionOptionsForLockup(objectGraph, data, lockup, platformDisplayStyle, baseMetricsOptions, true);
    addClickEventsToAdLockup(objectGraph, lockup, impressionsMetricOptions);
    addImpressionsFieldsToAd(objectGraph, lockup, impressionsMetricOptions, impressionsMetricOptions.pageInformation.iAdInfo);
}
/**
 * Creates a template type string for the medium ad format
 * @param lockup The lockup whose artwork to use for the template type string
 * @returns The native ad template type for the medium format ad
 */
export function getTemplateTypeForMediumAdFromLockupWithScreenshots(screenshots) {
    var _a;
    const templateString = [
        "MEDRIVER_",
        "U",
        "I",
        ((_a = screenshots === null || screenshots === void 0 ? void 0 : screenshots.artwork) !== null && _a !== void 0 ? _a : []).length, // Count of assets used.
    ].join("");
    return templateString;
}
/**
 * Creates a template type string for the medium ad format
 * @param lockup The lockup whose artwork to use for the template type string
 * @returns The native ad template type for the medium format ad
 */
export function getTemplateTypeForMediumAdFromLockupWithCustomCreative() {
    const templateString = [
        "MEDRIVER_",
        "U",
        "I",
        1,
        "_2x1", // Aspect Ratio used.
    ].join("");
    return templateString;
}
/**
 * Create an opportunity for an ad model
 * @param objectGraph The object graph.
 * @param data Data to build `SearchAd` for.
 * @param pageInformation Metrics page information.
 * @returns The search ad opportunity represented in the data
 */
export function searchAdOpportunityFromData(objectGraph, data, pageInformation) {
    const searchAd = searchAdFromData(objectGraph, data, pageInformation);
    const lifecycleEventPayloads = searchAdLifecycleEventPayloads(searchAd.instanceId, pageInformation);
    return new SearchAdOpportunity(searchAd.instanceId, lifecycleEventPayloads, searchAd);
}
/**
 * Create a missed opportunity for an ad model. This differentiates from `searchAdOpportunityFromData` because the
 * opportunity is represented by an organic content that is in an ad eligible position.
 * @param objectGraph The object graph.
 * @param pageInformation Metrics page information.
 * @returns The search ad opportunity for the missed opportunity
 */
export function searchAdMissedOpportunityFromId(objectGraph, pageInformation) {
    let adInstanceId;
    const placementType = pageInformation === null || pageInformation === void 0 ? void 0 : pageInformation.iAdInfo.placementType;
    if (isDefinedNonNull(placementType) && objectGraph.props.enabled("advertSlotReporting")) {
        try {
            adInstanceId = objectGraph.ads.getIdentifierForMissedOpportunity(placementType);
        }
        catch {
            adInstanceId = objectGraph.random.nextUUID();
            adLogger(objectGraph, `Error: getIdentifierForMissedOpportunity threw exception. Assigned ${adInstanceId}`);
        }
    }
    else {
        adInstanceId = objectGraph.random.nextUUID();
        adLogger(objectGraph, `Error: placementType was null or empty. Assigned ${adInstanceId}`);
    }
    const lifecycleEventPayloads = searchAdLifecycleEventPayloads(adInstanceId, pageInformation);
    return new SearchAdOpportunity(adInstanceId, lifecycleEventPayloads);
}
/**
 * Create the backing search ad model for any representation of ad.
 * @param objectGraph The object graph.
 * @param data Data to build `SearchAd` for.
 * @param pageInformation Information of the page hosting the lockup
 * @returns The search ad model
 */
function searchAdFromData(objectGraph, data, pageInformation) {
    let instanceId = advertInstanceIdForData(objectGraph, data);
    if (isNull(instanceId) || instanceId.length === 0) {
        instanceId = objectGraph.random.nextUUID();
        adLogger(objectGraph, `Error: instanceId was null or empty. Assigned ${instanceId}`);
    }
    const iAd = attributeAsDictionary(data, "iad");
    const impressionId = attributeAsString(data, "iad.impressionId");
    const transparencyData = attributeAsString(data, "iad.privacy");
    const bundleId = attributeAsString(data, "platformAttributes.ios.bundleId");
    const transparencyAction = new AdTransparencyAction(transparencyData);
    transparencyAction.title = objectGraph.adsLoc.string("IAD_PRIVACY_MARKER_BUTTON_TITLE");
    const advertType = content.isArcadeSupported(objectGraph, data)
        ? "arcadeApp"
        : "standardApp";
    const isPreorder = attributeAsBooleanOrFalse(data, "isPreorder");
    const purchaseType = isPreorder ? "preorder" : "standard";
    const reportingDestination = reportingDestinationFromMetricsOptions(objectGraph, pageInformation);
    const adActionMetrics = new AdvertActionMetrics(instanceId, data.id, bundleId, advertType, "markerPress", purchaseType, reportingDestination);
    const lifecycleEventPayloads = searchAdLifecycleEventPayloads(instanceId, pageInformation);
    const action = attachAdActionMetricsToAction(objectGraph, transparencyAction, adActionMetrics);
    return new SearchAd(instanceId, iAd, lifecycleEventPayloads, impressionId, action);
}
/**
 * Create the backing metadata for any representation of ad opportunity.
 * @param instanceId The identifier for the ad opportunity we're tracking
 * @param pageInformation Information of the page hosting the lockup
 * @returns Payloads to be sent to PromotedContent
 */
export function searchAdLifecycleEventPayloads(instanceId, pageInformation) {
    const pageIdentifierRaw = pageInformation === null || pageInformation === void 0 ? void 0 : pageInformation.baseFields.pageId;
    const pageIdentifier = typeof pageIdentifierRaw === "string" ? pageIdentifierRaw : "unknown";
    return {
        placed: {
            adInstanceId: instanceId,
            pageIdentifier: pageIdentifier,
        },
        pageEnter: {
            pageIdentifier: pageIdentifier,
        },
        pageExit: {
            pageIdentifier: pageIdentifier,
        },
        onScreen: {
            adInstanceId: instanceId,
        },
        offScreen: {
            adInstanceId: instanceId,
        },
        visible: {
            adInstanceId: instanceId,
        },
        completed: {
            adInstanceId: instanceId,
        },
    };
}
/**
 * Populates Ad-specific customizations for given card to an otherwise fully configured Today card
 * @param objectGraph The object graph.
 * @param data Data that will provide the ad data.
 * @param card The Today card to modify.
 * @param metricsOptions The card metrics options.
 */
export function performAdOverridesForCard(objectGraph, data, card, metricsOptions) {
    let instanceId = advertInstanceIdForData(objectGraph, data);
    if (isNull(instanceId) || instanceId.length === 0) {
        instanceId = objectGraph.random.nextUUID();
        adLogger(objectGraph, `Error: instanceId was null or empty. Assigned ${instanceId}`);
    }
    const advertType = content.isArcadeSupported(objectGraph, data)
        ? "arcadeApp"
        : "standardApp";
    const reportingDestination = reportingDestinationFromMetricsOptions(objectGraph, metricsOptions.pageInformation);
    const bundleId = attributeAsString(data, "platformAttributes.ios.bundleId");
    const isPreorder = attributeAsBooleanOrFalse(data, "isPreorder");
    const purchaseType = isPreorder ? "preorder" : "standard";
    const adActionMetrics = new AdvertActionMetrics(instanceId, data.id, bundleId, advertType, "advertPressed", purchaseType, reportingDestination);
    card.clickAction = attachAdActionMetricsToAction(objectGraph, card.clickAction, adActionMetrics);
}
/**
 * Attaches `AdActionMetrics` to an existing action enabling native ad interaction instrumentation.
 * This works by wrapping the existing action, along with a new `AdInteractionAction`, in a `CompoundAction`, which are executed at the same time.
 *
 * @param objectGraph The Object Graph.
 * @param existingAction The existing Action to wrap.
 * @param adActionMetrics The adActionMetrics object for the `AdInteractionAction`.
 * @returns The new Action containing the existing and new Actions.
 */
export function attachAdActionMetricsToAction(objectGraph, existingAction, adActionMetrics) {
    const adInteractionAction = new AdInteractionAction(adActionMetrics);
    const action = new CompoundAction([adInteractionAction, existingAction]);
    // Re-use the title from the existing action.
    action.title = existingAction.title;
    return action;
}
// endregion
// region Asset Overrides
/**
 * Performs iAd Asset overrides, where a field dictates which assets to use
 * @param data The data to source overrides from
 * @param lockup The lockup to modify
 * @param metricsOptions The metrics options that this operation has side effects on.
 */
export function performAssetOverridesForMixedMediaAdLockupIfNeeded(objectGraph, data, lockup, metricsOptions) {
    if (!shouldPerformAssetOverridesForData(objectGraph, data)) {
        return;
    }
    const iAdAssetOverrides = attributeAsArrayOrEmpty(data, "iad.assetOverride");
    if (iAdAssetOverrides.length) {
        const didApplyAssetOverrides = applyAssetOverridesToMixedMediaAdLockup(objectGraph, lockup, iAdAssetOverrides);
        if (metricsOptions.pageInformation.iAdInfo && metricsOptions.pageInformation.iAdInfo.iAdIsPresent) {
            if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
                metricsOptions.pageInformation.iAdInfo.setSpecifiedAlignedRegionUsed(didApplyAssetOverrides);
            }
        }
    }
}
/**
 * Whether or not an asset override should occur.
 * This is used to control specific scenarios when asset overrides should not occur, e.g. when it clashes with other features.
 *
 * @param data The apps resource to determine if asset overrides should be done for.
 */
function shouldPerformAssetOverridesForData(objectGraph, data) {
    /**
     * If an AB Test is being run on given app, don't accept the override.
     */
    const appHasABTest = appDataHasVariant(objectGraph, data, "abExperiment");
    return !appHasABTest;
}
/**
 * Apply asset overrides to the ad lock up, based on a given list of overrides
 * @param lockup Lockup to modify
 * @param iAdAssetOverrides Asset overrides specified
 * @return {boolean} - Return whether or not the overrides were applied
 */
function applyAssetOverridesToMixedMediaAdLockup(objectGraph, lockup, iAdAssetOverrides) {
    const unusedAssetOverrides = new Set(iAdAssetOverrides);
    const videoOverrides = [];
    const screenshotOverrides = [];
    const isOverrideMedia = function (url, checksum) {
        if (unusedAssetOverrides.size === 0) {
            return false;
        }
        for (const assetOverride of iAdAssetOverrides) {
            if (assetOverride === checksum || url.indexOf(assetOverride) !== -1) {
                unusedAssetOverrides.delete(assetOverride);
                return true;
            }
        }
        return false;
    };
    if (iAdAssetOverrides.length && (lockup.screenshots.length || lockup.trailers.length)) {
        if (lockup.trailers.length) {
            for (const video of lockup.trailers[0].videos) {
                if (isOverrideMedia(video.videoUrl, video.preview.checksum)) {
                    videoOverrides.push(video);
                }
            }
        }
        if (lockup.screenshots.length) {
            for (const screenshot of lockup.screenshots[0].artwork) {
                if (isOverrideMedia(screenshot.template, screenshot.checksum)) {
                    screenshotOverrides.push(screenshot);
                }
            }
        }
    }
    if (unusedAssetOverrides.size === 0 && (videoOverrides.length || screenshotOverrides.length)) {
        if (lockup.trailers.length) {
            lockup.trailers[0].videos = videoOverrides;
        }
        if (lockup.screenshots.length) {
            lockup.screenshots[0] = new Screenshots(screenshotOverrides, lockup.screenshots[0].mediaPlatform);
        }
        return true;
    }
    else {
        return false;
    }
}
// endregion
// region Reporting Destionation
/**
 * Determine the reporting destination based on metrics options
 */
export function reportingDestinationFromMetricsOptions(objectGraph, pageInformation) {
    const iadInfo = pageInformation === null || pageInformation === void 0 ? void 0 : pageInformation.iAdInfo;
    if (isNull(iadInfo)) {
        return "undefined";
    }
    const placementType = iadInfo.placementType;
    switch (placementType) {
        case "searchLanding":
            return "promotedContent"; // SLP uses PC-backed Journey reporting
        case "searchResults":
            return "searchAds"; // SRP uses SA-backed Journey reporting
        case "today":
        case "productPageYMAL":
        case "productPageYMALDuringDownload":
            return "promotedContent"; // Chainlink uses PC-backed Journey reporting
        default:
            throw new Error(`This method should never be called with value: ${placementType}`);
    }
}
// endregion
//# sourceMappingURL=ad-lockups.js.map