import type Credential from "./Credential.js";
import type OAuthInfo from "./OAuthInfo.js";
import type ServerInfo from "./ServerInfo.js";
import type Widget from "../widgets/Widget.js";
import type { Evented } from "../core/Evented.js";
import type { AbortOptions } from "../core/promiseUtils.js";

/** @since 5.0 */
export interface GetCredentialOptions extends AbortOptions {
  /**
   * Error object returned by the server from a previous attempt to fetch the given URL.
   *
   * @since 5.0
   */
  error?: Error;
  /**
   * If set to *false*, the user will not be shown a dialog before the OAuth popup window is opened.
   *
   * @default true
   * @since 5.0
   */
  oAuthPopupConfirmation?: boolean;
  /**
   * Token used for a previous unsuccessful attempt to fetch the given URL.
   *
   * @since 5.0
   */
  token?: string;
}

/**
 * @param params - The parameters to pass to the protocol handler.
 * @since 5.0
 */
export type ProtocolHandler = (params: ProtocolHandlerParameters) => boolean;

/** @since 5.0 */
export interface ProtocolHandlerParameters {
  /**
   * The secure resource URL.
   *
   * @since 5.0
   */
  resourceUrl: string;
  /**
   * ServerInfo object describing the server where the secure resource is hosted.
   *
   * @since 5.0
   */
  serverInfo: ServerInfo;
}

/** @since 5.0 */
export interface AuthorizeParameters {
  /**
   * The type of response returned.
   *
   * @since 5.0
   */
  response_type: string;
  /**
   * The expiration time in minutes.
   *
   * @since 5.0
   */
  expiration: number;
  /**
   * The state parameter passed back as the object in the
   * [Credential's oAuthState](https://developers.arcgis.com/javascript/latest/references/core/identity/Credential/#oAuthState) property.
   *
   * @since 5.0
   */
  state: string;
  /**
   * The redirect URL represents the valid places that a
   * user can be redirected to after a successful sign in.
   *
   * @since 5.0
   */
  redirect_uri: string;
  /**
   * The locale being used.
   *
   * @since 5.0
   */
  locale: string;
  /**
   * The application ID of the registered application.
   *
   * @since 5.0
   */
  client_id: string;
}

/**
 * The callback to execute when [setOAuthRedirectionHandler()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#setOAuthRedirectionHandler) is called.
 *
 * @param info - An object containing parameter values for the `handlerCallback`.
 * @since 5.0
 */
export type OAuthRedirectHandler = (info: OAuthRedirectHandlerInfo) => void;

/** @since 5.0 */
export interface OAuthRedirectHandlerInfo {
  /**
   * Object containing authorization parameters used to access the secure service.
   *
   * @since 5.0
   */
  authorizeParams: AuthorizeParameters;
  /**
   * The OAuth 2.0 authorization URL for the portal.
   *
   * @since 5.0
   */
  authorizeUrl: string;
  /**
   * The URL to the accessed resource.
   *
   * @since 5.0
   */
  resourceUrl: string;
  /**
   * The ServerInfo object describing the server
   * where the secure resource is hosted.
   *
   * @since 5.0
   */
  serverInfo: ServerInfo;
  /**
   * A reference to the OAuthInfo object.
   *
   * @since 5.0
   */
  oAuthInfo: OAuthInfo;
}

/** @since 5.0 */
export interface IdentityManagerBaseEvents {
  /**
   * Fires when the [IdentityManager](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManager/) dialog is created.
   * This is used to prompt users for their credentials.
   *
   * @since 5.0
   */
  "dialog-create": void;
  /**
   * Fires when a credential is created.
   *
   * @since 5.0
   */
  "credential-create": IdentityManagerCredentialCreateEvent;
}

/** @since 5.0 */
export interface IdentityManagerCredentialCreateEvent {
  /**
   * The returned credential.
   *
   * @since 5.0
   */
  credential: Credential;
}

