import * as validation from "@jet/environment/json/validation";
import { FetchTimingMetricsBuilder } from "@jet/environment/metrics/fetch-timing-metrics-builder";
import { isNothing, isSome } from "@jet/environment/types/optional";
import { PageRefreshPolicy, SearchAction, SearchFocusPage, SearchLandingPage, Shelf } from "../../api/models";
import * as mediaRequestUtils from "../../common/builders/url-mapping-utils";
import * as appStoreExperiments from "../../foundation/experimentation/app-store-experiments";
import { ExperimentAreaId } from "../../foundation/experimentation/experiment-area-id";
import * as serverData from "../../foundation/json-parsing/server-data";
import * as mediaAttributes from "../../foundation/media/attributes";
import * as mediaDataFetching from "../../foundation/media/data-fetching";
import * as mediaNetwork from "../../foundation/media/network";
import * as mediaRelationship from "../../foundation/media/relationships";
import { Parameters, Path, Protocol } from "../../foundation/network/url-constants";
import * as impressionDemotion from "../../common/personalization/on-device-impression-demotion";
import { iadInfoFromOnDeviceAdResponse, isAdPlacementEnabled } from "../ads/ad-common";
import * as adIncidents from "../ads/ad-incident-recorder";
import { fetchAds as landingAdFetchFetchAds } from "../ads/on-device-ad-fetch";
import * as landingAdStitch from "../ads/on-device-ad-stitch";
import * as contentArtwork from "../content/artwork/artwork";
import * as groupingCommon from "../grouping/grouping-common";
import * as metricsHelpersClicks from "../metrics/helpers/clicks";
import * as metricsHelpersLocation from "../metrics/helpers/location";
import * as metricsHelpersPage from "../metrics/helpers/page";
import * as metricsHelpersUtil from "../metrics/helpers/util";
import * as onDevicePersonalization from "../personalization/on-device-personalization";
import * as productPageVariants from "../product-page/product-page-variants";
import { areAppTagsEnabled } from "../util/app-tags-util";
import { isFeatureEnabledForCurrentUser } from "../util/lottery";
import { SearchPageType } from "./content/search-shelves";
import * as searchLandingCohort from "./landing/search-landing-cohort";
import * as searchLandingShelfController from "./landing/search-landing-shelf-controller";
/**
 * Determines whether or not the user will use the legacy Search Landing Page protocol
 * @param objectGraph The App Store Object Graph
 * @returns Whether or not the user will use the legacy Search Landing Page protocol
 */
function shouldUseProtocolV1(objectGraph) {
    if (objectGraph.client.isVision) {
        return false; // visionOS always uses the modern V2 protocol.
    }
    if (objectGraph.client.isWeb) {
        return false; // the "web" expects to use the V2 protocol as well
    }
    if (!objectGraph.bag.supportsSearchLandingPageV2) {
        return true; // Use V1 protocol when V2 is unsupported
    }
    // Use V1 protocol based on the bags rollout rate for V2 protocol.
    return !isFeatureEnabledForCurrentUser(objectGraph, objectGraph.bag.searchLandingPageV2RolloutRate);
}
async function fetchSearchLandingPage(objectGraph, fetchAds) {
    if (shouldUseProtocolV1(objectGraph)) {
        return await fetchSearchLandingPageV1(objectGraph, fetchAds);
    }
    if (objectGraph.bag.mediaAPISearchFocusEnabled) {
        return await fetchSearchLandingPageV2WithFocusPage(objectGraph, fetchAds);
    }
    return await fetchSearchLandingPageV2(objectGraph, fetchAds);
}
async function fetchSearchLandingPageV1(objectGraph, fetchAds) {
    const searchLandingRequest = new mediaDataFetching.Request(objectGraph)
        .forType("landing")
        .includingAgeRestrictions()
        .includingAdditionalPlatforms(mediaDataFetching.defaultAdditionalPlatformsForClient(objectGraph))
        .usingCustomAttributes(productPageVariants.shouldFetchCustomAttributes(objectGraph)); // for `extend=customArtwork`
    searchLandingRequest.targetResourceType = "groupings";
    const cohortIdOrNil = searchLandingCohort.cohortIdForUser(objectGraph, objectGraph.user.dsid);
    if ((cohortIdOrNil === null || cohortIdOrNil === void 0 ? void 0 : cohortIdOrNil.length) > 0) {
        searchLandingRequest.addingQuery("clusterId", cohortIdOrNil);
    }
    const fetchTimingMetricsBuilder = new FetchTimingMetricsBuilder();
    const modifiedObjectGraph = objectGraph.addingFetchTimingMetricsBuilder(fetchTimingMetricsBuilder);
    const fetchSearchLanding = mediaNetwork.fetchData(modifiedObjectGraph, searchLandingRequest);
    return await Promise.all([fetchSearchLanding, fetchAds]).then(([responseData, adResponse]) => {
        return fetchTimingMetricsBuilder.measureModelConstruction(() => {
            return landingPageFromResponseV1(modifiedObjectGraph, responseData, adResponse);
        });
    });
}
/**
 * Creates `SearchLandingPage` model from the V1 Search Landing Page protocol
 * @param objectGraph The App Store Object Graph
 * @param landingPageResponse The response from the fetch
 * @param adResponse The response from the ad fetch
 * @returns A `SearchLandingPage` model from the V1 Search Landing Page protocol
 */
