import { isNothing, isSome } from "@jet/environment";
import * as validation from "@jet/environment/json/validation";
import { ArtworkContentMode, ExternalUrlAction, FlowAction, TodayCardActionOverlay, TodayCardMediaArtwork, TodayCardMediaHero, TodayCardMediaMultiApp, TodayCardMediaVideo, TodayCardMediaWithArtwork, } from "../../api/models";
import { asBoolean, asDictionary, asString, isDefinedNonNull, isDefinedNonNullNonEmpty, isNull, isNullOrEmpty, objectPathToString, } from "../../foundation/json-parsing/server-data";
import * as mediaAttributes from "../../foundation/media/attributes";
import { relationshipCollection } from "../../foundation/media/relationships";
import { Parameters } from "../../foundation/network/url-constants";
import { URL } from "../../foundation/network/urls";
import * as color from "../../foundation/util/color-util";
import { artworkFromApiArtwork, editorialNotesFromData, hasMessagesExtensionFromData, isHiddenFromSpringboardFromData, joeColorSetFromData, notesFromData, screenSizeIPhone134, } from "../content/content";
import { extractEditorialClientParams } from "../editorial-pages/editorial-data-util";
import * as lockupsEditorialContext from "../lockups/editorial-context";
import { lockupsFromData } from "../lockups/lockups";
import * as metricsHelpersLocation from "../metrics/helpers/location";
import * as metricsHelpersImpressions from "./../metrics/helpers/impressions";
import { createTodayAppEventCard } from "./cards/today-app-event-card-builder";
import { createTodayBrandedCard } from "./cards/today-branded-card-builder";
import { createTodayFullBleedImageCard } from "./cards/today-full-bleed-image-card-builder";
import { createTodayGridCard } from "./cards/today-grid-card-builder";
import { createTodayInAppPurchaseCard } from "./cards/today-in-app-purchase-card-builder";
import { createTodayListCard } from "./cards/today-list-card-builder";
import { createTodayRiverCard } from "./cards/today-river-card-builder";
import { createTodayShortImageCard } from "./cards/today-short-image-card-builder";
import { createTodaySingleAppCard } from "./cards/today-single-app-card-builder";
import { createTodayVideoCard } from "./cards/today-video-card-builder";
import { HeroMediaDisplayContext, OfTheDayIntention, TodayCardDisplayStyle, } from "./today-types";
import { createTodayBaseCard } from "./cards/today-base-card-builder";
// MARK: - Today Card Creation
/**
 * Create a card with some appearance configuration within today page.
 * @param {AppStoreObjectGraph} objectGraph The object graph for the app store.
 * @param {Data} data to build card of.
 * @param {TodayCardConfiguration} cardConfig flags on how to configure a given card.
 * @param {TodayParseContext} context to store state updated throughout parsing.
 * @param {TodayCardAugmentingData} augmentingData that stores some additional responses that may be used to enhance the contents of `data`
 */
