/**
 * Created by ls on 5/15/18.
 */
import { Color, isSome } from "@jet/environment";
import * as models from "../../api/models/index";
import * as serverData from "../../foundation/json-parsing/server-data";
import { isDefinedNonNull } from "../../foundation/json-parsing/server-data";
import * as mediaFetching from "../../foundation/media/data-fetching";
import * as mediaNetwork from "../../foundation/media/network";
import * as mediaRelationships from "../../foundation/media/relationships";
import { Path, Protocol } from "../../foundation/network/url-constants";
import * as urls from "../../foundation/network/urls";
import * as artworkBuilder from "../content/artwork/artwork";
import * as metricsHelpersClicks from "../metrics/helpers/clicks";
import * as productPageVariants from "../product-page/product-page-variants";
import { MetricsIdentifierType } from "../../foundation/metrics/metrics-identifiers-cache";
import { named } from "../../foundation/util/color-util";
import { makeRoutableArcadeSeeAllPageIntent } from "../../api/intents/routable-arcade-see-all-page-intent";
import { getPlatform } from "../preview-platform";
import { getLocale } from "../locale";
import { makeArcadeSeeAllCanonicalUrl } from "./arcade-see-all-routing";
import { shouldUsePrerenderedIconArtwork } from "../content/content";
// endregion
// region Arcade Navigation Actions
/**
 * Creates a flow action for going to the page to see all Arcade games.
 * Defaults to sorting by release date (descending)
 * @param {ArcadeSeeAllGamesPageSort} sort The order which the games in response will be sorted in.
 * @param metricsPageInformation
 * @param metricsLocationTracker
 * @returns {FlowAction} Flow action to Arcade see all games page.
 */
export function seeAllArcadeGamesPageFlowAction(objectGraph, sort = "releaseDate", metricsPageInformation, metricsLocationTracker, title = undefined, id = undefined, idType = undefined, targetType = "button") {
    const seeAllGamesUrl = urls.URL.fromComponents(Protocol.internal, null, `/${Path.arcadeSeeAllGames}`, {
        sort: sort,
    });
    const flowAction = new models.FlowAction("arcadeSeeAllGames", seeAllGamesUrl.build());
    flowAction.title = title !== null && title !== void 0 ? title : objectGraph.loc.string("Arcade.SeeAllGames.Button.Title");
    if (objectGraph.client.isWeb) {
        const destination = makeRoutableArcadeSeeAllPageIntent({
            ...getLocale(objectGraph),
            ...getPlatform(objectGraph),
        });
        const pageUrl = makeArcadeSeeAllCanonicalUrl(objectGraph, destination);
        flowAction.destination = destination;
        flowAction.pageUrl = pageUrl;
    }
    const itemId = id !== null && id !== void 0 ? id : (objectGraph.client.isVision ? "SeeAllGames" : "arcade-see-all-games-button");
    const seeAllClickOptions = {
        id: itemId,
        idType: idType,
        targetType: targetType,
        actionType: "navigate",
        actionContext: "Arcade",
        pageInformation: metricsPageInformation,
        locationTracker: metricsLocationTracker,
    };
    metricsHelpersClicks.addClickEventToAction(objectGraph, flowAction, seeAllClickOptions);
    return flowAction;
}
/**
 * Create a flow action for opening the Arcade Subscribe page with optional parameters
 * @param context The context in which the Arcade Subscribe page is being opened for, e.g. where the flow action was initiated from.
 * @param contextualAppId Optional app ID to associate with flow, if any. This is used for flow into contextual upsell sheet.
 * @returns {FlowAction} Flow action to `arcadeSubscribe` page.
 */