function landingPageFromResponseV1(objectGraph, landingPageResponse, adResponse) {
    const mediaApiGroupingDataArray = serverData.asArrayOrEmpty(landingPageResponse, "results.contents");
    const mediaApiGroupingData = mediaApiGroupingDataArray[0];
    if (serverData.isNullOrEmpty(mediaApiGroupingData)) {
        return null;
    }
    if (!mediaRelationship.hasRelationship(mediaApiGroupingData, "tabs")) {
        return null;
    }
    const groupingGenreAdamId = mediaAttributes.attributeAsString(mediaApiGroupingData, "id");
    const pageInformation = metricsHelpersPage.metricsPageInformationFromMediaApiResponse(objectGraph, "Genre", mediaApiGroupingData.id, landingPageResponse);
    const onDevicePersonalizationMetricsData = onDevicePersonalization.metricsData(objectGraph);
    pageInformation.recoMetricsData = metricsHelpersUtil.combinedRecoMetricsDataFromMetricsData(pageInformation.recoMetricsData, null, onDevicePersonalizationMetricsData);
    pageInformation.iAdInfo = iadInfoFromOnDeviceAdResponse(objectGraph, "searchLanding", adResponse);
    const adIncidentRecorder = adIncidents.newRecorder(objectGraph, pageInformation.iAdInfo);
    adIncidents.recordAdResponseEventsIfNeeded(objectGraph, adIncidentRecorder, adResponse);
    const groupingParseContext = {
        shelves: [],
        metricsPageInformation: pageInformation,
        metricsLocationTracker: metricsHelpersLocation.newLocationTracker(),
        pageGenreAdamId: groupingGenreAdamId,
        pageGenreId: mediaAttributes.attributeAsNumber(mediaApiGroupingData, "genre"),
        hasAuthenticatedUser: serverData.isDefinedNonNull(objectGraph.user.dsid),
        isSearchLandingPage: true,
        adStitcher: landingAdStitch.adStitcherForOnDeviceSLPAdvertData(objectGraph, adResponse),
        adIncidentRecorder: adIncidentRecorder,
    };
    const flattenedGrouping = groupingCommon.flattenMediaApiGroupingData(objectGraph, mediaApiGroupingData);
    groupingCommon.insertInitialShelvesIntoGroupingParseContext(objectGraph, flattenedGrouping, groupingParseContext);
    const page = new SearchLandingPage(groupingParseContext.shelves);
    // Page refresh
    const refreshPolicy = new PageRefreshPolicy("timeSinceOnScreen", objectGraph.bag.searchLandingPageRefreshUpdateDelayInterval, objectGraph.bag.searchLandingPageOffscreenRefreshInterval, null);
    page.pageRefreshPolicy = refreshPolicy;
    // Ad Incidents
    page.adIncidents = adIncidents.recordedIncidents(objectGraph, groupingParseContext.adIncidentRecorder);
    metricsHelpersPage.addMetricsEventsToPageWithInformation(objectGraph, page, groupingParseContext.metricsPageInformation);
    return page;
}
function makeSearchLandingRequestV2(objectGraph, fetchAds) {
    const searchLandingRequest = new mediaDataFetching.Request(objectGraph)
        .forType("landing:new-protocol")
        .includingAgeRestrictions()
        .includingAdditionalPlatforms(mediaDataFetching.defaultAdditionalPlatformsForClient(objectGraph))
        .usingCustomAttributes(productPageVariants.shouldFetchCustomAttributes(objectGraph)) // for `extend=customArtwork`
        .includingScopedRelationships("search-recommendations", ["contents"])
        .addingQuery("name", "search-landing");
    if (areAppTagsEnabled(objectGraph, "slp")) {
        mediaRequestUtils.configureTagsForMediaRequest(searchLandingRequest);
    }
    if (objectGraph.client.isVision || objectGraph.client.isWeb) {
        searchLandingRequest.includingScopedAttributes("editorial-items", ["editorialClientParams"]);
    }
    const cohortIdOrNil = searchLandingCohort.cohortIdForUser(objectGraph, objectGraph.user.dsid);
    if ((cohortIdOrNil === null || cohortIdOrNil === void 0 ? void 0 : cohortIdOrNil.length) > 0) {
        searchLandingRequest.addingQuery("clusterId", cohortIdOrNil);
    }
    if (objectGraph.client.isiOS) {
        searchLandingRequest.addingQuery("meta", "adDisplayStyle");
    }
    return searchLandingRequest;
}
async function fetchSearchLandingPageV2(objectGraph, fetchAds) {
    const searchLandingRequest = makeSearchLandingRequestV2(objectGraph, fetchAds);
    const fetchTimingMetricsBuilder = new FetchTimingMetricsBuilder();
    const modifiedObjectGraph = objectGraph.addingFetchTimingMetricsBuilder(fetchTimingMetricsBuilder);
    const fetchSearchLanding = mediaNetwork.fetchData(modifiedObjectGraph, searchLandingRequest);
    const amsEngagement = objectGraph.amsEngagement;
    let amdPromise;
    if (amsEngagement && objectGraph.bag.enableRecoOnDeviceReordering) {
        const request = {
            timeout: 500,
            eventType: impressionDemotion.AMSEngagementAppStoreEventKey,
            tab: "search",
        };
        amdPromise = amsEngagement.performRequest(request);
    }
    return await Promise.all([fetchSearchLanding, fetchAds, amdPromise]).then(([responseData, adResponse, amdResponse]) => {
        return fetchTimingMetricsBuilder.measureModelConstruction(() => {
            return landingPageFromResponseV2(modifiedObjectGraph, responseData, adResponse, amdResponse);
        });
    });
}
/**
 * Creates `SearchLandingPage` model from the V2 Search Landing Page protocol
 * @param objectGraph The App Store Object Graph
 * @param landingPageResponse The response from the fetch
 * @param adResponse The response from the ad fetch
 * @returns A `SearchLandingPage` model from the V2 Search Landing Page protocol
 */
