"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.forceUpdateContent = exports.getContentEditUrl = exports.getShortenedCategories = exports.getPublisherInfo = exports.getItemOrgId = exports.extractFirstContact = exports.determineExtent = exports.getAdditionalResourceUrl = exports.isDataSourceOfItem = exports.extractRawResources = exports.getAdditionalResources = exports.getItemSpatialReference = exports.isPageType = exports.getHubRelativeUrl = exports.isProxiedCSV = exports.canUseHubApiForItem = exports.isPortal = exports.deriveLocationFromItem = exports.getExtentObject = exports.getContentBoundary = exports.setContentBoundary = void 0;
exports.getMetadataPath = getMetadataPath;
exports.getValueFromMetadata = getValueFromMetadata;
exports.parseISODateString = parseISODateString;
exports.getSchedulerApiUrl = getSchedulerApiUrl;
exports.getHubApiUrlRoot = getHubApiUrlRoot;
/**
 * Use this module for functions that do not need to be used by consumers (yet),
 * but may be shared between hub.js modules, and/or need to be tested
 * to get 100% coverage w/o writing cumbersome tests of higher level functions.
 *
 * Consuming will not be able to import these functions.
 *
 * It's probably a good pattern to add functions here first and then
 * move them to index.ts only when they are needed by a consumer.
 */
const arcgis_rest_feature_service_1 = require("@esri/arcgis-rest-feature-service");
const arcgis_rest_portal_1 = require("@esri/arcgis-rest-portal");
const extent_1 = require("../../extent");
const get_family_1 = require("../get-family");
const get_prop_1 = require("../../objects/get-prop");
const getEnvironmentFromPortalUrl_1 = require("../../utils/getEnvironmentFromPortalUrl");
const arcgis_1 = require("@terraformer/arcgis");
const api_1 = require("../../api");
const isSiteType_1 = require("../isSiteType");
const publisherSource_1 = require("../../core/enums/publisherSource");
const get_item_home_url_1 = require("../../urls/get-item-home-url");
const datePrecision_1 = require("./enums/datePrecision");
/**
 * Hashmap of Hub environment and application url surfix
 */
const envBases = {
    devext: "dev",
    qaext: "qa",
    production: "",
};
/**
 * Create a new content with updated boundary properties
 * @param content original content
 * @param boundary boundary provenance
 * @returns
 * @private
 */
const setContentBoundary = (content, boundary) => {
    // update content's item and boundary
    const properties = Object.assign(Object.assign({}, (content.item.properties || {})), { boundary });
    const item = Object.assign(Object.assign({}, content.item), { properties });
    const updated = Object.assign(Object.assign({}, content), { item });
    return Object.assign(Object.assign({}, updated), { boundary: (0, exports.getContentBoundary)(item) });
};
exports.setContentBoundary = setContentBoundary;
/**
 * get a content's boundary based on the item's boundary property
 * @param item
 * @returns
 * @private
 */
const getContentBoundary = (item) => {
    var _a;
    const bBox = item.extent;
    const isValidItemExtent = (0, extent_1.isBBox)(bBox);
    // user specified provenance is stored in item.properties
    const provenance = ((_a = item.properties) === null || _a === void 0 ? void 0 : _a.boundary) ||
        // but we default to item if the item has an extent
        (isValidItemExtent ? "item" : undefined);
    const boundary = {
        geometry: null,
        provenance,
    };
    if (provenance === "item" && isValidItemExtent) {
        const extent = (0, extent_1.bBoxToExtent)(bBox);
        const center = (0, extent_1.getExtentCenter)(extent);
        boundary.center = [center.x, center.y];
        boundary.geometry = Object.assign(Object.assign({}, (0, extent_1.extentToPolygon)(extent)), { type: "polygon" });
        boundary.spatialReference = boundary.geometry.spatialReference;
    }
    return boundary;
};
exports.getContentBoundary = getContentBoundary;
/**
 * Constructs IExtent from numeric item extent array
 * @param extent Raw item extent array
 * @returns IExtent
 */
const getExtentObject = (extent) => {
    return (0, extent_1.isBBox)(extent)
        ? Object.assign(Object.assign({}, (0, extent_1.bBoxToExtent)(extent)), { type: "extent" })
        : undefined;
};
exports.getExtentObject = getExtentObject;
/**
 * Derives proper IHubLocation given an ArcGIS Item.  If no
 * location (item.properties.location) is present, one will be
 * constructed from the item's extent.
 * @param item ArcGIS Item
 * @returns IHubLocation
 */
