//
//  search.ts
//  AppStoreKit
//
//  Created by Kevin MacWhinnie on 8/15/16.
//  Copyright (c) 2016 Apple Inc. All rights reserved.
//
import { isNothing, isSome } from "@jet/environment";
import * as validation from "@jet/environment/json/validation";
import { FetchTimingMetricsBuilder } from "@jet/environment/metrics/fetch-timing-metrics-builder";
import { PageInvocationPoint } from "@jet/environment/types/metrics";
import * as models from "../../api/models";
import { SearchResultsLearnMoreNotice } from "../../api/models";
import * as serverData from "../../foundation/json-parsing/server-data";
import * as mediaDataStructure from "../../foundation/media/data-structure";
import * as contentArtwork from "../content/artwork/artwork";
import * as metricsBuilder from "../metrics/builder";
import * as metricsHelpersClicks from "../metrics/helpers/clicks";
import * as metricsHelpersImpressions from "../metrics/helpers/impressions";
import * as metricsHelpersLocation from "../metrics/helpers/location";
import * as metricsHelpersMisc from "../metrics/helpers/misc";
import * as metricsHelpersPage from "../metrics/helpers/page";
import * as guidedSearch from "./guided-search/guided-search";
import { addGuidedSearchParentImpressionMetrics, addSearchResultParentImpressionMetrics, } from "./guided-search/guided-search-metrics";
import * as searchAdsODML from "./search-ads-odml";
import * as searchCommon from "./search-common";
import { createDefaultSelectedFacetOptions, createSearchFacets, createSearchPageFacets } from "./search-facets";
import * as searchResultsFetching from "./search-results-fetching";
import { createSearchResultsLearnMoreNoticeLinkableText } from "./search-results-learn-more-notice";
import * as searchResultsPipeline from "./search-results-pipeline";
import * as searchSpellCorrection from "./search-spell-correction";
import * as searchToken from "./search-token";
// MARK: - Search Hints
/**
 *  Convert the response from the search hints endpoint into a model object.
 *  @param  {String} prefixTerm     The term the search hints are for (i.e. searchPrefix).
 *  @param  {String} searchUrl      The Url if we were to search for this term right away
 *  @param  {*} response    The API response.
 *  @return {SearchHintSet} The search hint set containing the search hint array
 */
export function searchHintsFromApiResponse(objectGraph, prefixTerm, hintsContainer) {
    return validation.context("searchHintsFromApiResponse", () => {
        var _a, _b, _c, _d;
        const metricsOptions = {
            targetType: "listItem",
            pageInformation: metricsHelpersPage.pageInformationForSearchHintsPage(objectGraph, prefixTerm, hintsContainer.hintsRequestUrl, hintsContainer.dataSetId),
            locationTracker: metricsHelpersLocation.newLocationTracker(),
        };
        // Build user hint that matches what user typed. Appears in first position.
        let userTypedHintAction = null;
        if ((_a = hintsContainer.userTypedTerm) === null || _a === void 0 ? void 0 : _a.length) {
            userTypedHintAction = new models.SearchAction(hintsContainer.userTypedTerm, hintsContainer.userTypedTerm, null, "userTypedHint");
            userTypedHintAction.spellCheckEnabled = true;
            userTypedHintAction.prefixTerm = prefixTerm;
            metricsHelpersImpressions.addImpressionMetricsToHintsSearchAction(objectGraph, userTypedHintAction, metricsOptions);
            metricsHelpersClicks.addEventsToSearchAction(objectGraph, userTypedHintAction, metricsOptions.targetType, metricsOptions.locationTracker, metricsOptions.pageInformation);
            metricsHelpersLocation.nextPosition(metricsOptions.locationTracker);
        }
        // Build standard hints.
        const searchHintActions = (_c = (_b = hintsContainer.rawHints) === null || _b === void 0 ? void 0 : _b.map((rawHint) => {
            return searchHintAction(objectGraph, rawHint, prefixTerm, metricsOptions);
        })) !== null && _c !== void 0 ? _c : [];
        // Prepend user hint if any.
        if (userTypedHintAction != null) {
            searchHintActions.unshift(userTypedHintAction);
        }
        const hintSet = new models.SearchHintSet(searchHintActions, (_d = hintsContainer.ghostHintsTerm) !== null && _d !== void 0 ? _d : null);
        /**
         * Send `input` Search Events when search hints returns.
         * For SSS, this is the granularity we agreed on, instead of sending it in native per keystroke.
         * Unlike standard page metrics, we only:
         * - Fire a 'input' search event
         * - Setup pageFields for page fields generator.
         */
        const searchEvent = metricsBuilder.createMetricsSearchData(objectGraph, prefixTerm, "key", "input", hintsContainer.hintsRequestUrl, { ...metricsHelpersMisc.fieldsFromPageInformation(metricsOptions.pageInformation) });
        hintSet.pageMetrics.pageFields = metricsHelpersMisc.fieldsFromPageInformation(metricsOptions.pageInformation);
        hintSet.pageMetrics.addData(searchEvent, [PageInvocationPoint.pageEnter]);
        hintSet.searchClearAction = createSearchCancelledOrClearedAction(objectGraph, "clear", metricsOptions.pageInformation, metricsOptions.locationTracker, prefixTerm);
        hintSet.searchCancelAction = createSearchCancelledOrClearedAction(objectGraph, "cancel", metricsOptions.pageInformation, metricsOptions.locationTracker, prefixTerm);
        return hintSet;
    });
}
/**
 * Creates a search hint action from the search hint data
 * @param objectGraph The App Store object graph
 * @param hintData The hint object data
 * @param prefixTerm The hint prefix term
 * @param metricsOptions The metrics options to use for click and impressions metrics for this hint
 * @returns A search hint action for the hint data
 */
function searchHintAction(objectGraph, hintData, prefixTerm, metricsOptions) {
    var _a, _b, _c, _d, _e;
    const searchEntity = (_a = searchEntityFromHintData(hintData)) !== null && _a !== void 0 ? _a : undefined;
    const searchAction = new models.SearchAction((_b = hintData.displayTerm) !== null && _b !== void 0 ? _b : "", (_c = hintData.searchTerm) !== null && _c !== void 0 ? _c : "", null, "hints", searchEntity, hintData.source);
    searchAction.artwork = contentArtwork.createArtworkForSystemImage(objectGraph, (_d = models.searchEntitySystemImage(searchEntity)) !== null && _d !== void 0 ? _d : "magnifyingglass");
    searchAction.spellCheckEnabled = true;
    searchAction.prefixTerm = prefixTerm;
    metricsHelpersImpressions.addImpressionMetricsToHintsSearchAction(objectGraph, searchAction, metricsOptions);
    metricsHelpersClicks.addEventsToSearchAction(objectGraph, searchAction, metricsOptions.targetType, metricsOptions.locationTracker, (_e = metricsOptions.pageInformation) !== null && _e !== void 0 ? _e : undefined);
    metricsHelpersLocation.nextPosition(metricsOptions.locationTracker);
    return searchAction;
}
/**
 *
 * @param objectGraph The App Store object graph
 * @param entity The entity value string from the hint object
 * @param context The context value string from the hint object
 * @returns The correct entity for the hint based on legacy entity types
 *
 * Entities:
 * "arcade"     ==>     "apps" with context "arcade"
   "developer"  ==>     "developers"
   "story"      ==>     "editorial-items"
   "watch"      ==>     "apps" with context "watch"
 */
