/**
 * Data Fetching for Search Results for:
 * - Initial search requests
 * - Content pagination requests
 */
import { isNothing } from "@jet/environment/types/optional";
import * as models from "../../api/models";
import { asBooleanOrFalse, asString, isDefinedNonNull, isDefinedNonNullNonEmpty, isNullOrEmpty, } from "../../foundation/json-parsing/server-data";
import { defaultAdditionalPlatformsForClient, Request } from "../../foundation/media/data-fetching";
import { metricsFromMediaApiObject, } from "../../foundation/media/data-structure";
import { fetchData } from "../../foundation/media/network";
import { buildURLFromRequest } from "../../foundation/media/url-builder";
import * as constants from "../../foundation/util/constants";
import * as client from "../../foundation/wrappers/client";
import * as appPromotionsCommon from "../app-promotions/app-promotions-common";
import * as categories from "../categories";
import { addVariantParametersToRequestForItems, shouldFetchCustomAttributes, } from "../product-page/product-page-variants";
import { setPreviewPlatform } from "../preview-platform";
import { SponsoredSearchRequestData } from "./search-ads";
import { searchMetricsDataSetID } from "./search-common";
import { fetchSponsoredSearchNativeAdvertData } from "./sponsored-search-fetching";
import { dateFlooredToHour } from "../../foundation/util/date-util";
import { shouldUsePrerenderedIconArtwork } from "../content/content";
import { AppEventsAttributes } from "../../gameservicesui/src/foundation/media-api/requests/recommendation-request-types";
export async function fetchSegmentedSearchResults(objectGraph, options) {
    var _a;
    // Primary search request
    const searchRequest = (_a = createSearchRequest(objectGraph, options)) === null || _a === void 0 ? void 0 : _a.addingQuery("groupBy[search]", "platform");
    if (isNothing(searchRequest)) {
        return null;
    }
    const fetchSearchResults = fetchData(objectGraph, searchRequest, undefined);
    return await fetchSearchResults.then((catalogResponse) => {
        renameDataSetIdKey(catalogResponse);
        const data = {
            catalogResponse: catalogResponse,
            requestMetadata: {
                requestDescriptor: options,
                searchRequestUrl: buildURLFromRequest(objectGraph, searchRequest).toString(), // Searches are attributed to request url
            },
            categoriesFilterData: null,
            sponsoredSearchRequestData: null,
            sponsoredSearchAdvertData: null,
        };
        return data;
    });
}
/**
 * Fetch a collection of data for displaying a single, sequential set of search results.
 * @param options Parameters for the search being performed.
 * @returns `Promise` for aggregate search result data, or `null` if `options` was invalid.
 */
export async function fetchSequentialSearchResultsData(objectGraph, options) {
    // Advert targeting data from client
    const sponsoredSearchRequestData = new SponsoredSearchRequestData(options.targetingData, objectGraph.random.nextUUID());
    // Primary search request
    const searchRequest = createSearchRequest(objectGraph, options);
    if (searchRequest === null) {
        return null;
    }
    const fetchSearchResults = fetchData(objectGraph, searchRequest, createSearchFetchOptions(objectGraph, sponsoredSearchRequestData));
    // Search History
    if (objectGraph.bag.mediaAPISearchFocusEnabled) {
        const historyItem = {
            term: options.term.trim(),
            entity: options.searchEntity,
        };
        // ** MAINTAINER'S NOTE **
        // Fire and forget this request to reduce delay showing search results on persisting recent searches that are not visible at this time.
        objectGraph.onDeviceSearchHistoryManager.saveRecentSearchWithLimit(historyItem, 30);
    }
    // Optional requests
    const fetchSponsoredSearchAds = fetchSponsoredSearchNativeAdvertData(objectGraph, sponsoredSearchRequestData, options.term, fetchSearchResults);
    const fetchCategoriesOrNull = fetchCategoryFiltersDataIfNeeded(objectGraph);
    return await Promise.all([fetchSearchResults, fetchSponsoredSearchAds, fetchCategoriesOrNull]).then(([catalogResponse, sponsoredSearchAdvertData, categoriesFilterData]) => {
        var _a, _b, _c, _d;
        renameDataSetIdKey(catalogResponse);
        // Remember the last time a natural language search was performed.
        if ((_c = (_b = (_a = catalogResponse.meta) === null || _a === void 0 ? void 0 : _a.results) === null || _b === void 0 ? void 0 : _b.search) === null || _c === void 0 ? void 0 : _c.naturalLanguage) {
            const previousNLSQueryDate = objectGraph.storage.retrieveString("lastNLSQueryDate");
            const lastNLSQueryDate = dateFlooredToHour(new Date()).getTime().toString();
            objectGraph.storage.storeString("lastNLSQueryDate", lastNLSQueryDate);
            // Notify ODJ on change using AMSEngagement.
            (_d = objectGraph.amsEngagement) === null || _d === void 0 ? void 0 : _d.enqueueData({
                eventType: "lastNLSQueryDateChange",
                app: "com.apple.AppStore",
                oldState: previousNLSQueryDate,
                newState: lastNLSQueryDate,
            });
        }
        const data = {
            catalogResponse: catalogResponse,
            categoriesFilterData: categoriesFilterData,
            sponsoredSearchRequestData: sponsoredSearchRequestData,
            sponsoredSearchAdvertData: sponsoredSearchAdvertData,
            requestMetadata: {
                requestDescriptor: options,
                searchRequestUrl: buildURLFromRequest(objectGraph, searchRequest).toString(), // Searches are attributed to request url
            },
        };
        return data;
    });
}
/**
 * Fetch a collection of data for displaying several, grouped set of search results.
 * @param options Parameters for the search being performed.
 * @returns `Promise` for aggregate search result data, or `Promise` of `null` if `options` was invalid.
 */
