/* COPYRIGHT Esri - https://js.arcgis.com/1.2/LICENSE.txt */
import * as common from "@esri/solution-common";
import IdentityManager from "@arcgis/core/identity/IdentityManager.js";
import OAuthInfo from "@arcgis/core/identity/OAuthInfo.js";
import Portal from "@arcgis/core/portal/Portal.js";
class Auth {
  constructor(clientId, portalURL, locale, signInParam) {
    this._credential = void 0;
    this._isEnterprise = false;
    this._locale = "en";
    this._portalSubset = void 0;
    this._portalUserSubset = void 0;
    this._searchArcGISOnlineEnabled = true;
    this._signInParam = void 0;
    this._clientId = clientId ?? "";
    this._locale = locale ?? "en";
    this._portalUrl = portalURL ?? "https://www.arcgis.com";
    this._portalRestUrl = this._portalUrl + "/sharing/rest";
    this._portalUrl = location.origin.endsWith(".arcgis.com") ? window.location.origin : this._portalUrl;
    this._signInParam = signInParam;
    const info = new OAuthInfo({
      appId: this._clientId,
      locale: this._locale,
      popup: false,
      // inline redirects don't require any additional app configuration
      portalUrl: this._portalUrl,
      preserveUrlHash: true
    });
    try {
      IdentityManager.registerOAuthInfos([info]);
    } catch (error) {
      console.log(error);
    }
    this._portal = new Portal({ url: this._portalUrl });
  }
  get Credential() {
    return this._credential;
  }
  get IsEnterprisePortal() {
    return this._isEnterprise;
  }
  get IsSignedIn() {
    return !!this._credential;
  }
  get Portal() {
    return this._portal;
  }
  get PortalSubset() {
    return this._portalSubset;
  }
  get PortalUrl() {
    return this._portalUrl;
  }
  get PortalUserOrg() {
    return this._portalSubset && this._portalSubset.id;
  }
  get PortalUserName() {
    return this._portalUserSubset && this._portalUserSubset.username || "";
  }
  get PortalUserSubset() {
    return this._portalUserSubset;
  }
  get PortalUserPrivileges() {
    let privileges = [];
    if (this._portalUserSubset?.privileges) {
      privileges = this._portalUserSubset.privileges;
    }
    return privileges;
  }
  get SignInParam() {
    return this._signInParam;
  }
  get CanUseAI() {
    const orgSettings = this._portalSubset?.orgSettings;
    const hasPrivilege = this.PortalUserPrivileges.includes("portal:user:useAIAssistants");
    if (orgSettings?.aiAssistantsEnabled) {
      return orgSettings.aiAssistantsEnabled && !orgSettings.blockBetaApps && hasPrivilege;
    }
    return false;
  }
  get CanSearchArcGISOnline() {
    return this._searchArcGISOnlineEnabled;
  }
  get RestUserSession() {
    const serverInfo = IdentityManager.findServerInfo(this._portalRestUrl);
    const userSession = this._credential ? common.UserSession.fromCredential(this._credential, serverInfo) : this._isEnterprise ? new common.UserSession({}) : new common.UserSession({ portal: this._portalRestUrl });
    userSession.portal = this.PortalSubset?.restUrl ? this.PortalSubset?.restUrl : userSession.portal;
    userSession.clientId = this._credential?._oAuthCred?.appId;
    userSession._refreshToken = this._credential?._oAuthCred?.refreshToken;
    return userSession;
  }
  /**
   * Initializes the Auth object. This method must be called before any other Auth methods.
   * If the user is not authorized to use the app, the page will be redirected to an error page.
   *
   * @returns Promise that resolves with the initialized Auth object
   */
  async initialize() {
    try {
      await this._init();
    } catch (err) {
      this.destroy();
    }
    return this;
  }
  destroy() {
    this._credential = void 0;
    this._portalSubset = void 0;
    this._portalUserSubset = void 0;
    IdentityManager.destroyCredentials();
  }
  /**
   * Signs into app and refreshes the window.location or switches to the supplied URL.
   *
   * @param signInParamValue If supplied, the parameter `signInParam` with the supplied value
   * will be added to the redirect uri's search params
   */
  async signIn(signInParamValue = "") {
    IdentityManager.setOAuthRedirectionHandler((info) => {
      const redirectUri = info.authorizeParams.redirect_uri;
      if (signInParamValue.length > 0) {
        const redirectUriParts = redirectUri.split("?");
        const redirectOriginPath = redirectUriParts[0];
        let redirectSearch = "";
        const redirectSearchParams = new URLSearchParams(redirectUriParts[1] || "");
        redirectSearchParams.append("signInParam", signInParamValue);
        redirectSearch = "?" + redirectSearchParams.toString();
        info.authorizeParams.redirect_uri = redirectOriginPath + redirectSearch;
      }
      window.location.href = info.authorizeUrl + "?" + this._objectToQuery(info.authorizeParams);
      return;
    });
    await IdentityManager.getCredential((this._portalSubset?.portalUrl ?? this._portalUrl) + "/sharing").then(
      (credential) => {
        if (credential) {
          window.location.reload();
        }
      }
    );
  }
  /**
   * Signs out of app.
   * When the user is... 
   *  on the solutions page they should stay on that page
   *  on a solutions details page they should stay on that page
   *  on the deployments page they should stay on that page
   *  on a deployed solutions page they should be sent to the deployments page
   *  on the resources page they should stay on that page
   */
  signOut() {
    const href = window.location.href;
    const idRegEx = /[\/]\b([0-9A-Fa-f]){32}\b/g;
    let redirectUrl = href.match(idRegEx) ? href.replace(idRegEx, "") : href;
    const restUrl = this._portal?.restUrl ?? this._portalUrl + "/sharing/rest";
    const signOutUrl = `${restUrl}/oauth2/signout?client_id=${this._clientId}&redirect_uri=${encodeURIComponent(redirectUrl)}`;
    this.destroy();
    window.location.replace(signOutUrl);
  }
  /**
   * Refreshes the user session token
   */
  refreshSession() {
    const userSession = this.RestUserSession;
    if (!userSession || !this.IsSignedIn) {
      return void 0;
    }
    return userSession.exchangeRefreshToken();
  }
  // ---------------------------------------------------------------------------------------------------------------- //
  /**
   * Generates the organization URL.
   * @param portalUrl The URL to the portal instance.
   * @param portalUrlKey The prefix selected by the organization's administrator to be used with the customBaseURL.
   * @param portalCustomBaseUrl The custom base URL for the portal.
   * @returns
   */
  _getOrgUrl(portalUrl, portalUrlKey, portalCustomBaseUrl) {
    if (portalUrlKey && portalCustomBaseUrl) {
      return window.location.protocol + "//" + portalUrlKey.toLowerCase() + "." + portalCustomBaseUrl;
    } else {
      return portalUrl;
    }
  }
  /**
   * Initializes the Auth object. This method must be called before any other Auth methods.
   * If the user is not authorized to use the app, the page will be redirected to an error page.
   * @returns Promise that resolves with the initialized Auth object
   */
  async _init() {
    try {
      console.log(`portalUrl for AUTH: ${this._portalUrl + "/sharing"}`);
      this._credential = await IdentityManager.checkSignInStatus(this._portalUrl + "/sharing");
    } catch (error) {
      console.log(error);
    }
    try {
      const response = await IdentityManager.checkAppAccess(this._portalUrl + "/sharing", this._clientId);
      if (typeof response.valid !== "undefined" && !response.valid || response.viewOnlyUserTypeApp) {
        window.location.replace(`error.html?error=blocked&locale=${this._locale}`);
      }
      this._credential = response.credential;
    } catch (err) {
      console.log(err);
      if (err.name !== "identity-manager:not-authenticated") ;
    }
    this._portal.authMode = "auto";
    await this._portal.load();
    const orgSettings = this._credential ? await common.getOrganizationSettings(this.RestUserSession) : {};
    this._isEnterprise = this._portal.isPortal;
    if (this._isEnterprise) {
      try {
        const portalSettingsUrl = `${this._portal.restUrl}/portals/self/settings`;
        const portalSettings = await common.request(portalSettingsUrl, {
          authentication: this._credential ? this.RestUserSession : void 0
        });
        if (portalSettings?.portalConfigProperties?.searchArcGISOnlineEnabled !== void 0) {
          this._searchArcGISOnlineEnabled = portalSettings.portalConfigProperties.searchArcGISOnlineEnabled;
        } else {
          this._searchArcGISOnlineEnabled = false;
        }
      } catch (error) {
        console.warn("Failed to fetch portal settings:", error);
        this._searchArcGISOnlineEnabled = false;
      }
    } else {
      this._searchArcGISOnlineEnabled = true;
    }
    this._portalSubset = {
      name: this._portal.name ?? "",
      id: this._portal.id ?? "",
      restUrl: this._portal.restUrl ?? "",
      portalUrl: this._portal.url ?? "",
      urlKey: this._portal.urlKey ?? "",
      hostName: this._portal.portalHostname ?? "",
      orgUrl: this._getOrgUrl(this._portal.url, this._portal.urlKey ?? "", this._portal.customBaseUrl ?? ""),
      locationTrackingEnabled: this._portal.helperServices.hasOwnProperty("locationTracking"),
      hubPremiumEnabled: this._portal.portalProperties?.hub?.enabled || false,
      workflowURL: this._portal.helperServices?.workflowManager?.url ?? this._portal.url,
      isReadOnly: this._portal.isReadOnly,
      orgSettings
    };
    this._portalUserSubset = {
      fullName: this._portal.user?.fullName ?? "",
      username: this._portal.user?.username ?? "",
      culture: this._portal.user?.culture?.toLowerCase() || "en",
      thumbnailUrl: this._portal.user?.thumbnailUrl || this._portal.url + "/home/js/arcgisonline/css/images/no-user-thumb.jpg",
      orgId: this._portal.user?.orgId ?? "",
      privileges: this._portal.user.privileges,
      role: this._portal.user?.role ?? ""
    };
    if (this._isEnterprise && this._portalSubset) {
      this._portalSubset.workflowURL = await common.getWorkflowEnterpriseServerRootURL(
        this._portal.restUrl,
        this.RestUserSession || new common.UserSession({})
      );
    }
    if (this._portal?.user?.roleId && this._portalUserSubset) {
      this._portalUserSubset.roleId = this._portal.user.roleId;
    }
    return this;
  }
  /**
   * Checks if an object is serializable.
   * @param obj Object to check
   * This function checks if the object has a `toJSON` method, which is a
   * @returns Boolean indicating whether the object is serializable.
   */
  _isSerializable(obj) {
    return obj && typeof obj === "object" && "toJSON" in obj && typeof obj.toJSON === "function";
  }
  /**
   * Converts a hashmap of query parameters to a query string. Following formatting rules applies:
   *   - object values that implements `toJSON()` are stringified
   *   - array value will map to multiple key value pairs. ie `{ key: ["v1", "v2"] }` returns `key=v1&key=v2
   *
   * @param queryParameters the query parameters to convert to a string
   * @param replacers optional replacer functions defined per key
   *
   * @example
   * const queryParameters = {
   *   objectIds: [1, 2, 3, 4]
   * };
   *
   * // returns: "objectIds=1&objectIds=2&objectIds=3&objectIds=4"
   * objectToQuery(queryParameters);
   *
   * // returns: "objectIds=1,2,3,4"
   * objectToQuery(queryParameters, {
   *   objectIds: (v) => v.join(",")
   * });
   *
   * // returns: "objectIds=1,2,3,4"
   * objectToQuery(queryParameters, (k, v) => {
   *   switch (k) {
   *     case "objectIds":
   *       return v.join(","));
   *     default:
   *       return "" + v;
   *   }
   * });
   *
   * Copied from function exported in arcgis-js-api but not added to JSAPI.
   */
  _objectToQuery(queryParameters, replacers) {
    if (!queryParameters) {
      return "";
    }
    if (replacers && typeof replacers === "function") {
      return Object.keys(queryParameters).map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(replacers(key, queryParameters[key]))).join("&");
    }
    return Object.keys(queryParameters).map((key) => {
      const value = queryParameters[key];
      if (value == void 0) {
        return "";
      }
      const assign = encodeURIComponent(key) + "=";
      const replacer = replacers && replacers[key];
      if (replacer) {
        return assign + encodeURIComponent(replacer(value));
      }
      if (Array.isArray(value)) {
        return value.map((child) => {
          if (this._isSerializable(child)) {
            return assign + encodeURIComponent(JSON.stringify(child));
          }
          return assign + encodeURIComponent(child);
        }).join("&");
      }
      if (this._isSerializable(value)) {
        return assign + encodeURIComponent(JSON.stringify(value));
      }
      return assign + encodeURIComponent(value);
    }).filter((value) => value).join("&");
  }
}
export {
  Auth as A
};