function searchEntityFromHintData(rawHint) {
    const hintEntity = rawHint.entity;
    switch (hintEntity) {
        case "apps":
            return rawHint.context;
        case "developers":
            return "developer";
        case "editorial-items":
            return "story";
        default:
            return null;
    }
}
// MARK: - Trending Searches
/**
 *  Convert the response from the trending searches endpoint into a model object.
 *  @param  {*} response    The API response.
 *  @return {TrendingSearch} A trending searches model object.
 */
export function trendingSearchesFromApiResponse(objectGraph, response) {
    return validation.context("trendingSearchesFromApiResponse", () => {
        const locationTracker = metricsHelpersLocation.newLocationTracker();
        const searches = serverData.asArrayOrEmpty(response, "trendingSearches").map((rawSearch) => {
            const term = serverData.asString(rawSearch, "label");
            const searchAction = new models.SearchAction(term, term, serverData.asString(rawSearch, "url"), "trending");
            metricsHelpersClicks.addEventsToSearchAction(objectGraph, searchAction, "button", locationTracker);
            metricsHelpersLocation.nextPosition(locationTracker);
            return searchAction;
        });
        const title = searches.length > 0 ? serverData.asString(response, "header.label") : null;
        const trendingSearches = new models.TrendingSearches(title, searches);
        switch (objectGraph.client.deviceType) {
            case "pad":
                trendingSearches.maxNumberOfSearches = 10;
                break;
            case "phone":
                trendingSearches.maxNumberOfSearches = 7;
                break;
            default:
                break;
        }
        return trendingSearches;
    });
}
// MARK: - Search Results
/**
 * Gets the shelf ID for search results, on a specfic segment if applicable
 * @param segmentTitle The title of the segment this shelf is on, if using segmented search
 * @returns The id for the search results shelf
 * @note This needs to include the segment title for segment search results since the contents on
 * each segment would otherwise have the same shelf id and therefore would lead to the content never changing
 */
function getSearchResultsShelfId(segmentTitle) {
    if (isSome(segmentTitle) && segmentTitle.length !== 0) {
        return `SearchResults.${segmentTitle}.shelfId`;
    }
    else {
        return "SearchResults.shelfId";
    }
}
/**
 * Gets metrics pageid to use for the search results page.
 * @param segmentType The segment this page is displaying if any
 * @returns The id for the search results page
 */
function getSearchResultsPageId(segmentType) {
    switch (segmentType) {
        case models.SegmentedSearchResultsPageSegmentType.iOS:
            return "ios";
        case models.SegmentedSearchResultsPageSegmentType.visionOS:
            return "visionos";
        default:
            return "SearchTopResults";
    }
}
/**
 *  Creates a new empty `SearchResults` object.
 *  @return {SearchResults} An empty search results model object.
 */
export function emptyResults(objectGraph, requestFacets) {
    const searchResults = new models.SearchResults();
    if (serverData.isDefinedNonNull(requestFacets)) {
        searchResults.facets = createSearchFacets(objectGraph, requestFacets);
        searchResults.pageFacets = createSearchPageFacets(objectGraph);
        searchResults.selectedFacetOptions = createDefaultSelectedFacetOptions(objectGraph);
    }
    searchResults.results = [];
    return searchResults;
}
/**
 *  Creates a new empty `SearchResultsPage` object.
 *  @return {SearchResults} An empty search results model object.
 */
export function emptyResultsPage(objectGraph, requestFacets) {
    const searchResultsPage = new models.SearchResultsPage([]);
    if (serverData.isDefinedNonNull(requestFacets)) {
        searchResultsPage.facets = createSearchFacets(objectGraph, requestFacets);
        searchResultsPage.pageFacets = createSearchPageFacets(objectGraph);
        searchResultsPage.selectedFacetOptions = createDefaultSelectedFacetOptions(objectGraph);
    }
    return searchResultsPage;
}
/**
 * A container like class to manage a search results page segment and surrounding context
 */
class SegmentedSearchResultsPageSegmentContext {
}
/**
 * Builds `BaseSearchPage` from data.
 */
