import { PageInvocationPoint } from "@jet/environment/types/metrics";
import { isSome } from "@jet/environment/types/optional";
import * as models from "../../../api/models";
import * as serverData from "../../../foundation/json-parsing/server-data";
import * as mediaAttributes from "../../../foundation/media/attributes";
import * as mediaDataStructure from "../../../foundation/media/data-structure";
import { ResponseMetadata } from "../../../foundation/network/network";
import { Parameters } from "../../../foundation/network/url-constants";
import * as urls from "../../../foundation/network/urls";
import * as dateUtil from "../../../foundation/util/date-util";
import * as objects from "../../../foundation/util/objects";
import * as content from "../../content/content";
import { productVariantDataForData } from "../../product-page/product-page-variants";
import * as guidedSearch from "../../search/guided-search/guided-search-metrics";
import * as searchAds from "../../search/search-ads";
import * as metricsBuilder from "../builder";
import * as misc from "./misc";
import * as metricsModels from "./models";
import * as metricsUtil from "./util";
import { searchMetricsDataSetID } from "../../search/search-common";
//* *************************
//* Page Metrics
//* *************************
export function metricsPageInformationFromMarketingItemMediaApiResponse(objectGraph, pageType, pageId, marketingItemData) {
    var _a;
    const pageInformation = metricsPageInformationFromMediaApiResponse(objectGraph, pageType, pageId, marketingItemData);
    if (serverData.isDefinedNonNull(marketingItemData)) {
        pageInformation.mercuryMetricsData =
            (_a = metricsUtil.marketingItemTopLevelBaseFieldsFromData(objectGraph, marketingItemData)) !== null && _a !== void 0 ? _a : undefined;
    }
    return pageInformation;
}
export function metricsPageInformationFromMediaApiResponse(objectGraph, pageType, pageId, response, pageDetails, overrideIAdInfo) {
    var _a, _b, _c;
    const pageInformation = (_a = serverData.asInterface(response, ResponseMetadata.pageInformation, {})) !== null && _a !== void 0 ? _a : {};
    const timingMetrics = serverData.asInterface(response, ResponseMetadata.timingValues);
    const pageUrl = serverData.asString(response, ResponseMetadata.requestedUrl);
    let productVariantData;
    const responsePageBaseFields = pageInformation;
    responsePageBaseFields.pageType = pageType;
    responsePageBaseFields.pageId = pageId;
    if (pageDetails) {
        responsePageBaseFields["pageDetails"] = pageDetails;
    }
    if (pageType === "Software") {
        const softwareData = mediaDataStructure.dataFromDataContainer(objectGraph, response);
        if (isSome(softwareData)) {
            const name = mediaAttributes.attributeAsString(softwareData, "name");
            const artistName = mediaAttributes.attributeAsString(softwareData, "artistName");
            responsePageBaseFields["pageDetails"] = `${artistName}_${name}`;
            // To distinguish normal apps from Arcade apps
            if (content.isArcadeSupported(objectGraph, softwareData)) {
                responsePageBaseFields["softwareType"] = "Arcade";
            }
            productVariantData = productVariantDataForData(objectGraph, softwareData);
        }
    }
    else if (pageType === "Genre") {
        responsePageBaseFields["pageDetails"] = `${pageType}_${pageId}`;
    }
    else if (pageType === "Search") {
        responsePageBaseFields["pageDetails"] = "Apps";
    }
    else if (pageType === "SearchLanding" && pageId === "SearchLanding") {
        responsePageBaseFields["pageDetails"] = `${pageType}_${pageId}`;
    }
    const pageInfo = new metricsModels.MetricsPageInformation((_b = metricsUtil.sanitizedMetricsDictionary(responsePageBaseFields)) !== null && _b !== void 0 ? _b : {});
    if (timingMetrics !== null) {
        pageInfo.timingMetrics = timingMetrics;
        if (isSome(pageUrl)) {
            pageInfo.pageUrl = pageUrl;
        }
    }
    // For product pages, every shelf is creating it's a new page information. There should be one top-level one, shared for page.
    // Revisit in: rdar://77227964 (Metrics: ProductPage: Shelves should share page information instead of building it per-shelf.)
    if (serverData.isDefinedNonNull(productVariantData)) {
        pageInfo.productVariantData = productVariantData;
    }
    const iAdInfo = overrideIAdInfo !== null && overrideIAdInfo !== void 0 ? overrideIAdInfo : metricsModels.iAdInformationFromMediaApiResponse(objectGraph, pageId, response);
    if (isSome(iAdInfo)) {
        pageInfo.iAdInfo = iAdInfo;
    }
    pageInfo.recoMetricsData = (_c = mediaDataStructure.metricsFromMediaApiObject(response)) !== null && _c !== void 0 ? _c : undefined;
    return pageInfo;
}
export function fakeMetricsPageInformation(objectGraph, pageType, pageId, pageDetails, iAdClickInfo) {
    var _a;
    const page = new metricsModels.MetricsPageInformation({
        pageType: pageType,
        pageId: pageId,
        page: `${pageType}_${pageId}`,
        pageDetails: pageDetails,
    });
    if (iAdClickInfo) {
        // rdar://72607206 (Gibraltar: Tech Debt: Population of iAd fields in Product Page)
        const placementType = metricsModels.IAdSearchInformation.placementTypeFromPlacementId(objectGraph, serverData.asString(iAdClickInfo, "iAdPlacementId"));
        page.iAdInfo = new metricsModels.IAdSearchInformation(objectGraph, placementType, metricsModels.IAdSearchInformation.createInitialSlotInfos(objectGraph, placementType, null, null), (_a = serverData.asString(iAdClickInfo, "iAdId")) !== null && _a !== void 0 ? _a : undefined);
        page.iAdInfo.applyClickFieldsFromPageRequest(pageId, iAdClickInfo);
    }
    return page;
}
export function addMetricsEventsToPageWithInformation(objectGraph, page, pageInformation, fieldsModifier, isDefaultBrowser) {
    if (serverData.isNull(pageInformation)) {
        return;
    }
    page.pageMetrics.pageFields = misc.fieldsFromPageInformation(pageInformation);
    page.pageMetrics.addManyInstructions(impressionsInstructionsFromPageData(objectGraph, pageInformation, fieldsModifier));
    page.pageMetrics.addData(pageEventFromPageData(objectGraph, pageInformation, fieldsModifier, isDefaultBrowser), [
        PageInvocationPoint.pageEnter,
    ]);
    page.pageMetrics.addData(pageExitEventFromPageData(objectGraph, pageInformation, fieldsModifier), [
        PageInvocationPoint.pageExit,
    ]);
    page.pageMetrics.pageRenderFields = pageRenderFromPageData(objectGraph, pageInformation, fieldsModifier);
    page.pageRenderMetrics = pageRenderFromPageData(objectGraph, pageInformation, fieldsModifier);
    if (pageRequiresBackEvent(page)) {
        page.pageMetrics.addData(backEventFromPageData(objectGraph, pageInformation, fieldsModifier), [
            PageInvocationPoint.backButton,
        ]);
    }
    const fetchTimingMetricsBuilder = objectGraph.fetchTimingMetricsBuilder;
    if (isSome(fetchTimingMetricsBuilder)) {
        fetchTimingMetricsBuilder.decorate(page);
    }
}
/**
 * @param page The page to check
 * @returns Whether this page requires a back event to be sent when the user navigates back from it.
 */