function landingPageFromResponseV2(objectGraph, landingPageResponse, adResponse, impressionData) {
    if (serverData.isNullOrEmpty(landingPageResponse.data)) {
        return null;
    }
    // Creates the page info
    const pageInformation = metricsHelpersPage.metricsPageInformationFromMediaApiResponse(objectGraph, "SearchLanding", "SearchLanding", landingPageResponse);
    // Decorate page info with personalization metrics
    const onDevicePersonalizationMetricsData = onDevicePersonalization.metricsData(objectGraph);
    pageInformation.recoMetricsData = metricsHelpersUtil.combinedRecoMetricsDataFromMetricsData(pageInformation.recoMetricsData, null, onDevicePersonalizationMetricsData);
    pageInformation.iAdInfo = iadInfoFromOnDeviceAdResponse(objectGraph, "searchLanding", adResponse);
    const adIncidentRecorder = adIncidents.newRecorder(objectGraph, pageInformation.iAdInfo);
    adIncidents.recordAdResponseEventsIfNeeded(objectGraph, adIncidentRecorder, adResponse);
    // Creates Search Landing Page Context
    const landingPageContext = {
        shelves: [],
        metricsLocationTracker: metricsHelpersLocation.newLocationTracker(),
        metricsPageInformation: pageInformation,
        adStitcher: landingAdStitch.adStitcherForOnDeviceSLPAdvertData(objectGraph, adResponse, landingPageResponse),
        adIncidentRecorder: adIncidentRecorder,
        pageType: SearchPageType.Landing,
        recoImpressionData: impressionDemotion.impressionEventsFromData(objectGraph, impressionData),
    };
    // Create the shelves for the page
    searchLandingShelfController.insertShelvesIntoSearchPageContext(objectGraph, landingPageResponse, landingPageContext);
    // Add Unified Messaging placement to top of page for NLS BT.
    const bubbleTipShelf = createNaturalLanguageSearchBubbleTipShelf(objectGraph);
    if (bubbleTipShelf) {
        landingPageContext.shelves.unshift(bubbleTipShelf);
    }
    const landingPage = new SearchLandingPage(landingPageContext.shelves);
    // Page refresh
    landingPage.pageRefreshPolicy = new PageRefreshPolicy("timeSinceOnScreen", objectGraph.bag.searchLandingPageRefreshUpdateDelayInterval, objectGraph.bag.searchLandingPageOffscreenRefreshInterval, null);
    // Ad Incidents
    landingPage.adIncidents = adIncidents.recordedIncidents(objectGraph, landingPageContext.adIncidentRecorder);
    metricsHelpersPage.addMetricsEventsToPageWithInformation(objectGraph, landingPage, landingPageContext.metricsPageInformation);
    return landingPage;
}
/**
 * Creates the NLS BT shelf for SLP if enabled.
 * @param objectGraph The app store object graph.
 * @returns The shelf for the NLS BT shown on SLP, or undefined if bag has feature disabled.
 */