async function baseSearchPageFromResponse(objectGraph, combinedSearchData) {
    return await validation.context("searchResultsFromResponse", async () => {
        var _a;
        const fetchTimingMetricsBuilder = (_a = objectGraph.fetchTimingMetricsBuilder) !== null && _a !== void 0 ? _a : new FetchTimingMetricsBuilder();
        const page = await fetchTimingMetricsBuilder.measureModelConstructionAsync(async () => {
            var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
            const response = combinedSearchData.catalogResponse;
            const requestMetadata = combinedSearchData.requestMetadata;
            const searchRequestUrl = requestMetadata.searchRequestUrl;
            const sponsoredSearchRequestData = combinedSearchData.sponsoredSearchRequestData;
            const guidedSearchData = response.results.guidedSearch;
            // Term Context
            const termContext = searchCommon.createTermContextForSpellcheckedSequentialResponse(objectGraph, requestMetadata.requestDescriptor, response);
            // Metrics
            const wasOdmlSuccessful = searchAdsODML.wasODMLSuccessful(objectGraph, combinedSearchData.sponsoredSearchAdvertData);
            const metricsOptions = {
                locationTracker: metricsHelpersLocation.newLocationTracker(),
                pageInformation: metricsHelpersPage.pageInformationForSearchPage(objectGraph, requestMetadata.requestDescriptor, response, termContext, searchRequestUrl, getSearchResultsPageId(), sponsoredSearchRequestData, wasOdmlSuccessful, guidedSearchData),
                createUniqueImpressionId: true,
            };
            // Root container
            const isShelfBasedSearch = objectGraph.featureFlags.isEnabled("shelves_2_0_search") ||
                objectGraph.client.isiOS ||
                objectGraph.client.isTV ||
                objectGraph.client.isVision ||
                objectGraph.client.isWeb;
            const baseSearchPage = isShelfBasedSearch
                ? new models.SearchResultsPage()
                : new models.SearchResults();
            // Guided Search Experimentation (pinned vs mid-scroll)
            const guidedSearchPosition = searchResultsPipeline.guidedSearchPositionFromSearchResponseMeta(response.meta, objectGraph);
            if (isNothing(guidedSearchPosition)) {
                /**
                 * Guided Search
                 * - note: This is important to occur before `createSearchResults` since its impression index should be lower than the container for search results.
                 */
                addModelsForGuidedSearch(objectGraph, baseSearchPage, requestMetadata, guidedSearchData === null || guidedSearchData === void 0 ? void 0 : guidedSearchData.facets, metricsOptions);
            }
            const shelfMetricsOptions = {
                id: "search-results",
                kind: null,
                softwareType: null,
                targetType: "SearchResults",
                title: "Search Results",
                pageInformation: metricsOptions.pageInformation,
                locationTracker: metricsOptions.locationTracker,
                idType: "relationship",
            };
            let resultsShelf;
            if (isShelfBasedSearch) {
                resultsShelf = new models.Shelf("searchResult");
                resultsShelf.id = getSearchResultsShelfId();
                resultsShelf.isHorizontal = false;
                if (objectGraph.client.isWeb) {
                    resultsShelf.title = objectGraph.loc
                        .string("Search.ResultsTitle")
                        .replace("@@search_term@@", termContext.term);
                }
                // We need to create and apply impressions fields to the shelf before creating the search results,
                // so that the shelf is sitting in the right location relative to its children from an impressions perspective.
                metricsHelpersImpressions.addImpressionFields(objectGraph, resultsShelf, shelfMetricsOptions);
                metricsHelpersLocation.pushContentLocation(objectGraph, shelfMetricsOptions, "Search Results");
            }
            let advertData = dataForAdvertsFromCombinedData(objectGraph, combinedSearchData);
            const appliedPolicy = (_a = combinedSearchData.sponsoredSearchAdvertData) === null || _a === void 0 ? void 0 : _a.appliedPolicy;
            if (isSome(appliedPolicy)) {
                // If there was an applied policy, suppress the ad.
                advertData = [];
                if (appliedPolicy === "ageRestricted") {
                    (_b = metricsOptions.pageInformation) === null || _b === void 0 ? void 0 : _b.iAdInfo.setMissedOpportunity(objectGraph, "ODP_NOAD", "searchResults");
                }
            }
            const unsafeSearch = (_f = (_e = (_d = (_c = response.meta) === null || _c === void 0 ? void 0 : _c.results) === null || _d === void 0 ? void 0 : _d.search) === null || _e === void 0 ? void 0 : _e.searchSafety) !== null && _f !== void 0 ? _f : false;
            const missedOpportunityReason = ((_k = (_j = (_h = (_g = response.meta) === null || _g === void 0 ? void 0 : _g.results) === null || _h === void 0 ? void 0 : _h.search) === null || _j === void 0 ? void 0 : _j.reason) === null || _k === void 0 ? void 0 : _k.kind) === "no-results" ? "NLS_NORESULTS" : "NLS_NOAD";
            if (unsafeSearch) {
                // If the search was deemed unsafe, suppress Ad and record the missed opportunity.
                advertData = [];
                (_l = metricsOptions.pageInformation) === null || _l === void 0 ? void 0 : _l.iAdInfo.setMissedOpportunity(objectGraph, missedOpportunityReason, "searchResults");
            }
            /**
             * Advert + Search Results
             */
            const builderResults = await searchResultsPipeline.createSearchResults(objectGraph, requestMetadata, response.meta, metricsOptions, dataForSearchResultsFromCombinedData(objectGraph, combinedSearchData), advertData, guidedSearchData === null || guidedSearchData === void 0 ? void 0 : guidedSearchData.facets, installedStatesForAdvertsData(combinedSearchData), appStatesForAdvertsData(combinedSearchData));
            if (unsafeSearch && builderResults.builtSearchResults.length !== 0) {
                const searchAdOpportunity = builderResults.builtSearchResults[0].lockup
                    .searchAdOpportunity;
                searchAdOpportunity === null || searchAdOpportunity === void 0 ? void 0 : searchAdOpportunity.setMissedOpportunityReason(missedOpportunityReason);
                searchAdOpportunity === null || searchAdOpportunity === void 0 ? void 0 : searchAdOpportunity.setTemplateType("APPLOCKUP");
            }
            // Add result shelves to page.
            if (isShelfBasedSearch && resultsShelf) {
                const searchResultsPage = baseSearchPage;
                resultsShelf.items = builderResults.builtSearchResults;
                searchResultsPage.resultsParentImpressionMetrics = resultsShelf.impressionMetrics;
                searchResultsPage.shelves.push(resultsShelf);
                // Display the context message for the results (or lack thereof).
                const contextCard = createSearchResultsContextCard(response.results.queryContext, objectGraph);
                const resultsReason = (_p = (_o = (_m = response.meta) === null || _m === void 0 ? void 0 : _m.results) === null || _o === void 0 ? void 0 : _o.search) === null || _p === void 0 ? void 0 : _p.reason;
                if ((resultsReason === null || resultsReason === void 0 ? void 0 : resultsReason.kind) === "no-results") {
                    searchResultsPage.unavailableReason = {
                        title: objectGraph.loc.stringWithFallback("Search.Results.Empty.Title", "No results"),
                        message: resultsReason.text,
                        action: actionFromSearchResultsLinks(resultsReason.links),
                        contextCard: contextCard,
                    };
                }
                else if (contextCard) {
                    const paragraphShelf = new models.Shelf("searchResultsContextCard");
                    paragraphShelf.items = [contextCard];
                    const contextCardPosition = (_t = (_s = (_r = (_q = response.meta) === null || _q === void 0 ? void 0 : _q.displayStyle) === null || _r === void 0 ? void 0 : _r.queryContext) === null || _s === void 0 ? void 0 : _s.position) !== null && _t !== void 0 ? _t : 0;
                    if (contextCardPosition > 0) {
                        const postCardShelfContents = resultsShelf.items.splice(contextCardPosition);
                        const postCardResulsShelf = {
                            ...resultsShelf,
                            id: "searchResults2",
                            items: postCardShelfContents,
                            isValid: resultsShelf.isValid,
                        };
                        searchResultsPage.shelves.push(paragraphShelf);
                        searchResultsPage.shelves.push(postCardResulsShelf);
                    }
                    else {
                        searchResultsPage.shelves.unshift(paragraphShelf);
                    }
                }
                // Remove unsafe searches from recents if no results were provided.
                if (unsafeSearch && builderResults.builtSearchResults.length === 0) {
                    (_v = (_u = objectGraph.onDeviceSearchHistoryManager).removeRecentSearchTerm) === null || _v === void 0 ? void 0 : _v.call(_u, termContext.term);
                }
            }
            else {
                const searchResults = baseSearchPage;
                searchResults.results = builderResults.builtSearchResults;
                addSearchResultParentImpressionMetrics(objectGraph, searchResults, metricsOptions);
            }
            /**
             * Next Page
             */
            if (builderResults.deferredSearchResults.length > 0) {
                baseSearchPage.nextPage = searchToken.createSearchToken(objectGraph, builderResults.deferredSearchResults, requestMetadata, response.meta, metricsOptions);
            }
            if (isShelfBasedSearch) {
                metricsHelpersLocation.popLocation(shelfMetricsOptions.locationTracker);
            }
            /**
             * Spell Correction Message
             */
            baseSearchPage.message = searchSpellCorrection.spellCorrectionMessageFromTermContext(objectGraph, termContext, metricsOptions);
            /**
             * Search Entity
             */
            const searchEntity = requestMetadata.requestDescriptor.searchEntity;
            const searchEntitySpecified = !serverData.isNullOrEmpty(searchEntity);
            if (!searchEntitySpecified) {
                baseSearchPage.facets = createSearchFacets(objectGraph, requestMetadata.requestDescriptor.facets, combinedSearchData.categoriesFilterData);
                baseSearchPage.pageFacets = createSearchPageFacets(objectGraph, combinedSearchData.categoriesFilterData);
                baseSearchPage.selectedFacetOptions = serverData.isDefinedNonNullNonEmpty(combinedSearchData.requestMetadata.requestDescriptor.selectedFacetOptions)
                    ? combinedSearchData.requestMetadata.requestDescriptor.selectedFacetOptions
                    : createDefaultSelectedFacetOptions(objectGraph);
            }
            // Attach search term context
            baseSearchPage.searchTermContext = termContext;
            // Enable autoplay search results on all clients except tv.
            baseSearchPage.isAutoPlayEnabled = objectGraph.client.deviceType !== "tv";
            baseSearchPage.isCondensedSearchLockupsEnabled = objectGraph.client.isPhone;
            // Search Transparency
            baseSearchPage.transparencyLink = createSearchResultsLearnMoreNoticeLinkableText(objectGraph, metricsOptions);
            metricsHelpersPage.addMetricsEventsToPageWithInformation(objectGraph, baseSearchPage, metricsOptions.pageInformation);
            baseSearchPage.searchClearAction = createSearchCancelledOrClearedAction(objectGraph, "clear", metricsOptions.pageInformation, metricsOptions.locationTracker, termContext.term);
            baseSearchPage.searchCancelAction = createSearchCancelledOrClearedAction(objectGraph, "cancel", metricsOptions.pageInformation, metricsOptions.locationTracker, termContext.term);
            return baseSearchPage;
        });
        return page;
    });
}
// Creates context card model for search results from media API data.
export function createSearchResultsContextCard(queryContext, objectGraph) {
    return validation.context("createSearchResultsContextCard", () => {
        var _a, _b;
        if (isNothing(queryContext) || !["iOS", "macOS"].includes(objectGraph.host.platform)) {
            return undefined; // no server data or platform unsupported
        }
        const linkActions = (_a = queryContext.links) === null || _a === void 0 ? void 0 : _a.map(actionFromSearchResultsLink);
        const linkedSubstrings = linkActions === null || linkActions === void 0 ? void 0 : linkActions.reduce((map, linkAction) => {
            var _a;
            if ((_a = linkAction.title) === null || _a === void 0 ? void 0 : _a.length) {
                if (objectGraph.host.isMac) {
                    linkAction.title += "\u00a0\u2197";
                }
                map[linkAction.title] = linkAction;
            }
            return map;
        }, {});
        let rawText = queryContext.text;
        if ((linkActions === null || linkActions === void 0 ? void 0 : linkActions.length) === 1 && ((_b = linkActions[0].title) === null || _b === void 0 ? void 0 : _b.length)) {
            // Add single link to end of text, separated by a space.
            rawText += " " + linkActions[0].title;
        }
        else if (linkActions && linkActions.length > 1) {
            // Add multiple lines below end of text, separated by a new line.
            if (rawText.length > 0) {
                rawText += "\n";
            }
            rawText += linkActions
                .map((action) => action.title)
                .filter((title) => title === null || title === void 0 ? void 0 : title.length)
                .join("\n");
        }
        const styledText = new models.StyledText(rawText);
        const linkableText = new models.LinkableText(styledText, linkedSubstrings);
        const contextCard = new models.SearchResultsContextCard(linkableText);
        return contextCard;
    });
}
/// Creates and returns the first url action from the provided link data included in the search results response.
function actionFromSearchResultsLinks(linksData) {
    return validation.context("actionFromSearchResultsLinks", () => {
        const bestLinkData = linksData === null || linksData === void 0 ? void 0 : linksData.find((linkData) => linkData.url.length > 0);
        return bestLinkData ? actionFromSearchResultsLink(bestLinkData) : undefined;
    });
}
/// Creates and returns an action from the provided link data included in the search results response.
function actionFromSearchResultsLink(linkData) {
    return validation.context("actionFromSearchResultsLink", () => {
        var _a;
        const linkAction = new models.ExternalUrlAction(linkData.url, false);
        linkAction.title = (_a = linkData.label) === null || _a === void 0 ? void 0 : _a.replace(" ", "\u00a0"); // Use non-breaking spaces for link labels.
        linkAction.artwork = new models.Artwork("systemimage://arrow.up.forward", 0, 0, []);
        return linkAction;
    });
}
function installedStatesForAdvertsData(combinedSearchData) {
    var _a, _b;
    return (_b = (_a = combinedSearchData.sponsoredSearchAdvertData) === null || _a === void 0 ? void 0 : _a.installedStates) !== null && _b !== void 0 ? _b : {};
}
function appStatesForAdvertsData(combinedSearchData) {
    var _a, _b;
    return (_b = (_a = combinedSearchData.sponsoredSearchAdvertData) === null || _a === void 0 ? void 0 : _a.appStates) !== null && _b !== void 0 ? _b : {};
}
export async function searchResultsFromResponse(objectGraph, combinedSearchData) {
    return await baseSearchPageFromResponse(objectGraph, combinedSearchData);
}
export async function searchResultsPageFromResponse(objectGraph, combinedSearchData) {
    return await baseSearchPageFromResponse(objectGraph, combinedSearchData);
}
/**
 *  Creates a new empty `SegmentedSearchResultsPage` object.
 *  @return An empty segmented search results model object.
 */