function pageRequiresBackEvent(page) {
    const isSearchResultsPage = page instanceof models.SearchResults;
    const isContingentOfferDetailPage = page instanceof models.ContingentOfferDetailPage;
    const isWinbackOfferDetailPage = page instanceof models.OfferItemDetailPage;
    return !isSearchResultsPage && !isContingentOfferDetailPage && !isWinbackOfferDetailPage;
}
export function copyMetricsEventsIntoSidepackedPagewithInformation(objectGraph, page, pageInformation) {
    if (serverData.isNull(pageInformation)) {
        return;
    }
    page.pageMetrics.addData(backEventFromPageData(objectGraph, pageInformation, undefined), [
        PageInvocationPoint.backButton,
    ]);
    if (serverData.isNull(page.pageMetrics.pageFields)) {
        page.pageMetrics.pageFields = {};
    }
}
/**
 * Constructs metrics page information for a page navigated to from the action links on the product page.
 * @param productId The adamId of the product for which these action links are displayed.
 * @param pageType The metrics pageType for the page the links navigates to.
 */
export function pageInformationForActionLinkPage(objectGraph, productId, pageType) {
    const base = {
        pageId: productId || "",
        pageType: pageType,
    };
    return new metricsModels.MetricsPageInformation(base);
}
/**
 * Create metrics page information for rooms that may not have an its id.
 * @param pageId The identifier to differentiate room
 */