export function createNaturalLanguageSearchBubbleTipShelf(objectGraph) {
    var _a;
    if (!objectGraph.bag.isNaturalLanguageSearchEnabled && !objectGraph.bag.isNaturalLanguageSearchResultsEnabled) {
        return undefined; // feature not enabled in the bag
    }
    const context = {
        signal: {
            lastNLSQueryDate: objectGraph.storage.retrieveString("lastNLSQueryDate"),
            treatmentId: (_a = appStoreExperiments.currentTreatmentIdForArea(objectGraph, ExperimentAreaId.SearchLandingPage)) !== null && _a !== void 0 ? _a : null,
        },
    };
    const shelf = groupingCommon.shelfForUnifiedMessage(objectGraph, "searchFocusHeader", context, "pullOnly");
    shelf.refreshUrl = `${Protocol.internal}:/${Path.searchLandingPage}/${Path.shelf}/?${Parameters.isSearchFocusHeaderShelf}=true`;
    return shelf;
}
async function fetchSearchLandingPageV2WithFocusPage(objectGraph, fetchAds) {
    const searchLandingRequest = makeSearchLandingRequestV2(objectGraph, fetchAds).enablingFeature("search-focus-suggestions");
    const fetchTimingMetricsBuilder = new FetchTimingMetricsBuilder();
    const modifiedObjectGraph = objectGraph.addingFetchTimingMetricsBuilder(fetchTimingMetricsBuilder);
    const fetchSearchLanding = mediaNetwork.fetchData(modifiedObjectGraph, searchLandingRequest);
    const amsEngagement = objectGraph.amsEngagement;
    let amdPromise = null;
    if (amsEngagement && objectGraph.bag.enableRecoOnDeviceReordering) {
        const request = {
            timeout: 500,
            eventType: impressionDemotion.AMSEngagementAppStoreEventKey,
            tab: "search",
        };
        amdPromise = amsEngagement.performRequest(request);
    }
    return await Promise.all([fetchSearchLanding, fetchAds, amdPromise]).then(async ([responseData, adResponse, amdResponse]) => {
        return await fetchTimingMetricsBuilder.measureModelConstructionAsync(async () => await landingPageFromResponseV2WithFocusPage(modifiedObjectGraph, responseData, adResponse, amdResponse));
    });
}
/**
 * Creates `SearchLandingPage` model from the V2 Search Landing Page protocol, but with search-focus feature enabled.
 * @param objectGraph The App Store Object Graph
 * @param landingPageResponse The response from the landing fetch
 * @param landingAdResponse The response from the landingAd fetch
 * @returns A `SearchLandingPage` model created from server response.
 */