export function emptySegmentedResultsPage(objectGraph) {
    return new models.SegmentedSearchResultsPage();
}
/**
 * Creates the segmented search results page form the response
 * @param objectGraph The app store object graph
 * @param combinedSearchData The combined data for the segmented search results response
 * @returns A promise of the segmented search results page
 */
export async function segmentedSearchResultsPageFromResponse(objectGraph, combinedSearchData) {
    return await validation.context("segmentedSearchResultsPageFromResponse", async () => {
        const response = combinedSearchData.catalogResponse;
        const requestMetadata = combinedSearchData.requestMetadata;
        if (isSome(response.results.search.groups)) {
            const fetchPages = response.results.search.groups.map(async (group) => {
                const segmentResponse = await baseSegmentFromGroupResponse(objectGraph, combinedSearchData, response, group, requestMetadata);
                return segmentResponse;
            });
            return await Promise.all(fetchPages).then((pageContexts) => {
                const page = new models.SegmentedSearchResultsPage();
                if (serverData.isNullOrEmpty(pageContexts)) {
                    return page;
                }
                const segments = pageContexts.map((context) => {
                    return context.segment;
                });
                page.segments = segments;
                const initialSegmentId = response.results.search.autoSelectedGroupId || segments[0].groupId;
                page.selectedSegmentId = initialSegmentId;
                addSegmentChangeActionsToPages(objectGraph, pageContexts, initialSegmentId);
                return page;
            });
        }
        else {
            return new models.SegmentedSearchResultsPage();
        }
    });
}
/**
 * Adds all segment change actions to each search result page segment
 * @param objectGraph The App Store object graph
 * @param contexts The segment contexts for the page
 * @param initialSegmentId The initial segment Id.
 */