export function pageInformationForRoom(objectGraph, pageId) {
    const pageType = "Room";
    const page = new metricsModels.MetricsPageInformation({
        pageType: pageType,
        pageId: pageId,
        page: `${pageType}_${pageId}`,
    });
    return page;
}
/**
 * Constructs empty page information for search hints 'page' for given prefix term and optional dataSetId.
 */
export function pageInformationForSearchHintsPage(objectGraph, prefixTerm, pageUrl, dataSetId) {
    const base = {
        pageId: "hints",
        pageType: "Search",
    };
    if (dataSetId) {
        base[searchMetricsDataSetID] = dataSetId;
    }
    const pageInformation = new metricsModels.MetricsPageInformation(base);
    pageInformation.pageUrl = pageUrl;
    return pageInformation;
}
export function pageInformationForIAPPage(objectGraph, parentId, iapId) {
    const base = {
        pageId: iapId || "",
        pageType: "IAPInstallPage",
        parentId: metricsUtil.emptyStringIfNullOrUndefined(parentId),
    };
    const pageInformation = new metricsModels.MetricsPageInformation(base);
    return pageInformation;
}
/**
 * Build the pageInformation to use for search pages.
 */
export function pageInformationForSearchPage(objectGraph, request, response, termContext, searchRequestUrl, pageId, sponsoredSearchRequestData, wasOdmlSuccessful, guidedSearchData) {
    const pageInformation = metricsPageInformationFromMediaApiResponse(objectGraph, "Search", pageId, response);
    pageInformation.searchTermContext = termContext;
    pageInformation.pageUrl = searchRequestUrl; // For Search, pageUrl is request url
    if (guidedSearchData) {
        pageInformation.guidedSearch = guidedSearch.guidedSearchPageInformationFields(objectGraph, request, guidedSearchData);
    }
    if (searchAds.platformSupportsAdverts(objectGraph) && sponsoredSearchRequestData != null) {
        pageInformation.iAdInfo = new metricsModels.IAdSearchInformation(objectGraph, "searchResults", metricsModels.IAdSearchInformation.createInitialSlotInfos(objectGraph, "searchResults", null, null), sponsoredSearchRequestData.iAdId, sponsoredSearchRequestData.appStoreClientRequestId, wasOdmlSuccessful);
    }
    return pageInformation;
}
export function pageInformationForSegmentedSearchPage(objectGraph, response, termContext, searchRequestUrl, groupId) {
    const pageInformation = metricsPageInformationFromMediaApiResponse(objectGraph, "Search", groupId, response);
    pageInformation.searchTermContext = termContext;
    pageInformation.pageUrl = searchRequestUrl;
    return pageInformation;
}
export function pageInformationForAppPromotionDetailPage(objectGraph, appPromotionType, appPromotionId, parentAppAdamId, referrerData, recoMetricsData) {
    let pageId = "";
    let pageType = "";
    switch (appPromotionType) {
        case models.AppPromotionType.AppEvent:
            pageId = `${appPromotionId}_${parentAppAdamId}`;
            pageType = "EventDetails";
            break;
        case models.AppPromotionType.ContingentOffer:
        case models.AppPromotionType.OfferItem:
            pageId = `${appPromotionId}`;
            pageType = "OfferDetails";
            break;
        default:
            break;
    }
    const base = {
        pageId: pageId,
        pageType: pageType,
    };
    if (serverData.isDefinedNonNull(referrerData)) {
        base["refApp"] = referrerData["app"];
        base["extRefUrl"] = referrerData["externalUrl"];
    }
    const pageInformation = new metricsModels.MetricsPageInformation(base);
    pageInformation.recoMetricsData = recoMetricsData !== null && recoMetricsData !== void 0 ? recoMetricsData : undefined;
    return pageInformation;
}
export function addPageEventsToInAppPurchasePage(objectGraph, page, pageInformation) {
    addMetricsEventsToPageWithInformation(objectGraph, page, pageInformation);
}
export function makePageReferralEligible(objectGraph, page) {
    if (serverData.isNull(page) || serverData.isNull(page.pageMetrics)) {
        return;
    }
    const pageInstructions = page.pageMetrics.instructions;
    if (!serverData.isNull(pageInstructions)) {
        for (const instruction of pageInstructions) {
            // We shoudl only be requesting crossfire information for page events.
            // if we dont, and add it to all of the instructions then we end up clearing out
            // the referral information on the native side during any other event that happens on this page
            // before a buy can happen, for instance an impressions event that occurs on page exit when
            // displaying an upsell sheet.
            if (instruction.data.fields["eventType"] !== "page") {
                continue;
            }
            instruction.data.includingFields.push("crossfireReferral");
        }
    }
    // Ensure that for referral eligible pages, the attribution is included in the purchase configuration
    // <rdar://problem/45420867> Crossfire: Metrics: extRefApp2/extRefUrl2 buyparam are being persisted too strongly, being sent on buys of other app pages
    let productLockup = null;
    if (page instanceof models.ProductPage) {
        productLockup = page;
    }
    else if (page instanceof models.ShelfBasedProductPage) {
        productLockup = page.lockup;
    }
    if (productLockup) {
        const eligibleActions = [];
        if (productLockup.buttonAction instanceof models.OfferAction) {
            eligibleActions.push(productLockup.buttonAction);
        }
        else if (productLockup.buttonAction instanceof models.OfferConfirmationAction &&
            productLockup.buttonAction.buyAction instanceof models.OfferAction) {
            eligibleActions.push(productLockup.buttonAction.buyAction);
        }
        else if (productLockup.buttonAction instanceof models.OfferAlertAction &&
            productLockup.buttonAction.completionAction instanceof models.OfferAction) {
            eligibleActions.push(productLockup.buttonAction.completionAction);
        }
        else if (productLockup.buttonAction instanceof models.OfferStateAction) {
            if (productLockup.buttonAction.buyAction instanceof models.OfferAction) {
                eligibleActions.push(productLockup.buttonAction.buyAction);
            }
            if (productLockup.buttonAction.defaultAction instanceof models.OfferAction) {
                eligibleActions.push(productLockup.buttonAction.defaultAction);
            }
            if (productLockup.buttonAction.openAction instanceof models.OfferAction) {
                eligibleActions.push(productLockup.buttonAction.openAction);
            }
            if (productLockup.buttonAction.subscribePageAction instanceof models.FlowAction &&
                productLockup.buttonAction.subscribePageAction.page === "arcadeSubscribe" &&
                isSome(productLockup.buttonAction.subscribePageAction.pageUrl) &&
                productLockup.buttonAction.subscribePageAction.pageUrl.length > 0) {
                const pageUrl = urls.URL.from(productLockup.buttonAction.subscribePageAction.pageUrl);
                pageUrl.param(Parameters.includePostSubscribeAttribution, "true");
                productLockup.buttonAction.subscribePageAction.pageUrl = pageUrl.build();
            }
            if (productLockup.buttonAction.subscribePageAction instanceof models.FlowAction &&
                productLockup.buttonAction.subscribePageAction.pageData instanceof models.MarketingItemRequestInfo &&
                productLockup.buttonAction.subscribePageAction.pageData.purchaseSuccessAction instanceof
                    models.OfferAction) {
                eligibleActions.push(productLockup.buttonAction.subscribePageAction.pageData.purchaseSuccessAction);
            }
        }
        for (const eligibleAction of eligibleActions) {
            if (eligibleAction.purchaseConfiguration) {
                eligibleAction.purchaseConfiguration.excludeAttribution = false;
            }
        }
    }
}
/**
 * Add an updated set of page metrics to the shelf that will be used to update the existing page metrics,
 * as well as be sent as a `pageChange` event.
 * @param objectGraph The object graph.
 * @param shelf The shelf to attach the pageChange fields to
 * @param pageInformation The modified page information for the containing page.
 * @param fieldsModifier A field modifier, if required.
 * @returns
 */