export async function fetchPlatformGroupedSearchResultsData(objectGraph, options) {
    // Primary search request
    const searchRequest = createPlatformGroupedSearchRequest(objectGraph, options);
    if (isNothing(searchRequest)) {
        return null;
    }
    const fetchSearchResults = fetchData(objectGraph, searchRequest, createSearchFetchOptions(objectGraph));
    // Optional requests
    const fetchCategoriesOrNull = fetchCategoryFiltersDataIfNeeded(objectGraph);
    return await Promise.all([fetchSearchResults, fetchCategoriesOrNull]).then(([catalogResponse, categoriesFilterData]) => {
        renameDataSetIdKey(catalogResponse);
        const data = {
            catalogResponse: catalogResponse,
            categoriesFilterData: categoriesFilterData,
            sponsoredSearchRequestData: null,
            sponsoredSearchAdvertData: null,
            requestMetadata: {
                requestDescriptor: options,
                searchRequestUrl: buildURLFromRequest(objectGraph, searchRequest).toString(), // Searches are attributed to request url
            },
        };
        return data;
    });
}
/**
 * Fetch a set of items for appearing in search results
 * @param items Items to fetch.
 */
export async function fetchSearchResultItems(objectGraph, items) {
    const request = createCatalogRequestForItems(objectGraph, items);
    return await fetchData(objectGraph, request);
}
// endregion
// region Request Header
/**
 * Create fetch options, annotating with `SponsoredSearchRequestData` header fields.
 * @param adData Advert data to include in header.
 */
function createSearchFetchOptions(objectGraph, sponsoredSearchRequestData) {
    const searchRequestHeader = {};
    if (sponsoredSearchRequestData && sponsoredSearchRequestData.validAdRequest()) {
        searchRequestHeader[constants.appStoreClientRequestIdName] = sponsoredSearchRequestData.appStoreClientRequestId;
        searchRequestHeader[constants.iAdRequestDataHeaderName] = sponsoredSearchRequestData.sponsoredSearchRequestData;
        searchRequestHeader[constants.iAdRoutingInfoHeaderName] = sponsoredSearchRequestData.routingInfo;
    }
    return {
        headers: searchRequestHeader,
    };
}
// endregion
// region Search Results Request
/**
 * Set of relationships fetched as relation for search request / catalog requests
 */
const searchIncludeRelationships = ["apps", "top-apps"];
/**
 * Create the `Request` object to execute a search described by `options`
 * @param options Options for search being executed.
 * @returns `Request` configured for `options`, or `null` if `options` was invalid.
 */