export function arcadeSubscribePageFlowAction(objectGraph, context, contextualAppId, purchaseSuccessAction, options) {
    var _a, _b, _c, _d;
    const upsellRequestInfo = new models.MarketingItemRequestInfo("arcade", context, objectGraph.bag.metricsTopic, contextualAppId);
    upsellRequestInfo.purchaseSuccessAction = purchaseSuccessAction;
    upsellRequestInfo.carrierLinkSuccessAction = purchaseSuccessAction;
    const action = new models.FlowAction("upsellMarketingItem");
    if (isSome((_b = (_a = options === null || options === void 0 ? void 0 : options.pageInformation) === null || _a === void 0 ? void 0 : _a.searchTermContext) === null || _b === void 0 ? void 0 : _b.term)) {
        upsellRequestInfo.metricsOverlay["searchTerm"] = (_c = options.pageInformation.searchTermContext) === null || _c === void 0 ? void 0 : _c.term;
    }
    const metricsIdentifierFields = (_d = objectGraph.metricsIdentifiersCache) === null || _d === void 0 ? void 0 : _d.getMetricsFieldsForTypes([
        MetricsIdentifierType.user,
        MetricsIdentifierType.client,
    ]);
    if (isSome(metricsIdentifierFields)) {
        upsellRequestInfo.metricsOverlay = {
            ...upsellRequestInfo.metricsOverlay,
            ...metricsIdentifierFields,
        };
    }
    action.pageData = upsellRequestInfo;
    if (serverData.isDefinedNonNull(options)) {
        metricsHelpersClicks.addClickEventToArcadeBuyInitiateAction(objectGraph, action, options);
    }
    return action;
}
/**
 * Action to open the main Arcade page on each platform.
 * Depending on the platform, this can be a tab change action or open action to separate Arcade app.
 */
export function openArcadeMainAction(objectGraph, metricsPageInformation, metricsLocationTracker, popToRoot) {
    if (objectGraph.client.isTV) {
        return openTVArcadeAppAction(objectGraph);
    }
    else {
        const arcadeTabChangeAction = new models.TabChangeAction("arcade");
        if (serverData.isDefinedNonNull(popToRoot)) {
            arcadeTabChangeAction.popToRoot = popToRoot;
        }
        /*
         * Presidio / Yukon timeframe workaround for <rdar://problem/53600942> Allow deserialized TabChangeAction to have use `title` property from JS instead of always using `nil`
         * We're wrapping a single `TabChangeAction` within `CompoundAction` since `TabChangeAction` deserialization drops the JS provided title.
         *
         * Tracking removing this workaround in:
         * <rdar://problem/53601182> Arcade: Remove workaround for having a tab change action with a title
         */
        return new models.CompoundAction([arcadeTabChangeAction]);
    }
}
/**
 * Creates an action to open Arcade app on tvOS.
 */
export function openTVArcadeAppAction(objectGraph) {
    const url = "com.apple.Arcade://";
    return new models.ExternalUrlAction(url);
}
// endregion
/**
 * Creates an action to open GamesUI.
 */
export function openGamesUIAction(objectGraph, target = { playNow: {} }) {
    return new models.OpenGamesUIAction(target);
}
/**
 * Creates Game Center header.
 */