/** @since 5.0 */
export abstract class IdentityManagerBase extends Evented {
  /** @since 5.0 */
  "@eventTypes": IdentityManagerBaseEvents;
  /**
   * Dialog box widget used to challenge the user for their credentials when the application attempts to access a secure resource. This property is available after the dialog-create event has fired.
   *
   * @since 5.0
   */
  dialog: Widget | null;
  /**
   * The suggested lifetime of the token in minutes.
   *
   * @default 60
   * @since 5.0
   */
  tokenValidity: number | null;
  /**
   * Returns a [Credential](https://developers.arcgis.com/javascript/latest/references/core/identity/Credential/) if the user has already signed in to access the given resource
   * and is allowed to do so when using the given application id. In addition, it also returns a `boolean`, `viewOnly`, property
   * that indicates whether the app is only viewable. The default is `false`. If the user has not signed in or does not have access,
   * then the promise will be rejected and its error callback will be called.
   *
   * > [!WARNING]
   * >
   * > This scenario is generally not common unless you are building a licensed app.
   * > Also, please note that this method should only be used if your application is on the
   * > same domain as `*.arcgis.com` or ArcGIS Enterprise Server and is only applicable to
   * > applications registered as items in the `Esri` organization.
   *
   * @param resUrl - The resource URL.
   * @param appId - The registered OAuth application id.
   * @returns Resolves to an object which contains the following properties:
   * Property | Type | Description
   * ---------|------|------------
   * credential | [Credential](https://developers.arcgis.com/javascript/latest/references/core/identity/Credential/) | The credential of the user.
   * viewOnly | boolean | Indicates whether the app is only viewable. Default is `false`.
   */
  checkAppAccess(resUrl: string, appId: string): Promise<object>;
  /**
   * Returns the [Credential](https://developers.arcgis.com/javascript/latest/references/core/identity/Credential/) if the user has already signed in to access the given resource. If the user has not signed in, then the promise will be rejected and its error callback will be called.
   *
   * @param resUrl - The resource URL.
   * @returns Resolves to the returned credential of the signed-in user.
   * @since 5.0
   */
  checkSignInStatus(resUrl: string): Promise<Credential>;
  /**
   * Destroys all credentials. It is good practice to call this method if working
   * with an application that contains sign-out functionality as any tokens
   * generated via OAuth will automatically be revoked.
   *
   * @since 5.0
   */
  destroyCredentials(): void;
  /**
   * Disables the use of `window.postMessage` to serve authentication requests that were enabled by [enablePostMessageAuth()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#enablePostMessageAuth). This
   * should be called to prevent memory leaks in SPA routing apps when they need to transition routes. Setting this this helps clean up and remove any `windows's` `message` event listeners that [enablePostMessageAuth()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#enablePostMessageAuth) added.
   *
   * > [!WARNING]
   * >
   * > Please refer to the topic, [Passing authentication to IFramed apps](https://esri.github.io/arcgis-rest-js/guides/embedded-apps/) for additional information. The main differences are:
   * > The ArcGIS REST JS API's [enablePostMessageAuth](https://esri.github.io/arcgis-rest-js/api/auth/UserSession/#enablePostMessageAuth) method's signature is different than what is provided in the ArcGIS Maps SDK for JavaScript as explained here.
   * > Step three, i.e. `Embed App boots and Requests Auth`, does not apply when using the ArcGIS Maps SDK for JavaScript.
   *
   * @see [Passing authentication to IFramed apps](https://esri.github.io/arcgis-rest-js/guides/embedded-apps/)
   * @see [enablePostMessageAuth()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#enablePostMessageAuth)
   */
  disablePostMessageAuth(): void;
  /**
   * Enables the IdentityManager to serve authentication requests for the given resource from apps running in child iframes.
   * The only apps that will be allowed to request the credential are ones that are either running at `*.arcgis.com`,
   * or are running at the same origin as the host app. Requests from other apps will be ignored.
   *
   * Only one resource may be authenticated in this manner at any one time. The URL of the resource should be used as the value of a
   * parameter named `arcgis-auth-portal` that is included in the iframe's `src` URL. The iframe's `src` URL should
   * also include another parameter named `arcgis-auth-origin` with a value of `window.location.origin`. Both of these
   * parameter values should be URL-encoded using `encodeURIComponent`. These
   * parameters are used by the IdentityManager, or the [UserSession](https://esri.github.io/arcgis-rest-js/api/auth/UserSession/#fromParent) running
   * in the iframe app when it needs the user's authentication to access a given
   * resource.
   *
   * > [!WARNING]
   * >
   * > Please refer to the topic, [Passing authentication to IFramed apps](https://esri.github.io/arcgis-rest-js/guides/embedded-apps/) for additional information. The main differences are:
   * > The ArcGIS REST JS API's [enablePostMessageAuth](https://esri.github.io/arcgis-rest-js/api/auth/UserSession/#enablePostMessageAuth) method's signature is different than what is provided in the ArcGIS Maps SDK for JavaScript as explained here.
   * > Step three, i.e. `Embed App boots and Requests Auth`, does not apply when using the ArcGIS Maps SDK for JavaScript.
   *
   * @param resUrl - The resource URL. Default value is `https://www.arcgis.com/sharing/rest`.
   * @see [disablePostMessageAuth()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#disablePostMessageAuth)
   * @see [Passing authentication to IFramed apps](https://esri.github.io/arcgis-rest-js/guides/embedded-apps/)
   */
  enablePostMessageAuth(resUrl?: string): void;
  /**
   * Returns the [Credential](https://developers.arcgis.com/javascript/latest/references/core/identity/Credential/) for the resource identified by the specified url. Optionally, you can provide a userId to find credentials for a specific user.
   *
   * @param url - The URL to a server.
   * @param userId - The userId for which you want to obtain credentials.
   * @returns The credential for the resource identified by the specified URL.
   * @since 5.0
   */
  findCredential(url: string | null | undefined, userId?: string): Credential | undefined;
  /**
   * Returns the [OAuthInfo](https://developers.arcgis.com/javascript/latest/references/core/identity/OAuthInfo/) configuration for the passed in Portal server URL.
   *
   * @param portalUrl - The URL to a Portal.
   * @returns The OAuthInfo configuration for the passed in Portal server URL.
   * @since 5.0
   * @example
   * const esriId = await $arcgis.import("@arcgis/core/identity/IdentityManager.js");
   * let portalURL = "https://host.arcgis.com";
   * findOAuthInfo = function (){
   *   let oAuthInfo = esriId.findOAuthInfo(portalURL)
   *   console.log(oAuthInfo.toJSON())
   * }
   */
  findOAuthInfo(portalUrl: string): OAuthInfo | undefined;
  /**
   * Returns information about the server that is hosting the specified URL.
   *
   * @param url - The URL to the server
   * @returns The ServerInfo configuration for the passed in server URL.
   * @since 5.0
   */
  findServerInfo(url: string): ServerInfo | undefined;
  /**
   * Returns an object containing a token and its expiration time. It is necessary to provide the [ServerInfo](https://developers.arcgis.com/javascript/latest/references/core/identity/ServerInfo/) object that contains a token service URL and a user info object containing username and password. This is a helper method typically called by sub-classes to generate tokens.
   *
   * @param serverInfo - A ServerInfo object that contains a token service URL.
   * @param userInfo - A user info object containing a user name and password.
   * @param options - The options for generating a token.
   * @returns Resolves to an object containing a token and expiration time.
   * @since 5.0
   */
  generateToken(serverInfo: ServerInfo, userInfo: any, options?: IdentityManagerGenerateTokenOptions): Promise<{
      expires: number;
      ssl?: boolean;
      token: string;
      validity: number;
  }>;
  /**
   * Returns a [Credential](https://developers.arcgis.com/javascript/latest/references/core/identity/Credential/) object that can be used to access the secured resource identified by the input URL.
   *
   * @param url - The URL for the secure resource
   * @param options - The options for getting the credential.
   * @returns Resolves to an object containing a [Credential](https://developers.arcgis.com/javascript/latest/references/core/identity/Credential/) that can be used to access the secured resource identified by the input URL.
   * @since 5.0
   */
  getCredential(url: string, options?: GetCredentialOptions): Promise<Credential>;
  /**
   * Call this method during application initialization with the JSON previously obtained from the [toJSON()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#toJSON) method used to re-hydrate the state of IdentityManager.
   *
   * @param json - The JSON obtained from the [toJSON()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#toJSON) method.
   * @since 5.0
   */
  initialize(json: any): void;
  /**
   * Indicates if the IdentityManager is busy accepting user input. For example, it returns `true` if the user has invoked [IdentityManager](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManager/) sign-in and is waiting for a response.
   *
   * @returns Whether IdentityManager is currently accepting user input.
   * @since 5.0
   */
  isBusy(): boolean;
  /**
   * Registers OAuth 2.0 configurations.
   *
   * @param oAuthInfos - An array of OAuthInfo objects that defines the OAuth configurations.
   * @since 5.0
   * @example
   * const [OAuthInfo, esriId] = await $arcgis.import([
   *   "@arcgis/core/identity/OAuthInfo.js",
   *   "@arcgis/core/identity/IdentityManager.js"
   * ]);
   * let oAuthInfo = new OAuthInfo({
   *   appId: "<registered client id>"
   * }); // required parameter
   * esriId.registerOAuthInfos([oAuthInfo]);
   */
  registerOAuthInfos(oAuthInfos: OAuthInfo[]): void;
  /**
   * Register secure servers and the token endpoints.
   *
   * @param serverInfos - An array of ServerInfos objects that defines the secure service and token endpoint. The IdentityManager makes its best guess to determine the location of the secure server and token endpoint. Therefore, in most cases calling this method is not necessary. However, if the location of your server or token endpoint is not standard, use this method to register the location.
   * @since 5.0
   * @example
   * const [ServerInfo, esriId] = await $arcgis.import(["@arcgis/core/identity/ServerInfo.js", "@arcgis/core/identity/IdentityManager.js"]);
   * let serverInfo = new ServerInfo();
   * serverInfo.server = "https://sampleserver6.arcgisonline.com";
   * serverInfo.tokenServiceUrl = "https://sampleserver6.arcgisonline.com/arcgis/tokens/generateToken";
   * serverInfo.hasServer = true;
   * esriId.registerServers([serverInfo]);
   */
  registerServers(serverInfos: ServerInfo[]): void;
  /**
   * Registers the given OAuth 2.0 access token or ArcGIS Server token with the IdentityManager.
   * See [registerOAuthInfos()](https://developers.arcgis.com/javascript/latest/references/core/identity/IdentityManagerBase/#registerOAuthInfos) for additional information.
   * The `registerToken` method is an advanced workflow for pre-registering long-term tokens for when you don't want users to sign in.
   *
   * Once a user logs in, the access token is registered with the IdentityManager. Subsequently, every request made
   * by the application forwards this token when accessing web maps and other items stored in ArcGIS Online,
   * or resources on your server.
   *
   * @param properties - Additional properties for registering a token.
   * @since 5.0
   */
  registerToken(properties: IdentityManagerRegisterTokenProperties): void;
  /**
   * Once a user successfully logs in, they are redirected back to the application. Use this method if
   * the application needs to execute custom logic before the page is redirected. The IdentityManager
   * calls the custom handler function with an object containing redirection properties.
   *
   * @param handlerFunction - When called,
   * the callback passed to `setOAuthRedirectionHandler` receives an object containing the redirection
   * properties.
   * @since 5.0
   * @example
   * const esriId = await $arcgis.import("@arcgis/core/identity/IdentityManager.js");
   * esriId.setOAuthRedirectionHandler(function(info)
   *  {
   *    // Execute custom logic then perform redirect
   *    window.location = info.authorizeUrl + "?" + new URLSearchParams(info.authorizeParams).toString();
   * });
   */
  setOAuthRedirectionHandler(handlerFunction: OAuthRedirectHandler): void;
  /**
   * Use this method in the popup callback page to pass the token and other values back to the IdentityManager.
   *
   * @param hash - The token information in addition to any other values needed to be passed back to the IdentityManager.
   * @since 5.0
   */
  setOAuthResponseHash(hash: string): void;
  /**
   * When accessing secured resources, the IdentityManager may prompt for username and password and send them to the server using a secure connection. Due to potential browser limitations, it may not be possible to establish a secure connection with the server if the application is being run over HTTP protocol. In such cases, the Identity Manager will abort the request to fetch the secured resource.
   * To resolve this issue, configure your web application server with HTTPS support and run the application over HTTPS. This is the recommended solution for production environments. However, for internal development environments that don't have HTTPS support, you can define a protocol error handler that allows the Identity Manager to continue with the process over HTTP protocol.
   *
   * @param handlerFunction - The function to call when the protocol is mismatched.
   * @since 5.0
   */
  setProtocolErrorHandler(handlerFunction: ProtocolHandler): void;
  /**
   * Return properties of this object in JSON format. It can be stored in a cookie or persisted in HTML5 LocalStorage and later used to:
   * * Initialize the IdentityManager the next time a user opens your application.
   * * Share the state of the IdentityManager between multiple web pages of your website.
   * This way users will not be asked to sign in repeatedly when they launch your app multiple times or when navigating between multiple web pages in your website.
   *
   * @returns The JSON object representing the IdentityManager instance calling this method.
   * @since 5.0
   */
  toJSON(): object;
}