const deriveLocationFromItem = (item) => {
    const { properties, extent } = item;
    const location = properties === null || properties === void 0 ? void 0 : properties.location;
    if (location) {
        // IHubLocation already exists, so return it
        return location;
    }
    if ((properties === null || properties === void 0 ? void 0 : properties.boundary) === "none") {
        // Per https://confluencewikidev.esri.com/display/Hub/Hub+Location+Management
        // bounds = 'none' -> specifies not to show on map.  If this is true and
        // no location is already present, opt to not generate location from item extent
        return { type: "none" };
    }
    // IHubLocation does not exist on item properties, so construct it
    // from item extent
    const geometry = (0, exports.getExtentObject)(extent);
    if (geometry) {
        // geometry constructed from bbox
        const type = (0, isSiteType_1.isSiteType)(item.type) ? "org" : "custom";
        return {
            type,
            extent,
            geometries: [geometry],
            spatialReference: geometry.spatialReference,
        };
    }
    else {
        // Could not construct extent object, attempt to construct from geojson
        try {
            // Item extent is supposed to be in WGS84 per item documentation:
            // https://developers.arcgis.com/rest/users-groups-and-items/item.htm
            // But in many situations, this is not the case.  So we do out best to
            // determine the spatial reference of the extent.
            const bbox = (0, extent_1.GeoJSONPolygonToBBox)(extent);
            const defaultSpatialReference = { wkid: 4326 };
            const _geometry = Object.assign(Object.assign({ type: "polygon" }, (0, arcgis_1.geojsonToArcGIS)(extent)), { spatialReference: (0, extent_1.allCoordinatesPossiblyWGS84)(bbox)
                    ? defaultSpatialReference
                    : (0, exports.getItemSpatialReference)(item) || defaultSpatialReference });
            return {
                type: "custom",
                extent: bbox,
                geometries: [_geometry],
                spatialReference: _geometry.spatialReference,
            };
        }
        catch (_a) {
            // Finally, exhausted all options and return a location of type none
            return { type: "none" };
        }
    }
};
exports.deriveLocationFromItem = deriveLocationFromItem;
/**
 * Determine if we are in an enterprise environment
 * NOTE: when no request options are provided, the underlying
 * request functions assume that we are online in production
 * so we only want use enterprise logic if isPortal is explicitly defined
 * @param requestOptions
 * @returns
 * @private
 */
const isPortal = (requestOptions) => {
    return requestOptions && requestOptions.isPortal;
};
exports.isPortal = isPortal;
/**
 * Determine if we can use the Hub API for an item, i.e.
 * the item is public and we are not in an enterprise environment
 * @param item
 * @param requestOptions
 * @returns
 * @private
 */
const canUseHubApiForItem = (item, requestOptions) => {
    return !!item && item.access === "public" && !(0, exports.isPortal)(requestOptions);
};
exports.canUseHubApiForItem = canUseHubApiForItem;
/**
 * Returns whether or not an item is a proxied csv
 *
 * @param item
 * @param requestOptions Hub Request Options (including whether we're in portal)
 * @returns
 * @private
 */
const isProxiedCSV = (item, requestOptions) => !(0, exports.isPortal)(requestOptions) &&
    item.access === "public" &&
    item.type === "CSV" &&
    item.size <= 5000000;
exports.isProxiedCSV = isProxiedCSV;
/**
 * Get the relative URL to use for the item in a hub site
 * @param type
 * @param identifier optional, if not pass, will return a URL to the entities,
 * e.g. /initiatives, /projects
 * NOTE: not all entities have the entities route set up, in that case, we will
 * not return an URL, so they will be redirected back to the site home
 * @param typeKeywords
 * @returns
 * @private
 */