export function todayCardFromData(objectGraph, data, cardConfig, context, augmentingData) {
    return validation.catchingContext("todayCardFromData", () => {
        const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
        const clientIdentifier = lockupsEditorialContext.clientIdentifierForEditorialContextInData(objectGraph, data);
        // If clientIdentifier is empty, don't override the cardConfig as this may have been set
        // at a higher level.
        if (isSome(clientIdentifier)) {
            if (shouldRespectClientIdentifierOverride(objectGraph, data, clientIdentifier, cardConfig)) {
                cardConfig.clientIdentifierOverride = clientIdentifier;
            }
            else {
                cardConfig.clientIdentifierOverride = null;
            }
        }
        // Configure subtitle for cross link.
        cardConfig.crossLinkSubtitle = crossLinkSubtitleFromData(objectGraph, data);
        let card = null;
        switch (cardDisplayStyle) {
            case TodayCardDisplayStyle.AppOfTheDay:
            case TodayCardDisplayStyle.GameOfTheDay:
                card = createTodayBrandedCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            case TodayCardDisplayStyle.Video:
                card = createTodayVideoCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            case TodayCardDisplayStyle.FullBleedImage:
                card = createTodayFullBleedImageCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            case TodayCardDisplayStyle.InAppPurchase:
                card = createTodayInAppPurchaseCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            case TodayCardDisplayStyle.AppEventCard:
                card = createTodayAppEventCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            case TodayCardDisplayStyle.List:
            case TodayCardDisplayStyle.NumberedList:
                card = createTodayListCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            case TodayCardDisplayStyle.River:
            case TodayCardDisplayStyle.Grid:
                if (objectGraph.client.isMac) {
                    card = createTodayGridCard(objectGraph, data, cardConfig, context, augmentingData);
                }
                else {
                    card = createTodayRiverCard(objectGraph, data, cardConfig, context, augmentingData);
                }
                break;
            case TodayCardDisplayStyle.SingleApp:
                card = createTodaySingleAppCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            case TodayCardDisplayStyle.ShortImage:
                card = createTodayShortImageCard(objectGraph, data, cardConfig, context, augmentingData);
                break;
            default:
                card = null;
                break;
        }
        if (isNull(card)) {
            objectGraph.console.log(`Unknown style: ${cardDisplayStyle}`);
            return card;
        }
        // For certain platforms we add hero media to the card.
        addHeroMediaToTodayCardIfNecessary(objectGraph, card, cardConfig, data);
        addExternalLinkOverlayToTodayCardIfNecessary(card);
        addOTDStyleToCardIfNecessary(card, cardConfig);
        enableFlipAndBlurIfNecessary(objectGraph, card);
        if (isNothing(card.media)) {
            objectGraph.console.log(`Missing required media: ${cardDisplayStyle}`);
            card = null;
        }
        return card;
    }, (error) => {
        objectGraph.console.log(error);
        return null;
    });
}
/**
 * On the watch we need the todayCard on the article to get the title and subtitle,
 * so even if we dont successfully make a card for the article we create a basic one with an empty media so the title / subtitle can be used
 * @param {AppStoreObjectGraph} objectGraph The object graph for the app store.
 * @param {Data} data to build card of.
 * @param {TodayCardConfiguration} cardConfig flags on how to configure a given card.
 * @param {TodayParseContext} context to store state updated throughout parsing.
 */
export function fallbackWatchTodayCardFromData(objectGraph, data, cardConfig, context) {
    if (!objectGraph.client.isWatch) {
        return null;
    }
    const fallbackCard = createTodayBaseCard(objectGraph, data, cardConfig, context);
    fallbackCard.media = new TodayCardMediaArtwork([], [], []);
    return fallbackCard;
}
/**
 * Create the {EditorialDisplayOptions for a card given the MAPI data
 * @param objectGraph The dependency graph for the App Store
 * @param data The MAPI data for this today card
 * @param cardConfig The configuration for the card
 * @returns The editorialDisplayOptions for this card
 */
export function todayCardEditorialDisplayOptionsFromData(objectGraph, data, cardConfig) {
    var _a;
    // here is where we make the display options
    const editorialClientParams = extractEditorialClientParams(objectGraph, data);
    const isExtraWideCard = objectGraph.client.isPad && (cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.isHeroCard);
    const editorialDisplayOptions = {
        suppressTagline: mediaAttributes.attributeAsBoolean(data, "ignoreITunesShortNotes"),
        suppressLockup: asBoolean(editorialClientParams["suppressLockup"]),
        showBadgeInSmallCards: (_a = cardConfig.alwaysShowBadgeInSmallCards) !== null && _a !== void 0 ? _a : asBoolean(editorialClientParams["showBadgeInSmallCards"]),
        useMaterialBlur: cardConfig.alwaysUseMaterialBlur || isExtraWideCard || asBoolean(editorialClientParams["useMaterialBlur"]),
    };
    return editorialDisplayOptions;
}
// MARK: - Today Configuration
/**
 * Create the "default" card configuration that callers can generally base their configuration from.
 */
export function defaultTodayCardConfiguration(objectGraph) {
    return {
        useOTDTextStyle: false,
        enableFullScreenVideo: true,
        enableListCardToMultiAppFallback: true,
        canDisplayArcadeOfferButton: true,
        isHeroCard: false,
    };
}
// MARK: - Metrics / Location Tracking
/**
 * Allows for specialized card builders to share the same location tracking push / pop code.
 *
 * @param objectGraph The dependency graph for the App Store
 * @param data The media api data to get location information from
 * @param cardConfig The configuration for the card
 * @param context The parse context for the over all today page
 */