/** @since 5.0 */
export interface IdentityManagerRegisterTokenProperties {
  /**
   * Token expiration time specified as number of milliseconds since 1 January 1970 00:00:00 UTC.
   *
   * @since 5.0
   */
  expires?: number;
  /**
   * For ArcGIS Online or Portal, this is https://www.arcgis.com/sharing/rest or similar to https://www.example.com/portal/sharing/rest.
   *                                     For ArcGIS Server this is similar to https://www.example.com/arcgis/rest/services.
   *
   * @since 5.0
   */
  server: string;
  /**
   * Set this to `true` if the user has an ArcGIS Online organizational account and the organization is configured to allow access to resources only through SSL.
   *
   * @since 5.0
   */
  ssl?: boolean;
  /**
   * The access token.
   *
   * @since 5.0
   */
  token: string;
  /**
   * The id of the user who owns the access token.
   *
   * @since 5.0
   */
  userId?: string;
}

/** @since 5.0 */
export interface IdentityManagerGenerateTokenOptions extends AbortOptions {
  /**
   * The server URL.
   *
   * @since 5.0
   */
  serverUrl?: string;
  /**
   * The server token.
   *
   * @since 5.0
   */
  token?: string;
  /**
   * Indicates if the server requires SSL.
   *
   * @since 5.0
   */
  ssl?: boolean;
}