import { FetchTimingMetricsBuilder } from "@jet/environment/metrics/fetch-timing-metrics-builder";
import * as validation from "@jet/environment/json/validation";
import * as models from "../../api/models";
import * as mediaUrlMapping from "../../common/builders/url-mapping";
import * as categories from "../../common/categories";
import * as topChartsCommon from "../../common/top-charts-common";
import * as serverData from "../../foundation/json-parsing/server-data";
import { isSome } from "@jet/environment/types/optional";
import { makeChartsPageIntent } from "../../api/intents/charts-page-intent";
import { segmentFromData } from "../../common/charts/charts-page-model";
import { makeChartsPageURL } from "../../common/charts/charts-page-url";
import { getLocale } from "../../common/locale";
import { getPlatform } from "../../common/preview-platform";
import * as mediaDataStructure from "../../foundation/media/data-structure";
import * as mediaUrlBuilder from "../../foundation/media/url-builder";
import * as urls from "../../foundation/network/urls";
export async function fetchAndRenderTopChartPage(objectGraph, genreId, charts, selectedChart, ages) {
    if (serverData.isNullOrEmpty(charts)) {
        const siblingChart = topChartsCommon.lookupFallbackSiblingChart(objectGraph, selectedChart);
        if (siblingChart !== undefined) {
            charts = `${selectedChart},${siblingChart}`; // charts not provided, use hardcoded sibling along with selected chart
        }
        else {
            charts = selectedChart; // charts not provided, use selected chart
        }
    }
    else if (!charts.includes(selectedChart)) {
        charts += `,${selectedChart}`; // charts missing selected chart, so inject it.
    }
    // Request
    // Note: `FetchTimingMetricsBuilder` API currently only supports instrumenting a single primary network call.
    // We will choose to use the network metrics data from the top charts call.
    const fetchTimingMetricsBuilder = new FetchTimingMetricsBuilder();
    const modifiedObjectGraph = objectGraph.addingFetchTimingMetricsBuilder(fetchTimingMetricsBuilder);
    const topChartsPromise = topChartsCommon.fetchTopChartsData(modifiedObjectGraph, genreId, charts, ages);
    let categoriesRequestPromise;
    // Vision currently doesn't support displaying categories on the Charts page.
    if (!objectGraph.client.isVision) {
        categoriesRequestPromise = topChartsCommon.fetchCategoriesDataForPlatform(objectGraph, charts, genreId);
    }
    // Response
    return await Promise.all([topChartsPromise, categoriesRequestPromise]).then(([topChartsResponse, categoriesResponse]) => {
        return fetchTimingMetricsBuilder.measureModelConstruction(() => {
            const categoriesList = categories.categoryListFromApiResponse(objectGraph, categoriesResponse);
            const context = serverData.isNull(categoriesList)
                ? categories.createContextFromGenre(genreId)
                : categories.createContextFromList(categoriesList);
            return topChartsPageFromApiResponse(modifiedObjectGraph, topChartsResponse, genreId, categoriesList, selectedChart, context);
        });
    });
}
/**
 * Creates a Top Charts Page model object from a server data structure.
 *
 * @param objectGraph The object graph for the App Store.
 * @param response The server response to parse.
 * @param genreId The genre identifier of the genre to which this page belongs.
 * @param categoriesList The list of categories for top charts picker.
 * @param selectedChart The selected chart, typically shown in a segemented control.
 * @param context  The context of the charts, .e.g. All, Apps, or Games.
 * @returns A top charts page model object.
 */