export function makeGameCenterHeader(objectGraph, title = undefined, subtitle = undefined, useTitleArtwork = undefined) {
    let eyebrowArtwork;
    if (objectGraph.client.isTV) {
        eyebrowArtwork = artworkBuilder.createArtworkForResource(objectGraph, "systemimage://gamecenter.fill", 16, 16);
    }
    else {
        eyebrowArtwork = artworkBuilder.createArtworkForResource(objectGraph, "resource://GameCenterEyebrow", 16, 16);
    }
    const isShelfHeaderEnabled = objectGraph.featureFlags.isEnabled("shelf_header");
    const isGameCenterShelfHeaderEnabled = objectGraph.featureFlags.isEnabled("game_center_shelf_header");
    const isGSEUIEnabled = objectGraph.featureFlags.isGSEUIEnabled("de7bbd8e");
    if (isGSEUIEnabled) {
        const configuration = {
            eyebrowColor: named("secondaryText"),
            includeSeparator: !isShelfHeaderEnabled,
            prefersShelfHeader: isGameCenterShelfHeaderEnabled,
        };
        const eyebrow = objectGraph.loc.string("GAME_CENTER");
        const shelfHeader = {
            eyebrow: eyebrow,
            eyebrowArtwork: eyebrowArtwork,
            eyebrowArtworkType: models.ShelfHeaderArtworkType.Icon,
            title: title,
            subtitle: subtitle,
            configuration: configuration,
        };
        return shelfHeader;
    }
    else {
        const configuration = {
            eyebrowColor: isGSEUIEnabled ? named("systemBlue") : undefined,
            includeSeparator: !isShelfHeaderEnabled,
            prefersShelfHeader: isGameCenterShelfHeaderEnabled,
        };
        if (isSome(useTitleArtwork) && useTitleArtwork) {
            const shelfHeader = {
                title: title,
                titleArtwork: eyebrowArtwork,
                titleArtworkType: models.ShelfHeaderArtworkType.Icon,
                subtitle: subtitle,
                configuration: configuration,
            };
            return shelfHeader;
        }
        else {
            const eyebrow = objectGraph.loc.uppercased(objectGraph.loc.string("GAME_CENTER"));
            const shelfHeader = {
                eyebrow: eyebrow,
                eyebrowArtwork: eyebrowArtwork,
                eyebrowArtworkType: models.ShelfHeaderArtworkType.Icon,
                title: title,
                subtitle: subtitle,
                configuration: configuration,
            };
            return shelfHeader;
        }
    }
}
// region Arcade Catalog MAPI Requests
/**
 * Base request for all MAPI requests fetching a set of Arcade games from catalog.
 * This request has a server-defined implicit limit value (e.g. 100).
 * This request should be bare-bones. If additional attributes are needed, it should be chained to this request.
 *
 * @returns {mediaFetching.Request} Request object for fetching Arcade apps from MAPI catalog endpoint.
 */
export function arcadeAppsRequest(objectGraph, includeComingSoon = false) {
    let request = new mediaFetching.Request(objectGraph).forType("arcade-apps").includingAgeRestrictions();
    if (includeComingSoon) {
        request = request.addingQuery("with", "comingSoonApps");
    }
    // For visionOS, we need icons for bincompat games.
    if (objectGraph.client.isVision) {
        request = request.includingAdditionalPlatforms(["iphone", "ipad"]);
        request.attributeIncludes.add("compatibilityControllerRequirement");
    }
    return request;
}
/**
 * Request for fetching a set of arcade apps for displaying a set of Arcade Icons, e.g. for Arcade Grouping Footer, Contextual Upsell Icon Grid, and iOS Arcade Showcase.
 * This request is a very special request with MAPI data containing only `artwork` attribute. Data is meant to be used *as-is* for showing a set of icons only.
 *
 * Requirements:
 * - Icon Artwork for apps only. Additional metadata should be pruned if possible.
 *
 * @param limit Limit of apps. This should be configured to fit the view's needs.
 */