export function pushTodayCardLocation(objectGraph, data, cardConfig, context, titleOverride) {
    const title = isDefinedNonNullNonEmpty(titleOverride)
        ? titleOverride
        : todayCardTitleFromData(objectGraph, data, cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle));
    metricsHelpersLocation.pushContentLocation(objectGraph, todayCardMetricsOptions(objectGraph, data, cardConfig, context, title), title !== null && title !== void 0 ? title : "");
}
export function todayCardMetricsOptions(objectGraph, data, cardConfig, context, titleOverride) {
    var _a;
    return metricsHelpersImpressions.impressionOptions(objectGraph, data, titleOverride !== null && titleOverride !== void 0 ? titleOverride : "", {
        ...cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.baseMetricsOptions,
        targetType: "todayCard",
        pageInformation: context.pageInformation,
        locationTracker: context.locationTracker,
        isAdEligible: (_a = cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.isAdEligible) !== null && _a !== void 0 ? _a : false,
        optimizationId: asString(data, "meta.personalizationData.optimizationId"),
        optimizationEntityId: asString(data, "meta.personalizationData.optimizationEntityId"),
        rowIndex: cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.currentRowIndex,
        displayStyle: cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.metricsDisplayStyle,
    });
}
/**
 * Pop the today card location that was pushed by the pushTodayCardLocation function.
 *
 * @param context The parse context for the over all today page
 */
export function popTodayCardLocation(context) {
    metricsHelpersLocation.popLocation(context.locationTracker);
}
// MARK: - Card Title
/**
 * @param objectGraph The dependency graph for the App Store
 * @param data The media api data used to determine the title of the card
 * @param cardDisplayStyle The display style of the card
 * @returns The title of the card
 */
export function todayCardTitleFromData(objectGraph, data, cardDisplayStyle) {
    let title = notesFromData(objectGraph, data, "name");
    if (isNullOrEmpty(title)) {
        switch (cardDisplayStyle) {
            case TodayCardDisplayStyle.AppOfTheDay:
            case TodayCardDisplayStyle.GameOfTheDay:
                title = mediaAttributes.attributeAsString(data, "label");
                break;
            default:
                break;
        }
    }
    return title;
}
// MARK: - Card Display Styles
/**
 * These are today card types that can be coerced into an override type, there are times where we want to
 * have all List cards for instance, render as grids, this set denotes the types that can be coerced.
 */
const coercibleTodayCardStyles = new Set([
    TodayCardDisplayStyle.Grid,
    TodayCardDisplayStyle.List,
    TodayCardDisplayStyle.NumberedList,
    TodayCardDisplayStyle.River,
]);
/**
 * Find the today card style for the given data, allowing coercion if necessary.
 * @param data The media api data for an editorial item
 * @param styleOverride The override style to use if the data has a style that can be coerced
 * @returns The resolved todayCardDisplayStyle
 */
export function cardDisplayStyleFromData(data, styleOverride) {
    const cardDisplayStyle = mediaAttributes.attributeAsString(data, "cardDisplayStyle");
    if (coercibleTodayCardStyles.has(cardDisplayStyle) && isSome(styleOverride)) {
        return styleOverride;
    }
    return cardDisplayStyle;
}
// MARK: - Collection Cards
/**
 * @param objectGraph Dependency graph for the App Store
 * @param data MAPI data for the card these lockups are included in
 * @param cardConfig The configuration for the card these lockups are included in
 * @param context The parse context for the over all today page
 * @param includeLockupClickActions Whether we should include the click actions for the lockups, this is only needed
 * when a card allows clicks on a lockup
 * @returns The list of lockups for the collection displayed on a TodayCard
 */
export function lockupsForCollectionCardFromData(objectGraph, data, cardConfig, context, includeLockupClickActions) {
    const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
    const filteredRelatedContent = relatedContent.filter((cardData) => {
        const isHiddenFromSpringboard = isHiddenFromSpringboardFromData(objectGraph, cardData);
        const hasMessagesExtension = hasMessagesExtensionFromData(objectGraph, cardData);
        return !hasMessagesExtension || !isHiddenFromSpringboard;
    });
    return lockupsForRelatedContent(objectGraph, filteredRelatedContent, cardConfig, context.pageInformation, context.locationTracker, undefined, undefined, undefined, includeLockupClickActions);
}
/**
 * @param objectGraph Dependency graph for the App Store
 * @param relatedContent The list of MAPI data objects to generate lockups for
 * @param cardConfig The configuration for the card these lockups are included in
 * @param pageInformation The pageInformation for the page these lockups are included in
 * @param locationTracker The locationTracker used for impressions on these lockups
 * @param offerEnvironment The offer environment to use for the lockups
 * @param offerStyle The offerStyle to use for the lockups
 * @param externalDeepLinkUrl The promotional deep link url to use on the lockup's offer.
 * @param includeLockupClickActions Whether we should include the click actions for the lockups, this is only needed
 * when a card allows clicks on a lockup
 * @returns The list of lockups for the collection displayed on a TodayCard
 */