export function topChartsPageFromApiResponse(objectGraph, response, genreId, categoriesList, selectedChart, context) {
    return validation.context("topChartsPageFromApiResponse", () => {
        var _a;
        // Categories
        const topChartsCategories = [];
        if (isSome(categoriesList)) {
            for (const categoryListItem of categoriesList.categories) {
                if (isSome(categoryListItem.genreId)) {
                    topChartsCategories.push(convertCategoryToTopChartsCategory(objectGraph, categoryListItem));
                }
            }
        }
        // Construct the segments and sort by chart name to prevent the order from changing
        const rawSegments = mediaDataStructure.chartResultsFromServerResponse(response);
        const segments = rawSegments
            .map((segment) => {
            return segmentFromData(objectGraph, segment, response, genreId, context);
        })
            .sort((a, b) => {
            return a.chart.localeCompare(b.chart);
        });
        const segmentHref = serverData.asString(rawSegments, "0.href");
        const segmentURL = new urls.URL(segmentHref);
        const segmentGenreId = (_a = segmentURL.query["genre"]) !== null && _a !== void 0 ? _a : genreId;
        const ages = segmentURL.query["ages"];
        const selectedCategory = findCategoryInTree(segmentGenreId, ages, topChartsCategories);
        const title = topChartsCommon.topChartsPageTitleForSegments(objectGraph, segments);
        // Build Page
        const topChartsPage = new models.TopChartsPage(segmentGenreId, selectedCategory === null || selectedCategory === void 0 ? void 0 : selectedCategory.ageBandId, title, segments, selectedCategory === null || selectedCategory === void 0 ? void 0 : selectedCategory.name, topChartsCategories);
        // The "web" client needs a `FlowAction` defined on each category and segment to perform navigation
        if (objectGraph.client.isWeb) {
            for (const category of topChartsCategories) {
                const destination = makeChartsPageIntent({
                    ...getLocale(objectGraph),
                    ...getPlatform(objectGraph),
                    genreId: category.genreId,
                    chart: selectedChart,
                    ageBandId: category.ageBandId,
                });
                const pageUrl = makeChartsPageURL(objectGraph, destination);
                const action = new models.FlowAction("topCharts");
                action.destination = destination;
                action.pageUrl = pageUrl;
                category.chartSelectAction = action;
            }
            for (const segment of segments) {
                const destination = makeChartsPageIntent({
                    ...getLocale(objectGraph),
                    ...getPlatform(objectGraph),
                    genreId,
                    chart: segment.chart,
                    ageBandId: selectedCategory === null || selectedCategory === void 0 ? void 0 : selectedCategory.ageBandId,
                });
                const pageUrl = makeChartsPageURL(objectGraph, destination);
                const action = new models.FlowAction("topCharts");
                action.destination = destination;
                action.pageUrl = pageUrl;
                segment.segmentSelectAction = action;
            }
        }
        // Find selected chart segment
        const selectedIndex = segments.findIndex((segment) => segment.chart === selectedChart);
        if (selectedIndex >= 0) {
            topChartsPage.initialSegmentIndex = selectedIndex;
        }
        // Decorate the final page model with network performance metrics.
        const fetchTimingMetricsBuilder = objectGraph.fetchTimingMetricsBuilder;
        if (isSome(fetchTimingMetricsBuilder)) {
            fetchTimingMetricsBuilder.decorate(topChartsPage);
        }
        return topChartsPage;
    });
}
export function convertCategoryToTopChartsCategory(objectGraph, category) {
    const topChartsPageRequest = mediaUrlMapping.mediaApiChartsRequest(objectGraph, category.genreId, category.ageBandId);
    const children = category.children.map((child) => convertCategoryToTopChartsCategory(objectGraph, child));
    return new models.TopChartCategory(category, mediaUrlBuilder.buildURLFromRequest(objectGraph, topChartsPageRequest).toString(), children);
}
/**
 * Recursively search through a category list to find a matching genre/age
 * @param genreId The genreId to search for
 * @param ageBandId The age band to include when searching for selected genre
 * @param categoryList The array of categories to search recursively through
 * @returns A found category, or null if not found.
 */
function findCategoryInTree(genreId, ageBandId, categoryList) {
    for (const category of categoryList) {
        if ((ageBandId && category.ageBandId === ageBandId) ||
            (!ageBandId && !category.ageBandId && category.genreId === genreId)) {
            return category;
        }
        const result = findCategoryInTree(genreId, ageBandId, category.children);
        if (result) {
            return result;
        }
    }
    return null;
}
//# sourceMappingURL=top-charts-page.js.map