const getHubRelativeUrl = (type, identifier, typeKeywords) => {
    // solution types have their own logic
    let contentUrl = getSolutionUrl(type, identifier, typeKeywords) ||
        getInitiativeTemplateUrl(type, identifier, typeKeywords);
    const family = (0, get_family_1.getFamily)(type);
    if (!contentUrl) {
        const familiesWithPluralizedRoute = [
            "app",
            "dataset",
            "document",
            "map",
            "template",
            "project",
            "initiative",
            "discussion",
            "event",
        ];
        // default to the catchall content route
        let path = "/content";
        // the exception
        if (family === "feedback") {
            path = identifier ? "/feedback/surveys" : "";
        }
        else if ((0, exports.isPageType)(type, typeKeywords)) {
            // pages are in the document family,
            // but instead of showing the page's metadata on /documents/about
            // but we render the page on the pages route
            path = "/pages";
        }
        else if (familiesWithPluralizedRoute.indexOf(family) > -1) {
            // the rule: route name is plural of family name
            path = `/${family}s`;
        }
        contentUrl = identifier ? `${path}/${identifier}` : `${path}`;
    }
    // TODO: once an entity has its entities route set up, add it to this list
    const entitiesHaveEntitiesRoute = ["initiative", "project"];
    // if there is no identifier and the entity does not have the entities route
    // set up, do not return an url
    if (!identifier && !entitiesHaveEntitiesRoute.includes(family)) {
        contentUrl = "";
    }
    return contentUrl;
};
exports.getHubRelativeUrl = getHubRelativeUrl;
/**
 * Is this content type a page?
 * @param type
 * @returns
 * @private
 */
const isPageType = (type, typeKeywords = []) => ["Hub Page", "Site Page"].includes(type) || typeKeywords.includes("hubPage");
exports.isPageType = isPageType;
const getSolutionUrl = (type, identifier, typeKeywords) => {
    let hubUrl;
    if (type === "Solution") {
        // solution types are now in the Template family
        // we send all except the deployed solution items to the route for initiative templates
        if ((typeKeywords === null || typeKeywords === void 0 ? void 0 : typeKeywords.indexOf("Deployed")) > -1) {
            // deployed solutions go to the generic content route
            hubUrl = `/content/${identifier}/about`;
        }
        else {
            hubUrl = `/templates/${identifier}/about`;
        }
    }
    else if (type === "Web Mapping Application" &&
        (typeKeywords === null || typeKeywords === void 0 ? void 0 : typeKeywords.indexOf("hubSolutionTemplate")) > -1) {
        hubUrl = `/templates/${identifier}/about`;
    }
    return hubUrl;
};
const getInitiativeTemplateUrl = (type, identifier, typeKeywords) => {
    if ((type === "Hub Initiative" &&
        (typeKeywords === null || typeKeywords === void 0 ? void 0 : typeKeywords.indexOf("hubInitiativeTemplate")) > -1) ||
        type === "Hub Initiative Template") {
        return `/initiatives/templates/${identifier}/about`;
    }
};
/**
 * Get the path to a well known metadata value
 * @param identifier identifier for well known metadata value
 * @returns path to be used like get(metadata, path)
 * @private
 */
function getMetadataPath(identifier) {
    // NOTE: i have verified that this will work regardless of the "Metadata Style" set on the org
    const metadataPaths = {
        updateFrequency: "metadata.dataIdInfo.resMaint.maintFreq.MaintFreqCd.@_value",
        reviseDate: "metadata.dataIdInfo.idCitation.date.reviseDate",
        pubDate: "metadata.dataIdInfo.idCitation.date.pubDate",
        createDate: "metadata.dataIdInfo.idCitation.date.createDate",
        metadataUpdateFrequency: "metadata.mdMaint.maintFreq.MaintFreqCd.@_value",
        metadataUpdatedDate: "metadata.mdDateSt",
    };
    return metadataPaths[identifier];
}
/**
 * Get a well known value from metadata
 * @param metadata
 * @param identifier identifier for well known metadata value
 * @returns
 * @private
 */
function getValueFromMetadata(metadata, identifier) {
    const path = getMetadataPath(identifier);
    return path && (0, get_prop_1.getProp)(metadata, path);
}
/**
 * Parses an ISO8601 date string into a date and a precision.
 * This is because a) if somone entered 2018, we want to respect that and not treat it as the same as 2018-01-01
 * and b) you cannot naively call new Date with an ISO 8601 string that does not include time information
 * For example, when I, here in mountain time, do new Date('2018').getFullYear() I get "2017".
 * This is because when you do not provide time or timezone info, UTC is assumed, so new Date('2018') is 2018-01-01T00:00:00 in UTC
 * which is actually 7 hours earlier here in mountain time.
 *
 * @param {string} isoString
 * @return { date: Date, precision: DatePrecision }
 * @private
 */