function addSegmentChangeActionsToPages(objectGraph, contexts, initialSegmentId) {
    const firstNonEmptySegmentContext = contexts.find((context) => {
        const hasNonEmptyPage = context.segment.page.shelves.some((shelf) => {
            const isNotEmpty = shelf.items.length > 0;
            return isNotEmpty;
        });
        return hasNonEmptyPage;
    });
    const firstNonEmptySegmentId = firstNonEmptySegmentContext === null || firstNonEmptySegmentContext === void 0 ? void 0 : firstNonEmptySegmentContext.segment.groupId;
    const segments = contexts.map((context) => {
        return context.segment;
    });
    const nativeSegmentGroupId = segmentGroupIdForPlatform(objectGraph, segments);
    // Check if the native segment has content.
    const nativeSegmentIsNonEmpty = segments.some((segment) => {
        if (segment.groupId !== nativeSegmentGroupId) {
            return false;
        }
        return segment.page.shelves.some((shelf) => {
            return shelf.items.length > 0;
        });
    });
    contexts.forEach((context) => {
        context.segment.emptySegmentChangeAction = segmentChangeAction(objectGraph, SegmentChangeReason.EmptyResults, context.segment.page.id, context.metricsOptions, firstNonEmptySegmentId, context.segment.groupId);
        // Only add the non-native segment change action if the native segment has content, and
        // the initial segment wasn't the native segment.
        if (nativeSegmentIsNonEmpty && initialSegmentId !== nativeSegmentGroupId) {
            context.segment.nonNativeSegmentChangeAction = segmentChangeAction(objectGraph, SegmentChangeReason.NonNative, context.segment.page.id, context.metricsOptions, nativeSegmentGroupId, context.segment.groupId);
        }
        context.segment.segmentPickerSegmentChangeAction = segmentChangeAction(objectGraph, SegmentChangeReason.Picker, context.segment.page.id, context.metricsOptions, context.segment.groupId);
    });
}
/**
 *
 * @param objectGraph The app store object graph
 * @param combinedSearchData The combined search results which contains the response for segmented results
 * @param groupResponse The response for the segmented results
 * @param searchData The data for a single search results segment
 * @param requestMetadata The metadata from the search request
 * @returns A promise of a segmented search results page segment
 */
async function baseSegmentFromGroupResponse(objectGraph, combinedSearchData, groupResponse, searchData, requestMetadata) {
    return await validation.context("baseSearchPageFromGroupResponse", async () => {
        const searchRequestUrl = requestMetadata.searchRequestUrl;
        const segmentType = segmentTypeFromGroupId(searchData.groupId);
        // Term Context
        const termContext = searchCommon.createTermContextForSpellcheckedGroupedResponse(objectGraph, requestMetadata.requestDescriptor, groupResponse);
        // Metrics
        const metricsOptions = {
            locationTracker: metricsHelpersLocation.newLocationTracker(),
            pageInformation: metricsHelpersPage.pageInformationForSearchPage(objectGraph, requestMetadata.requestDescriptor, groupResponse, termContext, searchRequestUrl, getSearchResultsPageId(segmentType), null, false, null),
            createUniqueImpressionId: true,
        };
        const shelfMetricsOptions = {
            id: "search-results",
            kind: null,
            softwareType: null,
            targetType: "SearchResults",
            title: "Search Results",
            pageInformation: metricsOptions.pageInformation,
            locationTracker: metricsOptions.locationTracker,
            idType: "relationship",
        };
        // Root container
        const searchResultsPage = new models.SearchResultsPage();
        const searchSegment = new models.SegmentedSearchResultsPageSegment();
        const resultsShelf = new models.Shelf("searchResult");
        resultsShelf.isHorizontal = false;
        // We need to create and apply impressions fields to the shelf before creating the search results,
        // so that the shelf is sitting in the right location relative to its children from an impressions perspective.
        metricsHelpersImpressions.addImpressionFields(objectGraph, resultsShelf, shelfMetricsOptions);
        metricsHelpersLocation.pushContentLocation(objectGraph, shelfMetricsOptions, "Search Results");
        const resultsData = mediaDataStructure.dataCollectionFromDataContainer(searchData);
        const builderResults = await searchResultsPipeline.createSearchResults(objectGraph, requestMetadata, groupResponse.meta, metricsOptions, resultsData);
        const learnMoreNoticeLinkableText = createSearchResultsLearnMoreNoticeLinkableText(objectGraph, metricsOptions);
        const shelves = searchResultsShelvesFromBuilderResults(objectGraph, learnMoreNoticeLinkableText, builderResults, resultsShelf);
        searchResultsPage.shelves.push(...shelves);
        searchResultsPage.resultsParentImpressionMetrics = resultsShelf.impressionMetrics;
        if (builderResults.deferredSearchResults.length > 0) {
            searchResultsPage.nextPage = searchToken.createSearchToken(objectGraph, builderResults.deferredSearchResults, requestMetadata, groupResponse.meta, metricsOptions);
        }
        metricsHelpersLocation.popLocation(shelfMetricsOptions.locationTracker);
        /**
         * Spell Correction Message
         */
        searchResultsPage.message = searchSpellCorrection.spellCorrectionMessageFromTermContext(objectGraph, termContext, metricsOptions);
        // Enable autoplay search results on all clients except tv.
        searchResultsPage.isAutoPlayEnabled = objectGraph.client.deviceType !== "tv";
        searchResultsPage.isCondensedSearchLockupsEnabled = objectGraph.client.isPhone;
        // Search Transparency
        searchResultsPage.transparencyLink = learnMoreNoticeLinkableText;
        metricsHelpersPage.addMetricsEventsToPageWithInformation(objectGraph, searchResultsPage, metricsOptions.pageInformation);
        searchSegment.groupId = searchData.groupId;
        resultsShelf.id = getSearchResultsShelfId(searchSegment.groupId);
        searchSegment.page = searchResultsPage;
        searchSegment.title = segmentTitleForSegmentType(objectGraph, false, segmentType);
        return {
            segment: searchSegment,
            metricsOptions: metricsOptions,
        };
    });
}
/**
 * Get an array of shelves from the search builder results.
 * This is used to decide where to insert the search results learn more notice across
 * initial and paginated page builds.
 *
 * This is currently only used on visionOS search paths, but has the appropriate checks to ensure
 * it doesn't run on unsupported platforms, so could be used in all search code paths.
 * @param objectGraph The Object Graph.
 * @param learnMoreNoticeLinkableText The linkable text to display as part of the learn more notice.
 * @param builderResults The results from `createSearchResults`.
 * @param resultsShelf The initial shelf created to hold the results. This is passed in
 * because in many cases a reference to this is required later when building out the page.
 * @param startingContentOffsetWithinResults The starting content offset before the newly built
 * content is added. For an initial page build, this will be zero, and for paginated pages this
 * is stored on the token.
 * @returns An array of shelves for a search results page, with the learn more notice inserted
 * if required.
 */