export function addPageChangeFieldsToShelfWithInformation(objectGraph, shelf, pageInformation, fieldsModifier) {
    if (serverData.isNull(pageInformation)) {
        return;
    }
    let updatedEvents = [];
    // Page
    const pageFields = misc.fieldsFromPageInformation(pageInformation);
    const pageEvent = pageEventFromPageData(objectGraph, pageInformation, fieldsModifier);
    updatedEvents.push(pageEvent);
    // Impressions
    const impressionsInstructions = impressionsInstructionsFromPageData(objectGraph, pageInformation, fieldsModifier);
    const impressionsEvents = impressionsInstructions.map((instruction) => instruction.data);
    updatedEvents = updatedEvents.concat(impressionsEvents);
    filterPageMetricsFieldsForPageChange(pageFields);
    updatedEvents.forEach((event) => filterPageMetricsFieldsForPageChange(event.fields));
    shelf.pageChangeMetrics = {
        pageFields: pageFields,
        updatedEvents: updatedEvents,
    };
}
/// An allow list for the fields that can change in a `pageChange` event.
const allowedPageChangeKeys = new Set([
    "iAdAppStoreClientRequestId",
    "iAdId",
    "iAdSlotInfo",
    "iAdOdmlSuccess",
    "iAdEligible",
    "iAdContainerId",
    "iAdImpressionId",
    "iAdMetadata",
    "adamId",
    "iAd",
    "iAdPlacementId",
    "iAdMissedOpportunityReason",
    "hasiAdData",
    "iAdTemplateType",
    // These are required to merge with existing events
    "eventType",
    "impressionQueue",
]);
/**
 * Filter some fields for the allowed content for a pageChange event.
 * @param metricsFields The MetricsFields to filter.
 */