function parseISODateString(isoString) {
    isoString = `${isoString}`;
    let date;
    let precision;
    if (/^\d{4}$/.test(isoString)) {
        // yyyy
        date = new Date(+isoString, 0, 1);
        precision = datePrecision_1.DatePrecision.Year;
    }
    else if (/^\d{4}-\d{1,2}$/.test(isoString)) {
        // yyyy-mm
        const parts = isoString.split("-");
        date = new Date(+parts[0], +parts[1] - 1, 1);
        precision = datePrecision_1.DatePrecision.Month;
    }
    else if (/^\d{4}-\d{1,2}-\d{1,2}$/.test(isoString)) {
        // yyyy-mm-dd
        const parts = isoString.split("-");
        date = new Date(+parts[0], +parts[1] - 1, +parts[2]);
        precision = datePrecision_1.DatePrecision.Day;
    }
    else if (!Number.isNaN(Date.parse(isoString))) {
        // any other string parsable to a valid date
        date = new Date(isoString);
        precision = isoString.includes("T")
            ? datePrecision_1.DatePrecision.Time
            : datePrecision_1.DatePrecision.Day;
    }
    return date && precision && { date, precision };
}
// NOTE: IItem has spatialRefernce: ISpatialReference, but
// the portal REST API returns spatialReference as as string
// that is always either WKID like "102100" or the name of a
// WKT like "NAD_1983_HARN_StatePlane_Hawaii_3_FIPS_5103_Feet".
// We only coerce WKIDs into a ISpatialReference objects since we
// can't easily lookup a complete WKT.
/**
 * Get the spatial reference as an object for an item
 * @param item
 * @returns spatial reference object
 * @private
 */
const getItemSpatialReference = (item) => {
    const spatialReference = item.spatialReference;
    if (!spatialReference || typeof spatialReference === "object") {
        // no need to try and transform this into an ISpatialReference
        return spatialReference;
    }
    // otherwise it _should_ be a string (if coming form the REST API)
    // but we force it in case it was set to a number somewhere outside of TS
    const spatialReferenceString = spatialReference + "";
    const wkid = parseInt(spatialReferenceString, 10);
    return isNaN(wkid)
        ? // It looks like the portal api returns the name of a WKT, but we'd
            // need to perform a lookup to get the full WKT. Return null for now.
            null
        : //
            { wkid };
};
exports.getItemSpatialReference = getItemSpatialReference;
/**
 * Extracts additional resources from the provided metadata
 * and transforms them into a hub-friendly format.
 *
 * Returns null if no resources are available
 *
 * @param item
 * @param metadata formal metadata
 * @returns
 * @private
 */
const getAdditionalResources = (item, metadata, requestOptions) => {
    const rawResources = (0, exports.extractRawResources)(metadata);
    return (rawResources &&
        rawResources.map((resource) => ({
            name: resource.orName,
            url: (0, exports.getAdditionalResourceUrl)(resource, item, requestOptions),
            isDataSource: (0, exports.isDataSourceOfItem)(resource, item),
        })));
};
exports.getAdditionalResources = getAdditionalResources;
/**
 * @private
 *
 * Extracts additional resources from formal item metadata.
 * If none are available, null is returned.
 *
 * @param metadata the formal item metadata
 * @returns an array of all additional resources, or null
 */
const extractRawResources = (metadata) => {
    const rawResources = [];
    // The property path to additional resources should be fairly simple.
    // In many cases, it's just `metadata.metadata.distInfo.distTranOps.onLineSrc`.
    // However, since `distInfo`, `distTranOps` and `onLineSrc` can be either
    // Objects OR Arrays, we have to do all this looping.
    castToArray((0, get_prop_1.getProp)(metadata, "metadata.distInfo") || []).forEach((distInfo) => {
        castToArray(distInfo.distTranOps || []).forEach((distTranOps) => {
            castToArray(distTranOps.onLineSrc || []).forEach((onLineSrc) => {
                rawResources.push(onLineSrc);
            });
        });
    });
    return rawResources.length ? rawResources : null;
};
exports.extractRawResources = extractRawResources;
/**
 * @private
 *
 * Arrays are returned as-is.
 * Objects are wrapped into a 1 element array.
 *
 * @param objectOrArray
 * @returns the casted array
 */