export function lockupsForRelatedContent(objectGraph, relatedContent, cardConfig, pageInformation, locationTracker, offerEnvironment, offerStyle, externalDeepLinkUrl, includeLockupClickActions = true) {
    if (isNullOrEmpty(relatedContent)) {
        return [];
    }
    // IAPs are only suitable for the TodayCardInAppPurchase card. That card follows a separate path, so we should
    // filter them out here.
    const filteredRelatedContent = relatedContent.filter((cardData) => {
        if (isDefinedNonNullNonEmpty(cardData.attributes)) {
            return cardData.type !== "in-apps";
        }
        return true;
    });
    const options = {
        lockupOptions: {
            metricsOptions: {
                pageInformation: pageInformation,
                locationTracker: locationTracker,
            },
            offerEnvironment: offerEnvironment,
            offerStyle: offerStyle,
            clientIdentifierOverride: cardConfig.clientIdentifierOverride,
            externalDeepLinkUrl: externalDeepLinkUrl,
            crossLinkSubtitle: cardConfig.crossLinkSubtitle,
            artworkUseCase: 1 /* ArtworkUseCase.LockupIconSmall */,
            canDisplayArcadeOfferButton: cardConfig.canDisplayArcadeOfferButton,
            useJoeColorIconPlaceholder: cardConfig.useJoeColorIconPlaceholder,
            skipDefaultClickAction: !includeLockupClickActions,
        },
        filter: 76670 /* Filter.TodayCard */,
    };
    return lockupsFromData(objectGraph, filteredRelatedContent, options);
}
// MARK: - Related Content
/**
 * @param objectGraph Dependency graph for the App Store
 * @param data MAPI data to get the relationship from
 * @returns The card-contents relationship from the MAPI data
 */
export function relatedCardContentsContentsFromData(objectGraph, data) {
    return relationshipCollection(data, "card-contents");
}
// MARK: - Grid / List Collection Card Fallback
/**
 * @param objectGraph The object graph for the app store
 * @returns The min icon count that a Grid card should use before falling back to fallback media
 */
export function gridFallbackLimit(objectGraph) {
    switch (objectGraph.client.deviceType) {
        case "tv":
            return 2;
        default:
            return 4;
    }
}
/**
 * @param objectGraph The object graph for the app store
 * @returns The min icon count that a List card should use before falling back to fallback media
 */
export function listFallbackLimit(objectGraph) {
    // Limits before using fallback media card for list-types and grid-types (including river).
    switch (objectGraph.client.deviceType) {
        case "tv":
            return 3;
        default:
            return 4;
    }
}
/**
 * When we do not have enough content to render a list or grid card, we will use a multi
 * app fallback media in its place. This also updates the click action to include a param
 * indicating we're using fallback media.
 *
 * @param objectGraph The object graph for the app store
 * @param data The MAPI data for the card
 * @param fallbackItems The list of lockups to use as fallback media
 * @param card The base card to apply the fallback media to
 */
export function applyMultiAppFallbackToCollectionCard(objectGraph, data, fallbackItems, card) {
    const extraText = notesFromData(objectGraph, data, "short");
    card.media = new TodayCardMediaMultiApp(fallbackItems, extraText);
    card.style = "dark";
    if (card.clickAction instanceof FlowAction) {
        const updatedPageUrl = URL.from(card.clickAction.pageUrl);
        updatedPageUrl.param(Parameters.showingFallbackMedia, "true");
        card.clickAction.pageUrl = updatedPageUrl.build();
    }
}
// MARK: - Editorial Artwork
/**
 * @param objectGraph The object graph for the app store
 * @param cardDisplayStyle The display style for the card we're getting the key path for
 * @returns The keypath used to find the editorial artwork for the card
 */