export function createSearchRequest(objectGraph, options) {
    var _a;
    const term = (_a = options.term) === null || _a === void 0 ? void 0 : _a.trim();
    if (isNullOrEmpty(term)) {
        return null;
    }
    const origin = options.origin;
    const source = options.source;
    const searchEntityOrNull = options.searchEntity;
    const facetsOrNull = options.facets;
    const selectedFacetOptionsOrNull = options.selectedFacetOptions;
    const spellCheckEnabled = options.spellCheckEnabled;
    const excludedTermsOrNull = options.excludedTerms;
    const clientIdentifier = objectGraph.host.clientIdentifier;
    const searchRequest = new Request(objectGraph)
        .withSearchTerm(term)
        .includingAdditionalPlatforms(defaultAdditionalPlatformsForClient(objectGraph))
        .includingAttributes(includeAttributesForSearchResults(objectGraph))
        .includingScopedAttributes("editorial-items", ["showLabelInSearch"])
        .includingRelationshipsForUpsell(true)
        .includingMacOSCompatibleIOSAppsWhenSupported()
        .usingCustomAttributes(shouldFetchCustomAttributes(objectGraph));
    setPreviewPlatform(objectGraph, searchRequest);
    if (!objectGraph.client.isWatch && !objectGraph.client.isVision) {
        searchRequest.includingRelationships(searchIncludeRelationships);
    }
    if (objectGraph.client.isVision) {
        // Temporarily increase the sparseLimit on Vision to workaround lack of filtering for bincompat.
        searchRequest.addingQuery("sparseLimit[developers:top-apps]", "12");
    }
    if (appPromotionsCommon.appEventsAreEnabled(objectGraph)) {
        searchRequest.includingAssociateKeys("apps", ["app-events"]);
        searchRequest.includingScopedAttributes("app-events", AppEventsAttributes);
        searchRequest.includingScopedRelationships("editorial-items", ["primary-content"]);
    }
    /**
     * Tinker Filter
     */
    if (asBooleanOrFalse(objectGraph.client.isTinkerWatch)) {
        searchRequest.withFilter("contexts", "tinker");
    }
    /**
     * Guided Search
     */
    if (objectGraph.host.isiOS) {
        // Feature enablers
        searchRequest.enablingFeature("guidedSearch");
        searchRequest.enablingFeature("midScrollGuidedSearch");
        // Selected facets, if any.
        if (isDefinedNonNullNonEmpty(options.guidedSearchTokens)) {
            searchRequest.addingQuery("selectedFacets", options.guidedSearchTokens.join(","));
        }
        // Optimization term, if any were on request.
        if (isDefinedNonNullNonEmpty(options.guidedSearchOptimizationTerm)) {
            searchRequest.addingQuery("finalTerm", options.guidedSearchOptimizationTerm);
        }
        if (objectGraph.bag.isLLMSearchTagsEnabled) {
            searchRequest.includingAssociateKeys("results:apps", ["tags"]);
        }
    }
    if (objectGraph.featureFlags.isEnabled("voyager_bundles_2025A")) {
        searchRequest.includingScopedAttributes("apps", ["screenshotsByType"]);
    }
    /**
     * Entities in results
     */
    if (searchEntityOrNull === "story") {
        searchRequest.searchingOverTypes(["editorial-items"]);
    }
    else if (searchEntityOrNull === "developer") {
        searchRequest.searchingOverTypes(["developers"]);
    }
    else if (searchEntityOrNull === "watch" || searchEntityOrNull === "arcade") {
        searchRequest.searchingOverTypes(["apps"]).withFilter("contexts", searchEntityOrNull);
    }
    else if (objectGraph.client.isTV) {
        searchRequest.searchingOverTypes(["apps", "developers", "groupings", "editorial-items"]);
    }
    else if (objectGraph.client.isVision) {
        searchRequest.searchingOverTypes([
            "apps",
            "developers",
            "editorial-items",
            "app-bundles",
            "in-apps",
            "editorial-pages",
        ]);
    }
    else {
        searchRequest.searchingOverTypes([
            "apps",
            "developers",
            "groupings",
            "editorial-items",
            "app-bundles",
            "in-apps",
        ]);
    }
    /**
     * Signal Rosetta unavailability.
     */
    if (objectGraph.appleSilicon.isSupportEnabled && !objectGraph.appleSilicon.isRosettaAvailable) {
        searchRequest.addingQuery("restrict", "!requiresRosetta");
    }
    /**
     * Facets / filters
     */
    if (facetsOrNull) {
        for (const key of Object.keys(facetsOrNull)) {
            searchRequest.addingQuery(key, facetsOrNull[key]);
        }
    }
    if (selectedFacetOptionsOrNull) {
        for (const key of Object.keys(selectedFacetOptionsOrNull)) {
            const requestValues = models.PageFacets.requestValuesForSelectedFacetOptions(selectedFacetOptionsOrNull[key]);
            if (isDefinedNonNullNonEmpty(requestValues)) {
                searchRequest.addingQuery(key, requestValues.value);
                for (const additionalKey of Object.keys(requestValues.additionalKeyValuePairs)) {
                    searchRequest.addingQuery(additionalKey, requestValues.additionalKeyValuePairs[additionalKey]);
                }
            }
        }
    }
    /**
     * natural language search availability
     */
    const isNaturalLanguageSearchResultsEnabled = objectGraph.bag.isNaturalLanguageSearchEnabled || objectGraph.bag.isNaturalLanguageSearchResultsEnabled;
    /**
     * source attribution
     */
    const sourceKey = isNaturalLanguageSearchResultsEnabled ? "source" : "src";
    if (origin === "hints") {
        const hintSource = isNaturalLanguageSearchResultsEnabled && (source === null || source === void 0 ? void 0 : source.length) ? "hint:".concat(source) : "hint";
        searchRequest.addingQuery(sourceKey, hintSource);
    }
    else if (origin === "recents") {
        searchRequest.addingQuery(sourceKey, "recent");
    }
    else if (origin === "trending") {
        searchRequest.addingQuery(sourceKey, "trending");
    }
    else if (origin === "undoSpellCorrection") {
        searchRequest.addingQuery(sourceKey, "searchInstead");
    }
    else if (origin === "applySpellCorrection") {
        searchRequest.addingQuery(sourceKey, "didYouMean");
    }
    else if (origin === "guidedToken") {
        searchRequest.addingQuery(sourceKey, "facet");
    }
    /**
     * contexts
     */
    switch (clientIdentifier) {
        case client.watchIdentifier:
            searchRequest.addingContext("watch");
            break;
        case client.messagesIdentifier:
            searchRequest.addingContext("messages");
            break;
        case client.arcadeIdentifier:
            searchRequest.addingContext("arcade");
            break;
        default:
            break;
    }
    /**
     * Advert limit
     */
    searchRequest.addingQuery("limit[ads-result]", objectGraph.bag.mediaAdvertRequestLimit.toString());
    /**
     * Ads locale metadata.
     */
    if (isDefinedNonNullNonEmpty(objectGraph.bag.adsOverrideLanguage)) {
        searchRequest.enablingFeature("adsLocaleMetadata");
    }
    /**
     * Feature: Spellcheck
     */
    if (spellCheckEnabled) {
        searchRequest.enablingFeature("spellCheck");
    }
    /**
     * Feature: Natural Language
     */
    if (isNaturalLanguageSearchResultsEnabled) {
        searchRequest.enablingFeature("naturalLanguage");
    }
    /**
     * Feature: Organic CPPs
     */
    if (objectGraph.host.isiOS) {
        searchRequest.enablingFeature("searchResultCpps");
    }
    /**
     * Excluded terms
     */
    if (excludedTermsOrNull) {
        searchRequest.addingQuery("excludeTerms", excludedTermsOrNull.join(","));
    }
    return searchRequest;
}
/**
 * Create the `Request` object to execute a search described by `options`, with results grouped by platform
 * @param options Options for search being executed.
 * @returns `Request` configured for `options`, or `null` if `options` was invalid.
 */