function filterPageMetricsFieldsForPageChange(metricsFields) {
    Object.keys(metricsFields)
        .filter((key) => !allowedPageChangeKeys.has(key))
        .forEach((key) => delete metricsFields[key]);
}
function pageEventFromPageData(objectGraph, pageInformation, fieldsModifier, isDefaultBrowser) {
    var _a, _b, _c, _d, _e;
    const base = baseFieldsForPageEventsFromPageInformation(pageInformation, fieldsModifier);
    if (pageInformation.iAdInfo) {
        // Always copy this, even if no ad is present.
        Object.assign(base, pageInformation.iAdInfo.pageFields);
    }
    // Include pre-order release date for page events.
    if (serverData.isDefinedNonNull(pageInformation.offerReleaseDate)) {
        base["offerReleaseDate"] = dateUtil.millisecondsToUTCMidnightFromLocalDate(pageInformation.offerReleaseDate);
    }
    const searchTermContext = pageInformation.searchTermContext;
    if (searchTermContext) {
        base["searchTerm"] = searchTermContext.term;
        if (searchTermContext.suggestedTerm) {
            base["searchSuggestedTerm"] = searchTermContext.suggestedTerm;
        }
        if (searchTermContext.correctedTerm) {
            base["searchCorrectedTerm"] = searchTermContext.correctedTerm;
        }
        if (searchTermContext.originatingTerm) {
            base["searchOriginatingTerm"] = searchTermContext.originatingTerm;
        }
    }
    if (pageInformation.guidedSearch) {
        Object.assign(base, pageInformation.guidedSearch);
    }
    const pageEvent = metricsBuilder.createMetricsPageData(objectGraph, false, (_a = pageInformation.isCrossfireReferralCandidate) !== null && _a !== void 0 ? _a : false, pageInformation.timingMetrics, base, isDefaultBrowser);
    const hasAdverts = (_c = (_b = pageInformation.iAdInfo) === null || _b === void 0 ? void 0 : _b.iAdIsPresent) !== null && _c !== void 0 ? _c : false;
    const shouldIncludeAdRotationFields = (_e = (_d = pageInformation.iAdInfo) === null || _d === void 0 ? void 0 : _d.shouldIncludeAdRotationFields) !== null && _e !== void 0 ? _e : false;
    if (hasAdverts && shouldIncludeAdRotationFields) {
        pageEvent.includingFields.push("advertRotation");
    }
    return pageEvent;
}
/**
 * Creates a pageExitEvent which has all of the same fields as a pageEvent but with an eventType of pageExit.
 * Noted this event is an event that is primarily meant to be used so the JS can get a hook into when a page is
 * left so we can clear out associated referralContexts
 */