export function editorialArtKeyPathForCardDisplayStyle(objectGraph, cardDisplayStyle) {
    if (objectGraph.client.isWatch) {
        return "editorialArtwork.subscriptionHero";
    }
    else if (objectGraph.client.isVision) {
        return "editorialArtwork.storyCenteredStatic16x9";
    }
    else {
        switch (cardDisplayStyle) {
            case TodayCardDisplayStyle.AppOfTheDay:
            case TodayCardDisplayStyle.GameOfTheDay:
                return "editorialArtwork.dayCard";
            case TodayCardDisplayStyle.AppEventCard:
                return "editorialArtwork.eventCard";
            case TodayCardDisplayStyle.Video:
            case TodayCardDisplayStyle.FullBleedImage:
                return "editorialArtwork.mediaCard";
            default:
                return "editorialArtwork.generalCard";
        }
    }
}
/**
 * @param objectGraph The object graph for the app store
 * @param artworkData The artwork data to create the artwork from, from the cards media api data
 * @param cropCode The crop code to use for the artwork, otherwise we use the correct crop for the platform
 * @returns The artwork model for the card
 */
export function todayCardArtworkFromArtworkData(objectGraph, artworkData, cropCode) {
    if (isNullOrEmpty(artworkData)) {
        return null;
    }
    const artwork = artworkFromApiArtwork(objectGraph, artworkData, {
        withJoeColorPlaceholder: true,
        useCase: 15 /* ArtworkUseCase.TodayCardMedia */,
    });
    if (cropCode) {
        artwork.crop = cropCode;
    }
    else if (objectGraph.client.isMac || objectGraph.client.isTV) {
        // <rdar://problem/39155084> Articles: Use new crossover crop code for macOS story card art
        artwork.crop = "fn";
    }
    else {
        artwork.crop = "sr";
    }
    return artwork;
}
/**
 * Returns a branded title `Artwork` model object from the API `editorialArtwork.contentLogoTrimmed` response.
 * This artwork can be used in place of a text title for a Today Card.
 * @param objectGraph The dependency graph for the App Store
 * @param data The media API data to fetch the Artwork from
 * @returns The branded title `Artwork` object, or `undefined` if a branded title could not be found
 */
export function brandedTitleArtworkForCard(objectGraph, data) {
    const brandedTitleData = mediaAttributes.attributeAsDictionary(data, "editorialArtwork.contentLogoTrimmed");
    return artworkFromApiArtwork(objectGraph, brandedTitleData, {
        contentMode: ArtworkContentMode.scaleAspectFit,
        allowingTransparency: true,
        useCase: 17 /* ArtworkUseCase.TodayCardBrandedTitle */,
    });
}
/**
 * @param objectGraph The object graph for the app store
 * @param data The media api data for the card
 * @param cardConfig The configuration for the card being built
 * @returns The `bgGradientKind` field from the correct artwork dictionary
 */
export function todayCardArtworkTitleBackingGradientForKey(objectGraph, data, cardConfig) {
    const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
    const artworkKey = editorialArtKeyPathForCardDisplayStyle(objectGraph, cardDisplayStyle);
    const artworkData = mediaAttributes.attributeAsDictionary(data, artworkKey);
    if (!isDefinedNonNull(artworkData)) {
        return null;
    }
    return asString(artworkData, "bgGradientKind");
}
/**
 * @param objectGraph The object graph for the app store
 * @param artworkData The artwork data from the media api data for a card, that allows us to determine the style
 * @returns The light or dark style for this card, if we can determine it.
 */
export function todayCardStyleFromArtwork(objectGraph, artworkData) {
    var _a;
    if (isNullOrEmpty(artworkData)) {
        return undefined;
    }
    const joeColors = joeColorSetFromData(artworkData);
    const backgroundColor = joeColors.bgColor;
    const hasGradient = ((_a = joeColors.textGradient) === null || _a === void 0 ? void 0 : _a.length) === 2;
    if (!backgroundColor && !hasGradient) {
        return undefined;
    }
    if (objectGraph.client.isiOS || objectGraph.client.isWeb) {
        return cardStyleFromJoeColors(joeColors, "bgColor");
    }
    else if (hasGradient) {
        // Gradient colors are the text color, so if the gradient is a dark color, then
        // the environment is a light environment
        return color.isDarkColor(joeColors.textGradient[0]) ? "light" : "dark";
    }
    else {
        return color.isDarkColor(backgroundColor) ? "dark" : "light";
    }
}
export function cardStyleFromJoeColorsWithoutFallback(joeColors, preferredColorKeypath = "bgColor") {
    if (isNothing(joeColors)) {
        return undefined;
    }
    if (isSome(joeColors === null || joeColors === void 0 ? void 0 : joeColors.textGradient) && joeColors.textGradient.length === 2) {
        // Rare, but if gradient colors are present, defer to them
        return color.isDarkColor(joeColors.textGradient[0]) ? "white" : "dark";
    }
    const preferredColor = joeColors[preferredColorKeypath];
    if (isNothing(preferredColor)) {
        return undefined;
    }
    const luminance = color.luminanceFrom(preferredColor);
    if (luminance <= 0.1) {
        return "dark";
    }
    else if (luminance >= 0.84) {
        return "white";
    }
    else {
        return "light";
    }
}
export function cardStyleFromJoeColors(joeColors, preferredColorKeypath = "bgColor") {
    var _a;
    return (_a = cardStyleFromJoeColorsWithoutFallback(joeColors, preferredColorKeypath)) !== null && _a !== void 0 ? _a : "light";
}
// MARK: - Editorial Text
/**
 * Determines the heading for the card, were it to appear inline.
 * If the source card heading is null, we default to the existing
 * inline heading, if any.
 * @param objectGraph The object graph for the app store
 * @param card The today card we're collapsing the heading for
 * @returns string The title to use for the card.
 */