export function arcadeAppsRequestForIcons(objectGraph, limit) {
    return arcadeAppsRequest(objectGraph)
        .withSparseLimit(limit)
        .asPartialResponseLimitedToFields(["artwork"])
        .usingCustomAttributes(productPageVariants.shouldFetchCustomAttributes(objectGraph));
}
// endregion
// region Arcade Upsell Request
export function arcadeUpsellRequest(objectGraph, context, contextualAppId) {
    return arcadeUpsellMarketingItemRequest(objectGraph, context, contextualAppId);
}
export function arcadeUpsellMarketingItemRequest(objectGraph, context, contextualAppId) {
    // We always want `context` to be provided, but some callers will provide this value from an messy source, e.g. extracted from URL param. Fall back to `generic` just in case.
    if (serverData.isNullOrEmpty(context)) {
        context = models.marketingItemContextFromString("generic");
    }
    const request = new mediaFetching.Request(objectGraph)
        .forType("upsellMarketingItem")
        .addingQuery("serviceType", "arcade")
        .addingQuery("placement", context)
        .includingMetaKeys("marketing-items", ["metrics"])
        .includingRelationships(["contents"])
        .includingAttributes(["marketingArtwork", "marketingVideo"])
        .includingAgeRestrictions();
    // Append app id that is promoted, if any.
    if (serverData.isDefinedNonNull(contextualAppId)) {
        request.addingQuery("seed", contextualAppId);
    }
    return request;
}
// endregion
/**
 * Grab recently played games.
 * @param objectGraph The object graph.
 * @param {number} limit The number of recently played games to return.
 * @param {boolean} shouldHydrateAppsData Whether the apps data should be fetched from media api.
 * @param {number} timeout A timeout in seconds.
 * @returns {DataContainer} The media api data container with the recently played games.
 */
export async function getRecentlyPlayedGames(objectGraph, limit = null, shouldHydrateAppsData = false, timeout = null) {
    return await new Promise((resolve, reject) => {
        const isRecentlyPlayedGamesSupported = objectGraph.host.isiOS || objectGraph.host.isMac || objectGraph.host.isTV;
        // Check if the client supports recently played games.
        if (!isRecentlyPlayedGamesSupported) {
            resolve(null);
            return;
        }
        const getRecentlyPlayGamesPromise = objectGraph.arcade.getRecentlyPlayedGamesWithTimeout(timeout);
        // Get recently played games
        getRecentlyPlayGamesPromise
            .then((adamIds) => {
            // Only perform request when there are recently played games.
            if (serverData.isNull(adamIds) || adamIds.length === 0) {
                resolve(null);
                return;
            }
            // Enforce limit (if any).
            if (serverData.isNumber(limit) && adamIds.length > limit) {
                adamIds = adamIds.slice(0, limit);
            }
            if (shouldHydrateAppsData) {
                // Fetch data for recently played games.
                const attributes = [
                    "editorialArtwork",
                    "editorialVideo",
                    "description",
                    "minimumOSVersion",
                    "minPlayers",
                    "maxPlayers",
                    "remoteControllerRequirement",
                    "requiresGameController",
                    "supportsSharePlay",
                ];
                if (objectGraph.client.isVision) {
                    attributes.push("compatibilityControllerRequirement");
                }
                if (objectGraph.client.isMac) {
                    attributes.push("hasMacIPAPackage");
                }
                if (objectGraph.bag.enableUpdatedAgeRatings) {
                    attributes.push("ageRating");
                }
                if (shouldUsePrerenderedIconArtwork(objectGraph)) {
                    attributes.push("iconArtwork");
                }
                const mediaApiRequest = new mediaFetching.Request(objectGraph)
                    .withIdsOfType(adamIds, "apps")
                    .includingAttributes(attributes);
                const fetchDataPromise = mediaNetwork.fetchData(objectGraph, mediaApiRequest);
                fetchDataPromise.then((dataContainer) => resolve(dataContainer), (reason) => {
                    objectGraph.console.log(`getRecentlyPlayedGames() failed when calling mediaNetwork.fetchData() with reason: ${reason}`);
                    resolve(null);
                });
            }
            else {
                // Create an incomplete data container to be fetched later.
                const dataContainer = {
                    data: [],
                };
                adamIds.forEach((adamId) => {
                    dataContainer.data.push({
                        id: adamId,
                        type: "apps",
                    });
                });
                resolve(dataContainer);
            }
        })
            .catch((reason) => {
            objectGraph.console.log(`getRecentlyPlayedGames() failed with: ${reason}`);
            resolve(null);
        });
    });
}
/**
 * Convenience function to build a `ArcadeUpsellData` representation from upsell relationship joined to some data.
 * @seealso `upsellFromContentsOfUpsellResponse`
 * @param objectGraph The object graph
 * @param data Data containing `upsell` relationship to build `ArcadeUpsellData` with
 */