function searchResultsShelvesFromBuilderResults(objectGraph, learnMoreNoticeLinkableText, builderResults, resultsShelf, startingContentOffsetWithinResults = 0) {
    let learnMoreNoticeIndex = searchResultsLearnMoreNoticeIndex(objectGraph);
    const builtResultsCount = builderResults.builtSearchResults.length;
    const hasDeferredResults = builderResults.deferredSearchResults.length > 0;
    // The total built results for the page so far. This is the initial page, plus any paginated content.
    const totalBuiltResults = startingContentOffsetWithinResults + builtResultsCount;
    // There are a number of conditions we don't want to insert the learn more notice:
    // 1. No index at which to insert it - this generally means the platform doesn't support this method of insertion.
    // 2. No linkable text object - the bag key wasn't present.
    // 3. The starting offset is greater than or equal to the insertion index - we can assume we've already shown the
    // notice in a previous page build.
    // 4. We have deferred results and the total built results count is less than the threshold - we'll insert the
    // notice in a subsequent fetch.
    // 5. We have no built results and no deferred results, ie. the page will be empty.
    // If we fail any of these conditions, we will just attach all the items into the single shelf and return that.
    if (learnMoreNoticeIndex === undefined ||
        learnMoreNoticeLinkableText === undefined ||
        startingContentOffsetWithinResults >= learnMoreNoticeIndex ||
        (hasDeferredResults && totalBuiltResults < learnMoreNoticeIndex) ||
        (totalBuiltResults === 0 && !hasDeferredResults)) {
        resultsShelf.items = builderResults.builtSearchResults;
        return [resultsShelf];
    }
    // Adjust the index by the starting offset, to ensure we take any items built in the initial fetch into account.
    learnMoreNoticeIndex = learnMoreNoticeIndex - startingContentOffsetWithinResults;
    // We have satisfied the insertion conditions for the learn more notice - we need to split the shelf in two to incorporate
    // the learn more notice as its own shelf in between. This is due to layout restrictions on visionOS where we cannot
    // accommodate a full width item in a grid of two items per row.
    resultsShelf.items = builderResults.builtSearchResults.slice(0, learnMoreNoticeIndex);
    const learnMoreNoticeShelf = new models.Shelf("searchResultsLearnMoreNotice");
    learnMoreNoticeShelf.isHorizontal = false;
    const searchResult = new SearchResultsLearnMoreNotice(learnMoreNoticeLinkableText);
    learnMoreNoticeShelf.items = [searchResult];
    const shelves = [resultsShelf, learnMoreNoticeShelf];
    const secondResultsShelfItems = builderResults.builtSearchResults.slice(learnMoreNoticeIndex);
    if (secondResultsShelfItems.length > 0) {
        const secondResultsShelf = new models.Shelf("searchResult");
        // Give the second results shelf the same impression metrics. This will result in each shelf contributing to the same impression metrics,
        // the only difference is we will see two "viewedInfo" entries for each distinct shelf within the top level impressions item.
        secondResultsShelf.impressionMetrics = resultsShelf.impressionMetrics;
        secondResultsShelf.isHorizontal = false;
        secondResultsShelf.items = secondResultsShelfItems;
        shelves.push(secondResultsShelf);
    }
    return shelves;
}
/**
 * Get the index for where the Search Results Learn More Notice should be inserted.
 * Returns undefined if either:
 * - the editorial ID is not available in the bag, or
 * - the platform does not support this style of presentation.
 * @param objectGraph The Object Graph
 */