export function collapsedHeadingForTodayCard(objectGraph, card) {
    if (isDefinedNonNull(card.heading)) {
        return card.heading.replace(/\n/g, " ");
    }
    return card.collapsedHeading;
}
// MARK: - Offers
/**
 * Determines an appropriate OfferStyle for a TodayCard to ensure correct
 * contrast with the card's contents.
 *
 * @param cardStyle The today style for the card
 * @returns an appropriate offer style to use for the button
 */
export function offerStyleForTodayCard(objectGraph, cardStyle) {
    return "transparent";
}
/**
 * Determines an appropriate OfferEnvironment for a TodayCard to ensure correct
 * contrast with the card's contents.
 *
 * @param cardStyle The today style for the card
 * @returns an appropriate offer environment to use for the button
 */
export function offerEnvironmentForTodayCard(cardStyle) {
    if (cardStyle === "white") {
        return "light";
    }
    else {
        return "todayCard";
    }
}
// MARK: - Hero Media
/**
 * For mac and tv platforms, we add hero media to the today cards.
 *
 * @param objectGraph The dependency graph for the app store
 * @param card The today card we're adding hero media to
 * @param cardConfig The config used to create this card
 * @param data The media api data used to create this card
 */
export function addHeroMediaToTodayCardIfNecessary(objectGraph, card, cardConfig, data) {
    const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
    // MacOS and tvOS: Hero
    const heroArt = todayCardHeroArtForData(objectGraph, data, cardConfig.heroDisplayContext, cardDisplayStyle, cardConfig.prevailingCropCodes);
    let artworks = [];
    let videos = [];
    if (isDefinedNonNull(heroArt)) {
        artworks = [heroArt];
    }
    if (isDefinedNonNull(card.media) &&
        (card.media instanceof TodayCardMediaVideo || card.media instanceof TodayCardMediaArtwork)) {
        videos = card.media.videos;
    }
    const hero = new TodayCardMediaHero(artworks, videos);
    const supportsHeroMedia = objectGraph.client.isMac ||
        objectGraph.client.isTV ||
        objectGraph.client.isWeb ||
        (objectGraph.client.isVision && !cardConfig.isSearchContext) ||
        preprocessor.GAMES_TARGET;
    if (supportsHeroMedia && hero.isValid()) {
        card.heroMedia = hero;
        // Ensure we update the card styling. We prefer hero art, and fallback to hero video.
        const heroArtworkData = mediaAttributes.attributeAsDictionary(data, heroMediaArtworkKeyForContext(objectGraph, cardConfig.heroDisplayContext, cardDisplayStyle, Object.keys(mediaAttributes.attributeAsDictionary(data, "editorialArtwork"))));
        let style = todayCardStyleFromArtwork(objectGraph, heroArtworkData);
        if (!isDefinedNonNull(style)) {
            const heroVideoPreviewData = asDictionary(todayCardHeroVideoFromData(objectGraph, data), "previewFrame");
            style = todayCardStyleFromArtwork(objectGraph, heroVideoPreviewData);
        }
        card.style = style;
    }
}
/**
 * For external links we will display the standard link overlay in place of whatever overlay we had previously
 *
 * @param card The today card we're adding hero media to
 */
export function addExternalLinkOverlayToTodayCardIfNecessary(card) {
    if (card.clickAction instanceof ExternalUrlAction) {
        card.overlay = new TodayCardActionOverlay(card.clickAction);
        card.style = "white";
    }
}
/**
 * @param objectGraph The dependency graph for the app store
 * @param data The media api data to search for hero art in
 * @param context The context in which this hero media will be displayed
 * @param displayStyle The display style of the card
 * @param prevailingCropCodes The prevailing crop code to use for inline hero media
 * @returns The artwork to be used in the hero position for a today card
 */