const castToArray = (objectOrArray) => {
    return Array.isArray(objectOrArray) ? objectOrArray : [objectOrArray];
};
/**
 * Determines whether a raw additional resource (i.e. extracted out of formal
 * metadata with no transformation) references the underlying service that backs
 * the item.
 *
 * @param resource raw additional resource of an item
 * @param item
 * @returns
 * @private
 */
const isDataSourceOfItem = (resource, item) => {
    const serviceUrl = item.url && (0, arcgis_rest_feature_service_1.parseServiceUrl)(item.url);
    return (serviceUrl && resource.linkage && resource.linkage.includes(serviceUrl));
};
exports.isDataSourceOfItem = isDataSourceOfItem;
/**
 * Returns the url for an additional resource.
 *
 * Automatically appends auth token if token is available
 * and resource points to the backing service of an item.
 *
 * @param resource raw additional resource of an item
 * @param item
 * @param requestOptions IHubRequestOptions, including authentication
 * @returns
 * @private
 */
const getAdditionalResourceUrl = (resource, item, requestOptions) => {
    let result = resource.linkage;
    const token = (0, get_prop_1.getProp)(requestOptions, "authentication.token");
    if (token && (0, exports.isDataSourceOfItem)(resource, item)) {
        const resUrl = new URL(resource.linkage);
        const params = new URLSearchParams(resUrl.search);
        params.set("token", token);
        resUrl.search = params.toString();
        result = resUrl.toString();
    }
    return result;
};
exports.getAdditionalResourceUrl = getAdditionalResourceUrl;
/**
 * @private
 *
 * Contains fallback logic for determining a content's extent.
 *
 * The fallback priority is as follows:
 * 1) item's extent (if valid bbox)
 * 2) extent enrichment from the hub api (if coordinates are valid bbox)
 * 3) layer's extent (if spatial reference is 4326)
 *
 * If none of these conditions are met, undefined is returned.
 *
 * @param item
 * @param layer
 * @param extentEnrichment
 * @returns the correct extent in a bbox format, or undefined
 */
const determineExtent = (item, extentEnrichment, layer) => {
    const itemExtent = (0, extent_1.isBBox)(item.extent) ? item.extent : undefined;
    const extentEnrichmentCoordinates = (0, extent_1.isBBox)(extentEnrichment === null || extentEnrichment === void 0 ? void 0 : extentEnrichment.coordinates)
        ? extentEnrichment.coordinates
        : undefined;
    const layerExtent = (0, get_prop_1.getProp)(layer, "extent.spatialReference.wkid") === 4326
        ? (0, extent_1.extentToBBox)(layer.extent)
        : undefined;
    return itemExtent || extentEnrichmentCoordinates || layerExtent;
};
exports.determineExtent = determineExtent;
/**
 * @private
 *
 * Extracts the first contact from a given formal item metadata path.
 * This is particularly helpful if the contact path is either an object or an array.
 *
 * Note: the raw contact object must have the following properties:
 * - `rpIndName`: name of the individual
 * - `rpOrgName`: name of the individual's organization
 *
 * @param metadata formal item metadata
 * @param path path to the contact object/array
 * @returns
 */
const extractFirstContact = (metadata, path) => {
    const rawContacts = (0, get_prop_1.getProp)(metadata, path) || {};
    const { rpIndName, rpOrgName } = Array.isArray(rawContacts)
        ? rawContacts[0]
        : rawContacts;
    return { individualName: rpIndName, organizationName: rpOrgName };
};
exports.extractFirstContact = extractFirstContact;
/**
 * Determines the correct orgId for an item.
 * Note: it's undocumented, but the portal API will return orgId for items... sometimes.
 *
 * @param item
 * @param ownerUser item owner's hydrated user object
 */
const getItemOrgId = (item, ownerUser) => item.orgId || (ownerUser === null || ownerUser === void 0 ? void 0 : ownerUser.orgId);
exports.getItemOrgId = getItemOrgId;
/**
 * Calculates the Publisher display info for the given item.
 * Utilizes this fallback pattern:
 * 1) Formal Item Metadata > Resource > Citation > Contact
 * 2) Formal Item Metadata > Resource > Contact
 * 3) Item’s Owner and Org Name
 * 4) Undefined (Item Owner / Org are private and we can't access additional info)
 *
 * @param item
 * @param metadata
 * @param org portal info of the item's organization
 * @param ownerUser the item owner's hydrated user
 * @returns
 */