async function landingPageFromResponseV2WithFocusPage(objectGraph, landingPageResponse, landingAdResponse, impressionData) {
    // MAINTAINER'S NOTE: V3 protocol does not change any existing SLP v2 fields and is purely additives for focus page support
    const landingPage = landingPageFromResponseV2(objectGraph, landingPageResponse, landingAdResponse, impressionData);
    // Create Search Focus Page
    return await fetchFocusPageUsingLandingPageResponse(objectGraph, landingPageResponse).then((focusPage) => {
        landingPage.searchFocusPage = focusPage;
        return landingPage;
    });
}
/**
 * Creates `SearchFocusPage` model from the V3 Search Landing Page protocol, fetching search history if needed.
 * @param objectGraph The App Store Object Graph
 * @param focusPageResponse The response from the fetch
 * @returns A `SearchFocusPage` model from the V3 Search Landing Page protocol
 */
async function fetchFocusPageUsingLandingPageResponse(objectGraph, landingPageResponse) {
    var _a;
    if (serverData.isNullOrEmpty(landingPageResponse.data)) {
        return null;
    }
    // Creates the page info
    const pageInformation = metricsHelpersPage.metricsPageInformationFromMediaApiResponse(objectGraph, "SearchFocus", "Focus", landingPageResponse, " ");
    // Decorate page info with personalization metrics
    const onDevicePersonalizationMetricsData = onDevicePersonalization.metricsData(objectGraph);
    pageInformation.recoMetricsData = metricsHelpersUtil.combinedRecoMetricsDataFromMetricsData(pageInformation.recoMetricsData, null, onDevicePersonalizationMetricsData);
    // Creates Search Focus Page Context
    const focusPageContext = {
        shelves: [],
        metricsLocationTracker: metricsHelpersLocation.newLocationTracker(),
        metricsPageInformation: pageInformation,
        pageType: SearchPageType.Focus,
    };
    const searchHistoryShelfMarker = searchLandingShelfController.firstShelfMarkerMatchingUseCase(landingPageResponse, focusPageContext, "recentSearches");
    // Skip fetching search history if there isn't a marker for it in SLP response.
    if (isNothing(searchHistoryShelfMarker)) {
        return createFocusPageFromResponse(objectGraph, landingPageResponse, focusPageContext);
    }
    const searchHistoryDisplayCount = (_a = mediaAttributes.attributeAsNumber(searchHistoryShelfMarker, "displayCount")) !== null && _a !== void 0 ? _a : 0;
    const fetchSearchHistory = objectGraph.onDeviceSearchHistoryManager.fetchRecentsWithLimit(searchHistoryDisplayCount);
    return await fetchSearchHistory.then((searchHistory) => {
        focusPageContext.searchHistory = searchHistory;
        return createFocusPageFromResponse(objectGraph, landingPageResponse, focusPageContext);
    });
}
function createFocusPageFromResponse(objectGraph, landingPageResponse, focusPageContext) {
    // Create the shelves for the focus page using same logic as landing page, but
    // includes search history in page context to support `search-recommendations-marker`.
    searchLandingShelfController.insertShelvesIntoSearchPageContext(objectGraph, landingPageResponse, focusPageContext);
    const focusPage = new SearchFocusPage(focusPageContext.shelves);
    if (serverData.isNullOrEmpty(focusPage.shelves)) {
        return null;
    }
    metricsHelpersPage.addMetricsEventsToPageWithInformation(objectGraph, focusPage, focusPageContext.metricsPageInformation);
    return focusPage;
}
async function fetchTrendingSearchesFallbackPage(objectGraph, fetchAds) {
    const fetchRequest = {
        url: objectGraph.bag.trendingSearchesURL,
    };
    const trendingSearchesPromise = objectGraph.network.fetch(fetchRequest).then((response) => {
        if (!response.ok) {
            throw Error(`Bad Status code ${response.status} for ${fetchRequest.url}`);
        }
        return JSON.parse(response.body);
    });
    return await Promise.all([trendingSearchesPromise, fetchAds]).then(([trendingSearchesData, adResponse]) => {
        var _a;
        const page = new SearchLandingPage(trendingSearchesShelvesForResponse(objectGraph, trendingSearchesData));
        const pageInformation = metricsHelpersPage.fakeMetricsPageInformation(objectGraph, "SearchLanding", "trending", ""); // old trending endpoint doesn't have metrics meta
        pageInformation.iAdInfo = iadInfoFromOnDeviceAdResponse(objectGraph, "searchLanding", adResponse);
        (_a = pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.setMissedOpportunity(objectGraph, "SLPLOAD", "searchLanding"); // trending fallback never displays ad, so is always missed opportunity.
        metricsHelpersPage.addMetricsEventsToPageWithInformation(objectGraph, page, pageInformation);
        return page;
    });
}
/**
 * Creates a trending searches shelves from the given JSON response.
 *  @param  objectGraph  The App Store Object Graph.
 *  @param  response    The API response JSON data.
 *  @return {Shelf[]} Trending searches shelves created from response.
 */
function trendingSearchesShelvesForResponse(objectGraph, response) {
    return validation.context("trendingSearchesShelfForResponse", () => {
        const locationTracker = metricsHelpersLocation.newLocationTracker();
        const searches = serverData.asArrayOrEmpty(response, "trendingSearches").map((rawSearch) => {
            const term = serverData.asString(rawSearch, "label");
            const searchAction = new SearchAction(term, term, serverData.asString(rawSearch, "url"), "trending");
            if (objectGraph.client.isPhone) {
                searchAction.artwork = contentArtwork.createArtworkForResource(objectGraph, "systemimage://magnifyingglass");
            }
            metricsHelpersClicks.addEventsToSearchAction(objectGraph, searchAction, "button", locationTracker);
            metricsHelpersLocation.nextPosition(locationTracker);
            return searchAction;
        });
        let maxNumberOfSearches = 0;
        switch (objectGraph.client.deviceType) {
            case "pad":
                maxNumberOfSearches = 10;
                break;
            case "phone":
                maxNumberOfSearches = 7;
                break;
            default:
                break;
        }
        const shelf = new Shelf("action");
        shelf.title = searches.length > 0 ? serverData.asString(response, "header.label") : null;
        shelf.isHorizontal = false;
        shelf.items = searches.slice(0, maxNumberOfSearches);
        return [shelf];
    });
}
export async function fetchPage(objectGraph) {
    const fetchAds = isAdPlacementEnabled(objectGraph, "searchLanding")
        ? landingAdFetchFetchAds(objectGraph, "searchLanding").catch(() => null)
        : null;
    return await fetchSearchLandingPage(objectGraph, fetchAds).catch(async (e) => {
        // If the client has provided a `trendingSearchesURL`, we can fallback to that search
        // mechanism if the search landing request fails. If `trendingSearchesURL` is not
        // provided, we re-throw the original landing page error for the client to handle.
        if (isSome(objectGraph.bag.trendingSearchesURL)) {
            return await fetchTrendingSearchesFallbackPage(objectGraph, fetchAds);
        }
        else {
            throw e;
        }
    });
}
//# sourceMappingURL=search-landing-page-utils.js.map