export function todayCardHeroArtForData(objectGraph, data, context, displayStyle, prevailingCropCodes) {
    const heroArtworkKey = heroMediaArtworkKeyForContext(objectGraph, context, displayStyle, Object.keys(mediaAttributes.attributeAsDictionary(data, "editorialArtwork")));
    const heroArtworkData = mediaAttributes.attributeAsDictionary(data, heroArtworkKey);
    return todayCardArtworkFromArtworkData(objectGraph, heroArtworkData, heroMediaArtworkCropForContext(objectGraph, context, objectPathToString(heroArtworkKey), prevailingCropCodes));
}
/**
 * @param objectGraph The dependency graph for the app store
 * @param context The context in which this hero media will be displayed
 * @param displayStyle The display style of the card
 * @returns The key to use to find the hero media artwork
 */
function heroMediaArtworkKeyForContext(objectGraph, context, displayStyle, availableArtworkKeys) {
    if (context === "article" && (objectGraph.client.isMac || objectGraph.client.isiOS)) {
        // Use "iOS" art
        return editorialArtKeyPathForCardDisplayStyle(objectGraph, displayStyle);
    }
    else if (objectGraph.client.isVision) {
        return "editorialArtwork.heroStatic16x9";
    }
    else {
        if (objectGraph.client.isTV && availableArtworkKeys.includes("categoryDetailStatic16x9")) {
            return "editorialArtwork.categoryDetailStatic16x9";
        }
        return "editorialArtwork.crossoverCard";
    }
}
/**
 * @param objectGraph The dependency graph for the app store
 * @param context The context in which this hero media will be displayed
 * @param prevailingCropCodes The crop code to use if we're not overriding it
 * @returns The crop code to use for hero artwork in a given context
 */
function heroMediaArtworkCropForContext(objectGraph, context, heroArtworkKey, prevailingCropCodes) {
    if (context === HeroMediaDisplayContext.Article && objectGraph.client.isMac) {
        // Use "iOS" art crop code
        return "fn";
    }
    else if (context === HeroMediaDisplayContext.Article &&
        objectGraph.client.isTV &&
        heroArtworkKey === "editorialArtwork.categoryDetailStatic16x9") {
        return "sr";
    }
    else {
        return prevailingCropCodes === null || prevailingCropCodes === void 0 ? void 0 : prevailingCropCodes.defaultCrop;
    }
}
/**
 * @param objectGraph The dependency graph for the app store
 * @param data The media api data to search for hero video in
 * @returns The video to be used in the hero position for a today card
 */
function todayCardHeroVideoFromData(objectGraph, data) {
    let videoData;
    const videoData4x3 = mediaAttributes.attributeAsDictionary(data, "editorialVideo.storeFrontVideo4x3");
    const videoData16x9 = mediaAttributes.attributeAsDictionary(data, "editorialVideo.storeFrontVideo");
    // <rdar://problem/34339194> Today: Video Card: Incorrect key for 4x3 videos
    // On pad prefer 4x3 fallback to 16x9 and on phone vice versa
    if (objectGraph.client.isPad || objectGraph.client.screenSize.isEqualTo(screenSizeIPhone134)) {
        videoData = videoData4x3 || videoData16x9;
    }
    else {
        videoData = videoData16x9 || videoData4x3;
    }
    return videoData;
}
// MARK: - Crosslinks
/**
 *  Determines the cross link subtitle from card data.
 */