const getPublisherInfo = (item, metadata, org, ownerUser) => {
    const result = {
        nameSource: publisherSource_1.PublisherSource.None,
        organizationSource: publisherSource_1.PublisherSource.None,
    };
    const citationContact = (0, exports.extractFirstContact)(metadata, "metadata.dataIdInfo.idCitation.citRespParty");
    const resourceContact = (0, exports.extractFirstContact)(metadata, "metadata.dataIdInfo.idPoC");
    const metadataContact = (0, exports.extractFirstContact)(metadata, "metadata.mdContact");
    // Determine publisher name properties
    const ownerFullName = (0, get_prop_1.getProp)(ownerUser, "fullName");
    if (citationContact.individualName) {
        result.name = citationContact.individualName;
        result.nameSource = publisherSource_1.PublisherSource.CitationContact;
    }
    else if (resourceContact.individualName) {
        result.name = resourceContact.individualName;
        result.nameSource = publisherSource_1.PublisherSource.ResourceContact;
    }
    else if (metadataContact.individualName) {
        result.name = metadataContact.individualName;
        result.nameSource = publisherSource_1.PublisherSource.MetadataContact;
    }
    else if (ownerFullName) {
        result.name = ownerFullName;
        result.username = ownerUser.username;
        result.nameSource = publisherSource_1.PublisherSource.ItemOwner;
    }
    // Determine publisher org properties
    const orgName = (0, get_prop_1.getProp)(org, "name");
    if (citationContact.organizationName) {
        result.organization = citationContact.organizationName;
        result.organizationSource = publisherSource_1.PublisherSource.CitationContact;
    }
    else if (resourceContact.organizationName) {
        result.organization = resourceContact.organizationName;
        result.organizationSource = publisherSource_1.PublisherSource.ResourceContact;
    }
    else if (metadataContact.organizationName) {
        result.organization = metadataContact.organizationName;
        result.organizationSource = publisherSource_1.PublisherSource.MetadataContact;
    }
    else if (orgName) {
        result.organization = orgName;
        result.orgId = (0, exports.getItemOrgId)(item, ownerUser);
        result.organizationSource = publisherSource_1.PublisherSource.ItemOwner;
    }
    // We assume the item belongs to external org if no org info is available and the item is private
    result.isExternal =
        result.organizationSource === publisherSource_1.PublisherSource.None &&
            item.access !== "public";
    return result;
};
exports.getPublisherInfo = getPublisherInfo;
/**
 * returns the last section of each category path.
 * Example: "/categories/parent/child" > "child"
 *
 * @param categories an item's categories
 */
const getShortenedCategories = (categories) => {
    return categories.reduce((acc, category) => {
        const segments = category.split("/");
        const shortenedCategory = segments[segments.length - 1];
        // if you are updating this file, remove this comment and address the lint error
        // eslint-disable-next-line @typescript-eslint/no-unused-expressions
        shortenedCategory && acc.push(shortenedCategory);
        return acc;
    }, []);
};
exports.getShortenedCategories = getShortenedCategories;
/**
 * Returns URL to edit content of application type
 *
 * TODO: implement logic to compute edit URL for
 * Dataset, project, document, templates
 *
 * Logic copied from getEditUrl()
 * https://github.com/ArcGIS/opendata-ui/blob/9442a7a26ddde117bdaa747f60e2ef61f5163896/packages/ember-arcgis-opendata-components/addon/services/solutions-service.js#L1340
 *
 * @param item
 * @param requestOptions
 */
const getContentEditUrl = (item, requestOptions) => {
    let contentEditUrl;
    // TODO: edit URL for Dataset, project, document, templates
    switch (getNormalizedAppItemType(item)) {
        case "Dashboard":
            contentEditUrl = getDashboardEditUrl(item, requestOptions);
            break;
        case "Insights Workbook":
            contentEditUrl = getInsightsEditUrl(item, requestOptions);
            break;
        case "Experience":
            contentEditUrl = getExperienceBuilderEditUrl(item, requestOptions);
            break;
        case "Web AppBuilder":
            contentEditUrl = getWebAppBuilderEditUrl(item, requestOptions);
            break;
        case "Form":
            contentEditUrl = getSurvey123EditUrl(item, requestOptions);
            break;
        case "StoryMap":
            contentEditUrl = getStoryMapEditUrl(item, requestOptions);
            break;
        case "Urban Model":
            contentEditUrl = getUrbanModelEditUrl(item, requestOptions);
            break;
        default:
            contentEditUrl = (0, get_item_home_url_1.getItemHomeUrl)(item.id, requestOptions);
    }
    return contentEditUrl;
};
exports.getContentEditUrl = getContentEditUrl;
/**
 * returns normalized application item type
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/842685ab715209a4793a7a4cb752bd37225c16ec/packages/ember-arcgis-hub-components/addon/utils/item-urls.js#L132
 *
 * @param item
 */