export function createPlatformGroupedSearchRequest(objectGraph, options) {
    var _a;
    return (_a = createSearchRequest(objectGraph, options)) === null || _a === void 0 ? void 0 : _a.addingQuery("groupBy[search]", "platform").includingMacOSCompatibleIOSAppsWhenSupported();
}
/**
 * Create a request for fetching set of `items` for appearing in search tab. Used for pagination.
 * @param items Items to fetch.
 */
function createCatalogRequestForItems(objectGraph, items) {
    const shouldUseMixedCatalogRequest = objectGraph.bag.isLLMSearchTagsEnabled ||
        asBooleanOrFalse(objectGraph.bag.supportedMixedMediaRequestUsecases["search"]);
    const searchRequest = new Request(objectGraph, items, shouldUseMixedCatalogRequest, ["tags"])
        .includingAdditionalPlatforms(defaultAdditionalPlatformsForClient(objectGraph))
        .includingScopedAttributes("editorial-items", ["showLabelInSearch"])
        .includingAttributes(includeAttributesForSearchResults(objectGraph))
        .includingRelationshipsForUpsell(true)
        .includingMacOSCompatibleIOSAppsWhenSupported()
        .usingCustomAttributes(shouldFetchCustomAttributes(objectGraph));
    addVariantParametersToRequestForItems(objectGraph, searchRequest, items);
    if (!objectGraph.client.isWatch) {
        searchRequest.includingRelationships(searchIncludeRelationships);
    }
    if (appPromotionsCommon.appEventsAreEnabled(objectGraph)) {
        searchRequest.includingAssociateKeys("apps", ["app-events"]);
        searchRequest.includingScopedAttributes("app-events", AppEventsAttributes);
        searchRequest.includingScopedRelationships("editorial-items", ["primary-content"]);
    }
    return searchRequest;
}
/**
 * Returns the include attributes used for:
 * 1. Initial search request
 * 2. Subsequent catalog request for pagination
 */
