"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ArcGISContext = exports.ENTERPRISE_HOME_SUBDOMAIN = exports.ENTERPRISE_SITES_PATH = void 0;
const arcgis_rest_portal_1 = require("@esri/arcgis-rest-portal");
const hubHistory_1 = require("./core/hubHistory");
const getEnvironmentFromPortalUrl_1 = require("./utils/getEnvironmentFromPortalUrl");
const hubUserAppResources_1 = require("./utils/hubUserAppResources");
const checkPermission_1 = require("./permissions/checkPermission");
const get_org_thumbnail_url_1 = require("./resources/get-org-thumbnail-url");
const get_prop_1 = require("./objects/get-prop");
const get_with_default_1 = require("./objects/get-with-default");
exports.ENTERPRISE_SITES_PATH = "/apps/sites";
exports.ENTERPRISE_HOME_SUBDOMAIN = "home";
/**
 * Hash of Hub API end points so updates
 * are centralized
 */
const hubApiEndpoints = {
    domains: "/api/v3/domains",
    search: "/api/v3/datasets",
    discussions: "/api/discussions/v1",
    ogcRecords: "/api/search/v1",
};
/**
 * Per-environment Group Ids that contain documents/links to
 * Hub Resources (help docs, blog posts etc)
 */
const HUB_RESOURE_GROUPS = {
    qaext: [
        "da45c26e67764c79840928cd8d05561a", // owner: dcadminqa
    ],
    devext: [], // EMPTY
    production: ["e3db35f7de63451f8243415445694761"],
    enterprise: [], // EMPTY
    "enterprise-k8s": [], // EMPTY
};
/**
 * Abstraction that holds a `ArcGISIdentityManager`, along with
 * getters to streamline access to various platform
 * urls, and common constructs like `IRequestOptions`,
 * `IUserRequestOptions` etc.
 *
 * Instances are intended to be immutable, but this is not directly enforced.
 *
 * In most circumstances, this class should be created by
 * the ArcGISContextManager class.
 */