const getNormalizedAppItemType = (item) => {
    const typeKeywords = item.typeKeywords || [];
    let result = item.type;
    if (item.type === "StoryMap" ||
        (item.type === "Web Mapping Application" &&
            typeKeywords.includes("Story Map"))) {
        result = "StoryMap";
    }
    else if (item.type === "Insights Page") {
        result = "Insights Page";
    }
    else if (item.type === "Insights Workbook") {
        result = "Insights Workbook";
    }
    else if (item.type && item.type.includes("Insights")) {
        result = "Insights";
    }
    else if (item.type === "Web Experience" ||
        (item.type === "Web Mapping Experience" &&
            typeKeywords.includes("EXB Experience"))) {
        result = "Experience";
    }
    else if (typeKeywords.includes("Web AppBuilder")) {
        result = "Web AppBuilder";
    }
    return result;
};
/**
 * returns url to edit Story map application
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/842685ab715209a4793a7a4cb752bd37225c16ec/packages/ember-arcgis-opendata-components/addon/services/storymap-service.js#L289
 *
 * @param item
 * @param requestOptions
 */
const getStoryMapEditUrl = (item, requestOptions) => {
    const storyMapsBases = {
        devext: "storymapsdev",
        qaext: "storymapsqa",
        production: "storymaps",
    };
    const portalUrl = (0, arcgis_rest_portal_1.getPortalUrl)(requestOptions);
    const env = (0, getEnvironmentFromPortalUrl_1.getEnvironmentFromPortalUrl)(portalUrl);
    const storyMapUrl = `https://${storyMapsBases[env]}.arcgis.com`;
    return `${storyMapUrl}/stories/${item.id}/edit`;
};
/**
 * returns URL to edit Experience builder application
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/842685ab715209a4793a7a4cb752bd37225c16ec/packages/ember-arcgis-opendata-components/addon/services/experience-service.js#L227
 *
 * @param item
 * @param requestOptions
 */
const getExperienceBuilderEditUrl = (item, requestOptions) => {
    const portalUrl = (0, arcgis_rest_portal_1.getPortalUrl)(requestOptions);
    const env = (0, getEnvironmentFromPortalUrl_1.getEnvironmentFromPortalUrl)(portalUrl);
    const experienceBuilderUrl = `https://experience${envBases[env]}.arcgis.com`;
    return `${experienceBuilderUrl}/builder/?id=${item.id}`;
};
/**
 * returns URL to edit Dashboard application
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/842685ab715209a4793a7a4cb752bd37225c16ec/packages/ember-arcgis-opendata-components/addon/services/dashboard-service.js#L357
 *
 * @param item
 * @param requestOptions
 */
const getDashboardEditUrl = (item, requestOptions) => {
    const portalUrl = (0, arcgis_rest_portal_1.getPortalUrl)(requestOptions);
    let url = `${portalUrl}/apps/opsdashboard/index.html#/${item.id}?mode=edit`;
    if (item.typeKeywords && item.typeKeywords.includes("ArcGIS Dashboards")) {
        url = `${portalUrl}/apps/dashboards/${item.id}#mode=edit`;
    }
    return url;
};
/**
 * returns URL to edit Web application builder
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/842685ab715209a4793a7a4cb752bd37225c16ec/packages/ember-arcgis-opendata-components/addon/services/webapp-service.js#L359
 *
 * @param item
 * @param requestOptions
 */