export function upsellFromRelationshipOf(objectGraph, data) {
    // Data to extract.
    let marketingItemData = null;
    const upsellDataContainer = objectGraph.client.isVision || preprocessor.GAMES_TARGET
        ? mediaRelationships.relationship(data, "contents")
        : mediaRelationships.relationship(data, "upsell") ||
            mediaRelationships.relationship(data, "marketing-items");
    if (serverData.isNullOrEmpty(upsellDataContainer) || serverData.isNullOrEmpty(upsellDataContainer.data)) {
        return null;
    }
    // Create marketing items array from data container.
    const marketingItems = upsellDataContainer.data
        .map((item) => {
        if (item.type === "marketing-items") {
            return item;
        }
        else {
            return null;
        }
    })
        .filter((item) => isDefinedNonNull(item));
    // Return null if there are NOT any marketing items.
    if (serverData.isNullOrEmpty(marketingItems)) {
        return null;
    }
    const timeout = objectGraph.bag.marketingItemSelectionTimeout;
    // If there is only one marketing item or timeout is zero, set this as the data.
    // Otherwise get a single marketing item by calling AMS.
    if (marketingItems.length === 1 || timeout === 0) {
        marketingItemData = marketingItems[0];
    }
    else {
        try {
            marketingItemData = objectGraph.arcade.getMarketingItemWithTimeout(marketingItems, timeout);
        }
        catch {
            // Default to first item if the call was timed out.
            marketingItemData = marketingItems[0];
        }
    }
    // Return null if marketing item is null.
    if (serverData.isNull(marketingItemData)) {
        return null;
    }
    return {
        marketingItemData: marketingItemData,
    };
}
/**
 * Convenience function to build a `ArcadeUpsellData` representation from contents of a engagement/upsell response.
 * This doesn't really belong in
 * @seealso `upsellFromRelationshipOf`
 * @param arcadeUpsellResponse Response from the engagement/upsell endpoint.
 */
export function upsellFromContentsOfUpsellResponse(objectGraph, arcadeUpsellResponse) {
    if (!arcadeUpsellResponse) {
        return null;
    }
    let marketingItemData = null;
    const responseDataArray = serverData.asArrayOrEmpty(arcadeUpsellResponse, "results.data");
    if (responseDataArray.length > 0) {
        marketingItemData = responseDataArray[0];
    }
    /**
     * `arcadeUpsellResponse` is expected of form:
     * {
     *      content: { mediaDataStructure.Data }
     * }
     * matching what is provided via the appropriate `action=<usage>` param from engagement/upsell endpoint.
     */
    if (!serverData.isDefinedNonNull(marketingItemData)) {
        return null;
    }
    return {
        marketingItemData: marketingItemData,
    };
}
// endregion
// region Arcade Games For You Request
/**
 * Request for fetching list of recommended Arcade apps from recommendations API.
 * @param objectGraph The App Store object graph.
 * @param limit Maximum number of apps to fetch.
 * @returns {Request} Request object for fetching recommended Arcade apps from
 * personalized recommendations endpoint.
 */
export function arcadeGamesForYouRequest(objectGraph, limit) {
    let request = new mediaFetching.Request(objectGraph)
        .forType("personal-recommendations")
        .addingQuery("sparseLimit[contents]", `${limit}`)
        .addingQuery("include[personal-recommendations]", "contents")
        .addingQuery("filter[kind]", "arcadeGamesForYou")
        .includingAgeRestrictions();
    // For visionOS, we need to include bincompat apps.
    if (objectGraph.client.isVision) {
        request = request.includingAdditionalPlatforms(["iphone", "ipad"]);
    }
    return request;
}
// endregion
// The color to use for Arcade content.
export const arcadeColor = Color.fromRGB(1, 90 / 255, 80 / 255);
//# sourceMappingURL=arcade-common.js.map