class ArcGISContext {
    /**
     * Create a new instance of `ArcGISContext`.
     *
     * @param opts
     */
    constructor(opts) {
        this._portalUrl = "https://www.arcgis.com";
        this._featureFlags = {};
        this._trustedOrgIds = [];
        this._trustedOrgs = [];
        this._userResourceTokens = [];
        this.id = opts.id;
        this._portalUrl = opts.portalUrl;
        this._hubUrl = opts.hubUrl;
        this._serviceStatus = opts.serviceStatus;
        if (opts.authentication) {
            this._authentication = opts.authentication;
        }
        if (opts.portalSelf) {
            this._portalSelf = opts.portalSelf;
        }
        if (opts.portalSettings) {
            this._portalSettings = opts.portalSettings;
        }
        if (opts.currentUser) {
            this._currentUser = opts.currentUser;
        }
        if (opts.properties) {
            this._properties = opts.properties;
        }
        if (opts.trustedOrgIds) {
            this._trustedOrgIds = opts.trustedOrgIds;
        }
        if (opts.trustedOrgs) {
            this._trustedOrgs = opts.trustedOrgs;
        }
        this._featureFlags = opts.featureFlags || {};
        this._userResourceTokens = opts.userResourceTokens || [];
        this._userHubSettings = opts.userHubSettings || {
            schemaVersion: 1,
        };
    }
    /**
     * Return the ArcGISIdentityManager if authenticated
     */
    get session() {
        return this._authentication;
    }
    /**
     * Return boolean indicating if authenticatio is present
     */
    get isAuthenticated() {
        return !!this._authentication;
    }
    /**
     * Return hash of feature flags passed into constructor.
     * Default is empty object.
     */
    get featureFlags() {
        return this._featureFlags;
    }
    /**
     * Is the users org in the alpha orgs list?
     * Alpha orgs are passed in via properties.alphaOrgs
     */
    get isAlphaOrg() {
        var _a, _b;
        let result = false;
        const orgs = ((_a = this._properties) === null || _a === void 0 ? void 0 : _a.alphaOrgs) || [];
        const orgId = (_b = this._portalSelf) === null || _b === void 0 ? void 0 : _b.id;
        if (orgs.length && orgId) {
            result = orgs.includes(orgId);
        }
        return result;
    }
    /**
     * Is the users org in the beta orgs list?
     * Beta orgs are passed in via properties.betaOrgs
     */
    get isBetaOrg() {
        var _a, _b;
        let result = false;
        const orgs = ((_a = this._properties) === null || _a === void 0 ? void 0 : _a.betaOrgs) || [];
        const orgId = (_b = this._portalSelf) === null || _b === void 0 ? void 0 : _b.id;
        if (orgs.length && orgId) {
            result = orgs.includes(orgId);
        }
        return result;
    }
    /**
     * Return the HubEnvironment of the current context
     */
    get environment() {
        return (0, getEnvironmentFromPortalUrl_1.getEnvironmentFromPortalUrl)(this._portalUrl);
    }
    /**
     * Return `IUserRequestOptions`, which is used for REST-JS
     * functions which require authentication information.
     *
     * If context is not authenticated, this function will throw
     */
    get userRequestOptions() {
        if (this.isAuthenticated) {
            return {
                authentication: this._authentication,
                portal: this.sharingApiUrl,
            };
        }
    }
    /**
     * Return `IRequestOptions`, which is used by REST-JS functions
     * which *may* use authentication information if provided.
     *
     * If context is not authenticated, this function just returns
     * the `portal` property, which informs REST-JS what Sharing API
     * instance to use (i.e. AGO, Enterprise etc)
     */
    get requestOptions() {
        let ro = {
            portal: this.sharingApiUrl,
        };
        if (this.isAuthenticated) {
            ro = {
                authentication: this._authentication,
                portal: this.sharingApiUrl,
            };
        }
        return ro;
    }
    /**
     * Return a `IHubRequestOptions` object
     */
    get hubRequestOptions() {
        // We may add more logic around what is returned in some corner cases
        return {
            authentication: this.session,
            isPortal: this.isPortal,
            portalSelf: this.portal,
            hubApiUrl: this.hubUrl,
            portal: this.sharingApiUrl,
        };
    }
    /**
     * Return the portal url i.e. https://www.arcgis.com
     *
     * If authenticated @ ArcGIS Online, it will return
     * the https://org.env.arcgis.com
     *
     * If authenticated @ ArcGIS Enterprise, it will return
     * https://{portalHostname}/{webadaptor}
     */
    get portalUrl() {
        if (this.isAuthenticated) {
            if (this.isPortal || !this._portalSelf.urlKey) {
                return `https://${this._portalSelf.portalHostname}`;
            }
            else {
                return `https://${this._portalSelf.urlKey}.${this._portalSelf.customBaseUrl}`;
            }
        }
        else {
            return this._portalUrl;
        }
    }
    /**
     * Returns the current user's hub-home url. If not authenticated,
     * returns the Hub Url.
     */
    get hubHomeUrl() {
        if (this.isPortal) {
            return `${this.portalUrl}${exports.ENTERPRISE_SITES_PATH}/#/${exports.ENTERPRISE_HOME_SUBDOMAIN}`;
        }
        else {
            if (this.isAuthenticated) {
                const hubHostname = this._hubUrl.replace("https://", "");
                return `https://${this._portalSelf.urlKey}.${hubHostname}`;
            }
            else {
                return this._hubUrl;
            }
        }
    }
    /**
     * Returns the current user's Hub License
     */
    get hubLicense() {
        if (this.isPortal) {
            return "enterprise-sites";
        }
        else {
            if (this.hubEnabled) {
                return "hub-premium";
            }
            else {
                return "hub-basic";
            }
        }
    }
    /**
     * Returns the current hub service status information
     */
    get serviceStatus() {
        return this._serviceStatus;
    }
    /**
     * Returns the url to the sharing api composed from portalUrl
     * i.e. https://myorg.maps.arcgis.com/sharing/rest
     */
    get sharingApiUrl() {
        return `${this.portalUrl}/sharing/rest`;
    }
    /**
     * Returns the Hub url, based on the portalUrl
     *
     * For ArcGIS Enterprise this will return `undefined`
     */
    get hubUrl() {
        return this._hubUrl;
    }
    /**
     * Returns boolean indicating if the backing system
     * is ArcGIS Enterprise (formerly ArcGIS Portal) or not
     */
    get isPortal() {
        return this._portalSelf
            ? this._portalSelf.isPortal
            : this._portalUrl.indexOf("arcgis.com") === -1;
    }
    /**
     * Returns the discussions API URL
     */
    get discussionsServiceUrl() {
        if (this._hubUrl) {
            return `${this._hubUrl}${hubApiEndpoints.discussions}`;
        }
    }
    /**
     * Returns the Hub Search API URL
     */
    get hubSearchServiceUrl() {
        if (this._hubUrl) {
            return `${this._hubUrl}${hubApiEndpoints.search}`;
        }
    }
    /**
     * Returns Hub Domain Service URL
     */
    get domainServiceUrl() {
        if (this._hubUrl) {
            return `${this._hubUrl}${hubApiEndpoints.domains}`;
        }
    }
    /**
     * Returns Hub OGCAPI Service URL
     */
    get ogcApiUrl() {
        if (this._hubUrl) {
            // NOTE: In the future, we will be able to use _hubUrl directly
            // but for now, we swap "hub" to "opendata" in _hubUrl so we end
            // up with urls like https://opendataqa.arcgis.com/api/search/v1
            const opendataUrl = this._hubUrl.replace("hub", "opendata");
            return `${opendataUrl}${hubApiEndpoints.ogcRecords}`;
        }
    }
    /**
     * Returns the Events configuration object from portal/self
     *
     * `{serviceId: '3ef..', publicViewId: 'bc3...'}`
     */
    get eventsConfig() {
        if (this._portalSelf) {
            return (0, get_prop_1.getProp)(this._portalSelf, "portalProperties.hub.settings.events");
        }
    }
    /**
     * Returns boolean indicating if the current user
     * belongs to an organization that has licensed
     * ArcGIS Hub
     */
    get hubEnabled() {
        return (0, get_with_default_1.getWithDefault)(this._portalSelf, "portalProperties.hub.enabled", false);
    }
    /**
     * Return the Hub Community Org Id, if defined
     */
    get communityOrgId() {
        if (this._portalSelf) {
            return (0, get_prop_1.getProp)(this._portalSelf, "portalProperties.hub.settings.communityOrg.orgId");
        }
    }
    /**
     * If we are in a community org with an associated e-org
     * Return the Hub Enterprise Org Id, if defined
     */
    get enterpriseOrgId() {
        if (this._portalSelf) {
            return (0, get_prop_1.getProp)(this._portalSelf, "portalProperties.hub.settings.enterpriseOrg.orgId");
        }
    }
    /**
     * Returns the Hub Community Org Hostname, if defined
     *
     * i.e. c-org.maps.arcgis.com
     */
    get communityOrgHostname() {
        if (this._portalSelf) {
            return (0, get_prop_1.getProp)(this._portalSelf, "portalProperties.hub.settings.communityOrg.portalHostname");
        }
    }
    /**
     * Returns the Hub Community Org url
     *
     * i.e. https://c-org.maps.arcgis.com
     */
    get communityOrgUrl() {
        if (this.communityOrgHostname) {
            return `https://${this.communityOrgHostname}`;
        }
    }
    /**
     * Returns the hash of helper services from portal self
     */
    get helperServices() {
        if (this._portalSelf) {
            return this._portalSelf.helperServices;
        }
    }
    /**
     * Returns the current user as IUser
     */
    get currentUser() {
        return this._currentUser;
    }
    /**
     * Returns the portal object as IPortal
     */
    get portal() {
        return this._portalSelf;
    }
    /**
     * Returns the portal settings object as IPortalSettings
     */
    get portalSettings() {
        return this._portalSettings;
    }
    /**
     * Return the properties hash that was passed in.
     * Useful for app-specific context such as the active
     * Site for ArcGIS Hub
     */
    get properties() {
        return this._properties;
    }
    /**
     * Returns the array of Trusted Org Ids
     */
    get trustedOrgIds() {
        return this._trustedOrgIds;
    }
    /**
     * Returns the array of Trusted Orgs
     */
    get trustedOrgs() {
        return this._trustedOrgs;
    }
    /**
     * Returns whether the current user's org type is a community org
     */
    get isCommunityOrg() {
        let result = false;
        if (this._portalSelf) {
            const orgType = (0, get_prop_1.getProp)(this._portalSelf, "portalProperties.hub.settings.orgType");
            result = orgType === "community";
        }
        return result;
    }
    /**
     * Returns whether the current user is an org admin and not in a custom role.
     */
    get isOrgAdmin() {
        const { _currentUser } = this;
        let result = false;
        if (_currentUser) {
            result = _currentUser.role === "org_admin" && !_currentUser.roleId;
        }
        return result;
    }
    /**
     * Return the whole array of user resource tokens
     */
    get userResourceTokens() {
        return this._userResourceTokens;
    }
    /**
     * Return the user hub settings.
     * Updates must be done via `contextManager.updateUserHubSettings`
     */
    get userHubSettings() {
        return this._userHubSettings;
    }
    get orgThumbnailUrl() {
        var _a;
        return (0, get_org_thumbnail_url_1.getOrgThumbnailUrl)(this.portal, (_a = this.session) === null || _a === void 0 ? void 0 : _a.token);
    }
    /**
     * Return the survey123 url
     */
    get survey123Url() {
        var _a;
        const suffixes = {
            qaext: "qa",
            devext: "dev",
        };
        const suffix = (_a = suffixes[this.environment]) !== null && _a !== void 0 ? _a : "";
        return `https://survey123${suffix}.arcgis.com`;
    }
    /**
     * Return a token for a specific app
     * @param app
     * @returns
     */
    tokenFor(app) {
        const entry = this._userResourceTokens.find((e) => e.app === app);
        if (entry) {
            return entry.token;
        }
    }
    /**
     * Re-fetch the current user, including their groups
     * @returns
     */
    refreshUser() {
        const opts = {
            authentication: this.session,
            portal: this.sharingApiUrl,
            username: this.session.username,
        };
        return (0, arcgis_rest_portal_1.getUser)(opts).then((user) => {
            this._currentUser = user;
        });
    }
    /**
     * Return the user's history
     * @returns
     */
    get history() {
        return this._userHubSettings.history || { entries: [] };
    }
    /**
     * Return an array of GroupIds, per-environment, that contain
     * Hub Resources (linkable documents etc) that are tagged to appear
     * in different areas of the appliation.
     */
    get resourceGroupIDs() {
        return HUB_RESOURE_GROUPS[this.environment];
    }
    /**
     * Check specific permission for the current user, and optionally an entity
     * @param permission
     * @param entity
     * @returns
     */
    checkPermission(permission, entity) {
        return (0, checkPermission_1.checkPermission)(permission, this, entity);
    }
    /**
     * Add an entry to the user's history
     * @param entry
     * @param win
     * @returns
     */
    async addToHistory(entry) {
        // No-op if not authenticated
        if (!this.isAuthenticated) {
            return;
        }
        // No-op if the user doesn't have the permission
        const chk = this.checkPermission("hub:feature:history");
        if (!chk.access) {
            return;
        }
        // add the entry to the history
        const updated = (0, hubHistory_1.addHistoryEntry)(entry, this.history);
        // The getter reads from this, so just re-assign
        this._userHubSettings.history = updated;
        // update the user-app-resource
        return this.updateUserHubSettings(this._userHubSettings);
        // --------------------------------------------------------------------
        // Turns out that integrating this with LocalStorage will involve
        // a lot of syncronization as auth'd user moves between sites
        // which may have different history states in localStorage
        // So let's start with just tracking the history
        // in workspaces and see how that goes.
        // --------------------------------------------------------------------
    }
    /**
     * Clear the entire history
     * @returns
     */
    async clearHistory() {
        // No-op if not authenticated
        if (!this.isAuthenticated) {
            return;
        }
        // No-op if the user doesn't have the permission
        const chk = this.checkPermission("hub:feature:history");
        if (!chk.access) {
            return;
        }
        // create a new history object
        const history = {
            entries: [],
        };
        // assign into the user-app-resource
        this._userHubSettings.history = history;
        // update the user-app-resource
        return this.updateUserHubSettings(this._userHubSettings);
    }
    /**
     * Remove a specific entry from the user's history
     * @param entry
     * @returns
     */
    async removeFromHistory(entry) {
        // No-op if not authenticated
        if (!this.isAuthenticated) {
            return;
        }
        // No-op if the user doesn't have the permission
        const chk = this.checkPermission("hub:feature:history");
        if (!chk.access) {
            return;
        }
        // remove the entry from the history
        const updated = (0, hubHistory_1.removeHistoryEntry)(entry, this.history);
        this._userHubSettings.history = updated;
        // update the user-app-resource
        return this.updateUserHubSettings(this._userHubSettings);
    }
    /**
     * Update the user's hub settings
     *
     * Possible issue here is that we're not updating the contextManager
     * so the contextManager will be out of sync with this instance of
     * ArcGISContext. Unclear if this will be an actual problem.
     * @param settings
     */
    async updateUserHubSettings(settings) {
        if (!this._authentication) {
            throw new Error("Cannot update user hub settings without an authenticated user");
        }
        // update the user-app-resource replacing it instead of extending
        // changes onto it. This ensures that migrations are applied.
        await (0, hubUserAppResources_1.updateUserHubSettings)(settings, this, true);
        // update the context
        this._userHubSettings = settings;
        // iterate the props in the features object
        Object.keys((0, get_with_default_1.getWithDefault)(settings, "features", {})).forEach((key) => {
            const val = (0, get_prop_1.getProp)(settings, `features.${key}`);
            this._featureFlags[`hub:feature:${key}`] = val;
        });
    }
}
exports.ArcGISContext = ArcGISContext;
//# sourceMappingURL=ArcGISContext.js.map