const getWebAppBuilderEditUrl = (item, requestOptions) => {
    const portalUrl = (0, arcgis_rest_portal_1.getPortalUrl)(requestOptions);
    // TODO: handle special cases for economic-development app and enterprise
    return `${portalUrl}/apps/webappbuilder/index.html?id=${item.id}`;
};
/**
 * returns edit URL for Survey123 application item
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/842685ab715209a4793a7a4cb752bd37225c16ec/packages/ember-arcgis-opendata-components/addon/services/survey-service.js#L1014
 *
 * @param item
 * @param requestOptions
 */
const getSurvey123EditUrl = (item, requestOptions) => {
    const portalUrl = (0, arcgis_rest_portal_1.getPortalUrl)(requestOptions);
    const env = (0, getEnvironmentFromPortalUrl_1.getEnvironmentFromPortalUrl)(portalUrl);
    const survey123Url = `https://survey123${envBases[env]}.arcgis.com`;
    let surver123EditUrl;
    // TODO: handle cases for enterprise and Hub feedback relative URL
    if (item.typeKeywords.includes("Survey123 Connect")) {
        surver123EditUrl = (0, get_item_home_url_1.getItemHomeUrl)(item.id, portalUrl);
    }
    else {
        surver123EditUrl = `${survey123Url}/surveys/${item.id}/design?portalUrl=${portalUrl}`;
    }
    return surver123EditUrl;
};
/**
 * returns URL to edit Insights application
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/9442a7a26ddde117bdaa747f60e2ef61f5163896/packages/ember-arcgis-opendata-components/addon/services/solutions-service.js#L1366-L1369
 *
 * @param item
 * @param requestOptions
 */
const getInsightsEditUrl = (item, requestOptions) => {
    const portalUrl = (0, arcgis_rest_portal_1.getPortalUrl)(requestOptions);
    const env = (0, getEnvironmentFromPortalUrl_1.getEnvironmentFromPortalUrl)(portalUrl);
    const insightsUrl = `https://insights${envBases[env]}.arcgis.com`;
    const itemHomeUrl = (0, get_item_home_url_1.getItemHomeUrl)(item.id, portalUrl);
    return isPortalFromUrl(portalUrl)
        ? itemHomeUrl
        : `${insightsUrl}/#/edit/${item.id}`;
};
/**
 * returns URL to edit Urban Model application
 *
 * Logic copied from
 * https://github.com/ArcGIS/opendata-ui/blob/9442a7a26ddde117bdaa747f60e2ef61f5163896/packages/ember-arcgis-opendata-components/addon/services/solutions-service.js#L1370-L1373
 *
 * @param item
 * @param requestOptions
 */
const getUrbanModelEditUrl = (item, requestOptions) => {
    const portalUrl = (0, arcgis_rest_portal_1.getPortalUrl)(requestOptions);
    const env = (0, getEnvironmentFromPortalUrl_1.getEnvironmentFromPortalUrl)(portalUrl);
    const urbanModelUrl = `https://urban${envBases[env]}.arcgis.com`;
    const itemHomeUrl = (0, get_item_home_url_1.getItemHomeUrl)(item.id, portalUrl);
    return isPortalFromUrl(portalUrl)
        ? itemHomeUrl
        : `${urbanModelUrl}/?id=${item.id}`;
};
const isPortalFromUrl = (portalUrl) => {
    return portalUrl.indexOf("arcgis.com") === -1;
};
function getSchedulerApiUrl(itemId, requestOptions) {
    const hubApiUrlRoot = getHubApiUrlRoot(requestOptions);
    return `${hubApiUrlRoot}/api/download/v1/items/${itemId}/schedule?token=${requestOptions.authentication.token}`;
}
function getHubApiUrlRoot(requestOptions) {
    // sometimes the url has /api/v3 at the end, so we need to remove it
    const hubApiUrlWithVersion = (0, api_1.getHubApiUrl)(requestOptions);
    return hubApiUrlWithVersion.replace(/\/api\/v3$/, "");
}
const forceUpdateContent = async (itemId, requestOptions) => {
    const hubApiUrlRoot = (0, api_1.getHubApiUrl)(requestOptions);
    const url = `${hubApiUrlRoot}/api/v3/jobs/item/${itemId}/forceUpdate`;
    const options = {
        method: "POST",
        headers: {
            "content-type": "application/json",
            authorization: requestOptions.authentication.token,
        },
    };
    const response = await fetch(url, options);
    return response.ok;
};
exports.forceUpdateContent = forceUpdateContent;
//# sourceMappingURL=internalContentUtils.js.map