function searchResultsLearnMoreNoticeIndex(objectGraph) {
    const editorialItemId = objectGraph.bag.searchResultsLearnMoreEditorialId;
    if (isNothing(editorialItemId) || (editorialItemId === null || editorialItemId === void 0 ? void 0 : editorialItemId.length) === 0) {
        return undefined;
    }
    if (objectGraph.client.isVision) {
        return 6;
    }
    return undefined;
}
export async function paginatedSearchResultsWithToken(objectGraph, token) {
    return await validation.context("paginatedSearchResultsWithToken", async () => {
        const nextItemsToFetch = searchToken.getNextItemsToFetch(objectGraph, token);
        const advancedToken = searchToken.advanceSearchTokenResults(objectGraph, token);
        if (nextItemsToFetch.length === 0) {
            return await Promise.resolve(emptyResults(objectGraph));
        }
        return await searchResultsFetching
            .fetchSearchResultItems(objectGraph, nextItemsToFetch)
            .then(async (dataContainer) => {
            const resultsDatum = mediaDataStructure.dataCollectionFromDataContainer(dataContainer);
            return await searchResultsPipeline
                .createSearchResults(objectGraph, token.requestMetadata, token.responseMetadata, token.metricsOptions, resultsDatum)
                .then((builderResults) => {
                const searchResults = new models.SearchResults();
                searchResults.results = builderResults.builtSearchResults;
                searchResults.nextPage = advancedToken;
                return searchResults;
            });
        });
    });
}
export async function paginatedSearchResultsPageWithToken(objectGraph, token) {
    return await validation.context("paginatedSearchResultsPageWithToken", async () => {
        const nextItemsToFetch = searchToken.getNextItemsToFetch(objectGraph, token);
        const advancedToken = searchToken.advanceSearchTokenResults(objectGraph, token);
        if (nextItemsToFetch.length === 0) {
            return await Promise.resolve(emptyResultsPage(objectGraph));
        }
        return await searchResultsFetching
            .fetchSearchResultItems(objectGraph, nextItemsToFetch)
            .then(async (dataContainer) => {
            const resultsDatum = mediaDataStructure.dataCollectionFromDataContainer(dataContainer);
            const shelfMetricsOptions = {
                id: "search-results",
                kind: null,
                softwareType: null,
                targetType: "SearchResults",
                title: "Search Results",
                pageInformation: token.metricsOptions.pageInformation,
                locationTracker: token.metricsOptions.locationTracker,
                idType: "relationship",
            };
            // Set up the "new" shelf
            const resultsShelf = new models.Shelf("searchResult");
            resultsShelf.id = getSearchResultsShelfId();
            resultsShelf.isHorizontal = false;
            // Shelf Impressions: Add impression fields.
            // This shelf, and the associated metrics data isn't really used - it's basically just a vehicle for the new items to be
            // merged into the old page/shelf. Even so, we create and apply impressions metrics correctly before setting the content
            // location and current position so it looks correct.
            metricsHelpersImpressions.addImpressionFields(objectGraph, resultsShelf, shelfMetricsOptions);
            // Set the content location to the "search-results" shelf.
            metricsHelpersLocation.pushContentLocation(objectGraph, shelfMetricsOptions, "Search Results");
            // Update position to content offset within the same search shelf prior to building the new results.
            metricsHelpersLocation.setCurrentPosition(shelfMetricsOptions.locationTracker, token.contentOffsetWithinResultsShelf);
            return await searchResultsPipeline
                .createSearchResults(objectGraph, token.requestMetadata, token.responseMetadata, token.metricsOptions, resultsDatum)
                .then((builderResults) => {
                const learnMoreNoticeLinkableText = createSearchResultsLearnMoreNoticeLinkableText(objectGraph, shelfMetricsOptions);
                const shelves = searchResultsShelvesFromBuilderResults(objectGraph, learnMoreNoticeLinkableText, builderResults, resultsShelf, token.contentOffsetWithinResultsShelf);
                const searchResultsPage = new models.SearchResultsPage(shelves);
                // Ensure we increment the offset for any future paginated results.
                if (serverData.isDefinedNonNull(advancedToken)) {
                    advancedToken.contentOffsetWithinResultsShelf = metricsHelpersLocation.currentPosition(shelfMetricsOptions.locationTracker);
                    searchResultsPage.nextPage = advancedToken;
                }
                searchResultsPage.isCondensedSearchLockupsEnabled = objectGraph.client.isPhone;
                searchResultsPage.resultsParentImpressionMetrics = resultsShelf.impressionMetrics;
                metricsHelpersLocation.popLocation(token.metricsOptions.locationTracker);
                searchResultsPage.searchClearAction = createSearchCancelledOrClearedAction(objectGraph, "clear", token.metricsOptions.pageInformation, token.metricsOptions.locationTracker, token.requestMetadata.requestDescriptor.term);
                searchResultsPage.searchCancelAction = createSearchCancelledOrClearedAction(objectGraph, "cancel", token.metricsOptions.pageInformation, token.metricsOptions.locationTracker, token.requestMetadata.requestDescriptor.term);
                return searchResultsPage;
            });
        });
    });
}
// region Extracting Data
/**
 * Returns the array of data objects to build search results with.
 */
function dataForSearchResultsFromCombinedData(objectGraph, combinedSearchData) {
    return mediaDataStructure.dataCollectionFromDataContainer(combinedSearchData.catalogResponse.results.search);
}
/**
 * Returns the array of data objects to build search adverts with.
 */
function dataForAdvertsFromCombinedData(objectGraph, combinedSearchData) {
    const rawAdvertsData = mediaDataStructure.dataCollectionFromDataContainer(combinedSearchData.catalogResponse.results["ads-result"]);
    return searchAdsODML.applyNativeAdvertData(objectGraph, rawAdvertsData, combinedSearchData.sponsoredSearchAdvertData);
}
// endregion
// region Guided Search
/**
 * Add models for Guided Search into `SearchResults` model, specifically:
 * - `GuidedSearchToken`s
 * - `GuidedSearchQuery`s
 * - Metrics Container for tokens.
 */
function addModelsForGuidedSearch(objectGraph, searchResultsPage, requestMetadata, facetData, metricsOptions) {
    if (!objectGraph.host.isiOS) {
        return;
    }
    const request = requestMetadata.requestDescriptor;
    metricsHelpersLocation.pushBasicLocation(objectGraph, {
        pageInformation: metricsOptions.pageInformation,
        locationTracker: metricsOptions.locationTracker,
        targetType: "SearchRevisions",
    }, "");
    const tokens = [];
    // Tokens from facet data, if any
    if (serverData.isDefinedNonNullNonEmpty(facetData)) {
        for (const data of facetData) {
            const token = guidedSearch.createGuidedSearchToken(objectGraph, "toggle", request, data, metricsOptions);
            if (token) {
                tokens.push(token);
                metricsHelpersLocation.nextPosition(metricsOptions.locationTracker);
            }
        }
    }
    // Token from selected entity hint, iff there aren't tokens already
    if (serverData.isNullOrEmpty(tokens) && requestMetadata.requestDescriptor.searchEntity) {
        const entityClearingToken = guidedSearch.createGuidedSearchTokenClearingEntityFilter(objectGraph, requestMetadata.requestDescriptor, metricsOptions);
        tokens.push(entityClearingToken);
        metricsHelpersLocation.nextPosition(metricsOptions.locationTracker);
    }
    const queries = guidedSearch.createGuidedSearchQueries(objectGraph, requestMetadata.requestDescriptor, facetData);
    metricsHelpersLocation.popLocation(metricsOptions.locationTracker);
    if (serverData.isDefinedNonNullNonEmpty(tokens)) {
        searchResultsPage.guidedSearchTokens = tokens;
        searchResultsPage.guidedSearchQueries = queries;
        addGuidedSearchParentImpressionMetrics(objectGraph, searchResultsPage, metricsOptions);
        metricsHelpersLocation.nextPosition(metricsOptions.locationTracker); // increment **after** assigning guided token parent impression.
    }
}
/// The contextual reason for the segment change
var SegmentChangeReason;
(function (SegmentChangeReason) {
    SegmentChangeReason[SegmentChangeReason["EmptyResults"] = 0] = "EmptyResults";
    SegmentChangeReason[SegmentChangeReason["Picker"] = 1] = "Picker";
    SegmentChangeReason[SegmentChangeReason["NonNative"] = 2] = "NonNative";
})(SegmentChangeReason || (SegmentChangeReason = {}));
/**
 * Creates a segment change action for moving from one segment to another for a specific reason
 * @param objectGraph The App Store object graph
 * @param reason The reason why the segment is changing
 * @param pageId The search results page id
 * @param metricsOptions The metrics options for the segment this action will attach to
 * @param switchingToGroupId The groupId of the segment we are switching to
 * @param switchingFromGroupId The groupId of the segment we are switching from (the current segment)
 * @returns A segment change action for the specific context and locations
 */