function includeAttributesForSearchResults(objectGraph) {
    const attributes = [
        "screenshotsByType",
        "messagesScreenshots",
        "videoPreviewsByType",
        "requiredCapabilities",
        "editorialBadgeInfo",
        "supportsFunCamera",
        "minimumOSVersion",
        "customScreenshotsByTypeForAd",
        "customVideoPreviewsByTypeForAd",
        "secondaryGenreShortDisplayNames",
        "genreShortDisplayName",
        "editorialVideo",
    ];
    if (objectGraph.appleSilicon.isSupportEnabled) {
        attributes.push("macRequiredCapabilities");
    }
    if (objectGraph.client.isMac) {
        attributes.push("hasMacIPAPackage");
    }
    if (objectGraph.client.isVision) {
        attributes.push("compatibilityControllerRequirement");
    }
    if (objectGraph.bag.enableUpdatedAgeRatings) {
        attributes.push("ageRating");
    }
    if (shouldUsePrerenderedIconArtwork(objectGraph)) {
        attributes.push("iconArtwork");
    }
    /// We don't want to unnecessarily ask for clients that don't have metadata ribbon capability
    if (objectGraph.host.isOSAtLeast(15, 5, 0)) {
        attributes.push("remoteControllerRequirement");
    }
    // Keeping custom creatives out of prod.
    if (preprocessor.CARRY_BUILD || preprocessor.DEBUG_BUILD) {
        if (objectGraph.featureFlags.isEnabled("aligned_region_artwork_2025A")) {
            attributes.push("adCreativeArtwork");
            attributes.push("adCreativeVideo");
        }
    }
    return attributes;
}
// endregion
// region Categories Filter Request
/**
 * Fetches data to for category filters on devices that need them.
 */
async function fetchCategoryFiltersDataIfNeeded(objectGraph) {
    const deviceType = objectGraph.client.deviceType;
    if (deviceTypeSupportsCategoryFilters(deviceType)) {
        const categoriesRequest = categories.createRequest(objectGraph, null, null, defaultAdditionalPlatformsForClient(objectGraph));
        if (categoriesRequest) {
            // Failable request.
            return await fetchData(objectGraph, categoriesRequest).catch(() => null);
        }
    }
    return null;
}
function deviceTypeSupportsCategoryFilters(deviceType) {
    switch (deviceType) {
        case "pad":
        case "mac":
            return true;
        default:
            return false;
    }
}
// endregion
// region Search Result Modification
/**
 * The search dataset id is set to the key `dataSetId` on `meta.metrics`, but needs to have `data.search.dataSetId` key.
 * <rdar://problem/39920993> Metrics: metrics blob in search response has incorrect field name
 */
function renameDataSetIdKey(searchResponse) {
    var _a;
    const searchMetrics = metricsFromMediaApiObject(searchResponse);
    const oldDataSetId = "dataSetId";
    if (isDefinedNonNull(searchMetrics) &&
        isDefinedNonNull(searchResponse.meta) &&
        isDefinedNonNull((_a = searchResponse.meta) === null || _a === void 0 ? void 0 : _a.metrics)) {
        searchResponse.meta.metrics[searchMetricsDataSetID] = asString(searchMetrics, oldDataSetId);
        delete searchResponse.meta.metrics[oldDataSetId];
    }
}
// endregion
//# sourceMappingURL=search-results-fetching.js.map