export function crossLinkSubtitleFromData(objectGraph, data) {
    // Start with short notes
    let subtitle = editorialNotesFromData(objectGraph, data, "short");
    // Fallback to name
    if (!isDefinedNonNullNonEmpty(subtitle)) {
        subtitle = notesFromData(objectGraph, data, "name");
    }
    // Fallback to label
    if (!isDefinedNonNullNonEmpty(subtitle)) {
        const cardDisplayStyle = mediaAttributes.attributeAsString(data, "displayStyle");
        if (cardDisplayStyle === TodayCardDisplayStyle.AppOfTheDay ||
            cardDisplayStyle === TodayCardDisplayStyle.GameOfTheDay) {
            subtitle = mediaAttributes.attributeAsString(data, "label");
        }
    }
    return subtitle;
}
// MARK: - Client Identifier Overrids
function shouldRespectClientIdentifierOverride(objectGraph, data, clientIdentifier, cardConfig) {
    var _a;
    if (clientIdentifier === "com.apple.visionproapp" /* ClientIdentifier.VisionCompanion */ || clientIdentifier === "VisionAppStore" /* ClientIdentifier.VisionAppStore */) {
        // If using the companion app, it's expected that we always respect the given identifier.
        return true;
    }
    const relatedContent = relatedCardContentsContentsFromData(objectGraph, data);
    const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig.coercedCollectionTodayCardDisplayStyle);
    // Do not apply the override if this is not a Content type card.
    const cardTypesRequiringClientIdentifierOverride = new Set([
        TodayCardDisplayStyle.AppEventCard,
        TodayCardDisplayStyle.Grid,
        TodayCardDisplayStyle.InAppPurchase,
        TodayCardDisplayStyle.List,
        TodayCardDisplayStyle.NumberedList,
        TodayCardDisplayStyle.River,
        TodayCardDisplayStyle.ShortImage,
        TodayCardDisplayStyle.SingleApp,
    ]);
    if (!cardTypesRequiringClientIdentifierOverride.has(cardDisplayStyle)) {
        return false;
    }
    // Grids with >= 6 items are unsupported
    if (relatedContent.length >= 6 && cardDisplayStyle === TodayCardDisplayStyle.Grid) {
        return false;
    }
    // Content cards with Artwork are unsupported
    const artworkKey = editorialArtKeyPathForCardDisplayStyle(objectGraph, cardDisplayStyle);
    if (!mediaAttributes.attributeAsBooleanOrFalse(data, "ignoreEditorialArt") &&
        todayCardArtworkFromArtworkData(objectGraph, mediaAttributes.attributeAsDictionary(data, artworkKey), (_a = cardConfig.prevailingCropCodes) === null || _a === void 0 ? void 0 : _a.defaultCrop)) {
        return false;
    }
    // Content cards with iAP are unsupported
    if (inAppPurchaseDataFromRelatedContent(objectGraph, relatedContent)) {
        return false;
    }
    return true;
}
function inAppPurchaseDataFromRelatedContent(objectGraph, relatedContent) {
    if (relatedContent.length === 1) {
        const contentData = relatedContent[0];
        if (contentData.type === "in-apps") {
            return contentData;
        }
    }
    return null;
}
/**
 * If a card should be using the OTD style we need to make sure we set that on the card
 *
 * @param card The today card we're adding the style to
 * @param cardConfig The config used to create this card
 */
function addOTDStyleToCardIfNecessary(card, cardConfig) {
    if (isNothing(card.media)) {
        return;
    }
    card.media.otdTextStyle = cardConfig.useOTDTextStyle;
}
/**
 * Check if this card should have the flip and blur enabled, this is the case when a card has videos.
 * @param objectGraph The dependency graph for the app store
 * @param card The today card we're modifying
 */
function enableFlipAndBlurIfNecessary(objectGraph, card) {
    const cardMedia = card.media;
    const cardMediaHasArtwork = cardMedia instanceof TodayCardMediaWithArtwork;
    if (!cardMediaHasArtwork) {
        return;
    }
    const cardMdiaWithArtwork = cardMedia;
    const hasVideos = isDefinedNonNullNonEmpty(cardMdiaWithArtwork.videos);
    card.supportsMediaMirroring = hasVideos;
}
/**
 * @param data The media API data used to determine the intention of the card
 * @param cardConfig The configuration for the card
 * @returns Whether this app is an App of the Day or Game of the Day
 */
export function isCardOTDIntention(data, cardConfig) {
    let otdIntention = mediaAttributes.attributeAsString(data, "ofTheDayIntent");
    if (isNothing(otdIntention)) {
        const cardDisplayStyle = cardDisplayStyleFromData(data, cardConfig === null || cardConfig === void 0 ? void 0 : cardConfig.coercedCollectionTodayCardDisplayStyle);
        switch (cardDisplayStyle) {
            case TodayCardDisplayStyle.AppOfTheDay:
                otdIntention = OfTheDayIntention.AppOfTheDay;
                break;
            case TodayCardDisplayStyle.GameOfTheDay:
                otdIntention = OfTheDayIntention.GameOfTheDay;
                break;
            default:
                break;
        }
    }
    return otdIntention === OfTheDayIntention.AppOfTheDay || otdIntention === OfTheDayIntention.GameOfTheDay;
}
//# sourceMappingURL=today-card-util.js.map