function segmentChangeAction(objectGraph, reason, pageId, metricsOptions, switchingToGroupId, switchingFromGroupId) {
    const includeAppsSuffix = reason !== SegmentChangeReason.Picker;
    const switchingToSegmentTitle = segmentTitleForSegmentType(objectGraph, includeAppsSuffix, segmentTypeFromGroupId(switchingToGroupId));
    let action;
    switch (reason) {
        case SegmentChangeReason.EmptyResults:
            if (isNothing(switchingToSegmentTitle) || switchingFromGroupId === switchingToGroupId) {
                return undefined;
            }
            metricsHelpersLocation.pushBasicLocation(objectGraph, {
                pageInformation: metricsOptions.pageInformation,
                locationTracker: metricsOptions.locationTracker,
                targetType: "SearchResults",
            }, "emptyResultsSegmentSwitch");
            const emptyResultsTitle = objectGraph.loc
                .string("SEARCH_RESULTS_SWITCH_TO_OTHER_RESULTS")
                .replace("{platformApps}", switchingToSegmentTitle);
            action = new models.SearchPageSegmentChangeAction(switchingToGroupId, switchingToSegmentTitle, new models.StyledText(emptyResultsTitle));
            metricsHelpersClicks.addClickEventToSearchPageSegmentChangeAction(objectGraph, action, "button", metricsOptions.locationTracker);
            metricsHelpersLocation.popLocation(metricsOptions.locationTracker);
            break;
        case SegmentChangeReason.Picker:
            if (isNothing(switchingToSegmentTitle)) {
                return undefined;
            }
            metricsHelpersLocation.pushBasicLocation(objectGraph, {
                pageInformation: metricsOptions.pageInformation,
                locationTracker: metricsOptions.locationTracker,
                targetType: "SearchResults",
            }, "searchResultsSegmentSwitch");
            action = new models.SearchPageSegmentChangeAction(switchingToGroupId, switchingToSegmentTitle);
            metricsHelpersClicks.addClickEventToSearchPageSegmentChangeAction(objectGraph, action, "button", metricsOptions.locationTracker);
            metricsHelpersLocation.popLocation(metricsOptions.locationTracker);
            break;
        case SegmentChangeReason.NonNative:
            const currentSegmentType = segmentTypeFromGroupId(switchingFromGroupId);
            if (currentSegmentType === segmentTypeForPlatform(objectGraph)) {
                return undefined;
            }
            const switchingFromSegmentTitle = segmentTitleForSegmentType(objectGraph, true, segmentTypeFromGroupId(switchingFromGroupId));
            const nonNativeTitle = objectGraph.loc
                .string("Search.Results.ShowingNonNativeResults")
                .replace("@@current_platform_apps@@", switchingFromSegmentTitle)
                .replace("@@native_platform_apps@@", switchingToSegmentTitle);
            metricsHelpersLocation.pushBasicLocation(objectGraph, {
                pageInformation: metricsOptions.pageInformation,
                locationTracker: metricsOptions.locationTracker,
                targetType: "SearchResults",
            }, "nonNativeResultsSegmentSwitch");
            action = new models.SearchPageSegmentChangeAction(switchingToGroupId, switchingToSegmentTitle, new models.StyledText(nonNativeTitle, "text/x-apple-as3-nqml"));
            metricsHelpersLocation.popLocation(metricsOptions.locationTracker);
            break;
        default:
            break;
    }
    const clickActionOptions = {
        actionType: "navigate",
        id: pageId,
        targetType: "button",
        pageInformation: metricsOptions.pageInformation,
        locationTracker: metricsOptions.locationTracker,
    };
    metricsHelpersClicks.addClickEventToAction(objectGraph, action, clickActionOptions);
    return action;
}
/**
 * Gets the segment type for the given group id
 * @param groupId The group id of the segment
 * @returns The segment type for the group id
 */
function segmentTypeFromGroupId(groupId) {
    if (isSome(groupId)) {
        switch (groupId) {
            case "iOS":
            case "ios":
                return models.SegmentedSearchResultsPageSegmentType.iOS;
            case "xrOS":
            case "xros":
                return models.SegmentedSearchResultsPageSegmentType.visionOS;
            default:
                return undefined;
        }
    }
    return undefined;
}
/**
 * Gets the segment type for the current platform
 * @param objectGraph The App Store object graph
 * @returns The segment type for the current platform
 */
function segmentTypeForPlatform(objectGraph) {
    switch (objectGraph.client.deviceType) {
        case "vision":
            return models.SegmentedSearchResultsPageSegmentType.visionOS;
        case "pad":
        case "phone":
            return models.SegmentedSearchResultsPageSegmentType.iOS;
        default:
            return undefined;
    }
}
/**
 * Gets the segment title for the given segment type
 * @param objectGraph The App Store object graph
 * @param includeAppsSuffix Whether to include the word "Apps" at the end of the title
 * @param segmentType The segment type we want the title of
 * @returns The segment title for the segment type
 */
function segmentTitleForSegmentType(objectGraph, includeAppsSuffix, segmentType) {
    switch (segmentType) {
        case models.SegmentedSearchResultsPageSegmentType.visionOS:
            return includeAppsSuffix
                ? objectGraph.loc.string("SEARCH_RESULTS_VISION_APPS_TITLE")
                : objectGraph.loc.string("SEARCH_RESULTS_VISION_TITLE");
        case models.SegmentedSearchResultsPageSegmentType.iOS:
            return includeAppsSuffix
                ? objectGraph.loc.string("SEARCH_RESULTS_IPHONE_IPAD_APPS_TITLE")
                : objectGraph.loc.string("SEARCH_RESULTS_IPHONE_IPAD_TITLE");
        default:
            return undefined;
    }
}
/**
 * Gets the group id for the current platform
 * @param objectGraph The App Store object graph
 * @param segments The segments on the search results page
 * @returns The group id for the current platform
 */
function segmentGroupIdForPlatform(objectGraph, segments) {
    const nativeSegmentType = segmentTypeForPlatform(objectGraph);
    const nativeSegment = segments.find((segment) => {
        return segmentTypeFromGroupId(segment.groupId) === nativeSegmentType;
    });
    return nativeSegment === null || nativeSegment === void 0 ? void 0 : nativeSegment.groupId;
}
/**
 *
 * @param objectGraph The App Store Object Graph
 * @param searchClearActionType The way the user cleared the search (x in search field or cancel button in toolbar)
 * @param pageInformation The metrics page information
 * @param locationTracker The metrics location tracker
 * @param searchTerm The current search term
 * @returns An action to trigger when the search is cancelled or cleared
 */
export function createSearchCancelledOrClearedAction(objectGraph, searchClearActionType, pageInformation, locationTracker, searchTerm) {
    const action = new models.BlankAction();
    let type;
    let targetId;
    switch (searchClearActionType) {
        case "cancel":
            type = "dismiss";
            targetId = "cancel";
            break;
        case "clear":
            type = "delete";
            targetId = "clear";
            break;
        default:
            break;
    }
    metricsHelpersClicks.addClickEventToSearchCancelOrDismissAction(objectGraph, action, {
        targetType: "button",
        id: targetId,
        idType: undefined,
        actionType: type,
        pageInformation: pageInformation,
        locationTracker: locationTracker,
    }, "button", searchTerm);
    return action;
}
//# sourceMappingURL=search.js.map