function pageExitEventFromPageData(objectGraph, pageInformation, fieldsModifier) {
    const pageExitEvent = pageEventFromPageData(objectGraph, pageInformation, fieldsModifier);
    pageExitEvent.fields["eventType"] = "pageExit";
    return pageExitEvent;
}
function backEventFromPageData(objectGraph, pageInformation, fieldsModifier) {
    const base = baseFieldsForPageEventsFromPageInformation(pageInformation, fieldsModifier);
    return metricsBuilder.createMetricsBackClickData(objectGraph, base);
}
function pageRenderFromPageData(objectGraph, pageInformation, fieldsModifier) {
    const base = baseFieldsForPageEventsFromPageInformation(pageInformation, fieldsModifier);
    if (pageInformation.searchTermContext) {
        base["searchTerm"] = pageInformation.searchTermContext.term;
    }
    // Splice in page fields until all native clients have fix for <rdar://problem/68879825>
    if (pageInformation.baseFields) {
        Object.assign(base, pageInformation.baseFields);
    }
    return metricsBuilder.createMetricsPageRenderFields(objectGraph, pageInformation.timingMetrics, base);
}
function impressionsInstructionsFromPageData(objectGraph, pageInformation, fieldsModifier) {
    var _a, _b, _c;
    const base = baseFieldsForPageEventsFromPageInformation(pageInformation, fieldsModifier);
    if (pageInformation.searchTermContext) {
        base["searchTerm"] = pageInformation.searchTermContext.term;
    }
    const impressionBase = objects.shallowCopyOf(base);
    if (pageInformation.iAdInfo) {
        Object.assign(impressionBase, pageInformation.iAdInfo.impressionsFields);
    }
    if (pageInformation.guidedSearch) {
        Object.assign(impressionBase, pageInformation.guidedSearch);
    }
    // Regular Impressions
    const shouldIncludeAdMetrics = serverData.isDefinedNonNull(pageInformation.iAdInfo);
    const iAdEligibleForWindowCollection = serverData.isNullOrEmpty((_a = pageInformation.iAdInfo) === null || _a === void 0 ? void 0 : _a.missedOpportunityReason) && objectGraph.client.isPad;
    const shouldIncludeAdRotationFields = (_c = (_b = pageInformation.iAdInfo) === null || _b === void 0 ? void 0 : _b.shouldIncludeAdRotationFields) !== null && _c !== void 0 ? _c : false;
    const impressionsData = metricsBuilder.createMetricsImpressionsData(objectGraph, impressionBase, shouldIncludeAdMetrics && iAdEligibleForWindowCollection, shouldIncludeAdRotationFields, true);
    const instruction = {
        data: impressionsData,
        invocationPoints: [PageInvocationPoint.appExit, PageInvocationPoint.pageExit],
    };
    // Fast Impressions
    const impressionsArray = [instruction];
    if (shouldIncludeAdMetrics) {
        const fastImpressions = metricsBuilder.createMetricsFastImpressionsData(objectGraph, impressionBase, pageInformation);
        impressionsArray.push({
            data: fastImpressions,
            invocationPoints: [PageInvocationPoint.appExit, PageInvocationPoint.pageExit, PageInvocationPoint.timer],
        });
    }
    return impressionsArray;
}
function baseFieldsForPageEventsFromPageInformation(pageInformation, fieldsModifier) {
    const base = {};
    // Add offer type for pre-orders (this should be added to everything, including impressions)
    if (serverData.isDefinedNonNull(pageInformation.offerType)) {
        base["offerType"] = pageInformation.offerType;
    }
    if (fieldsModifier !== undefined && base) {
        fieldsModifier(base);
    }
    return base;
}
//# sourceMappingURL=page.js.map