import { AuthError } from './errors'
import { Fetch } from './fetch'
import { EIP1193Provider, EthereumSignInInput, Hex } from './web3/ethereum'
import type { SolanaSignInInput, SolanaSignInOutput } from './web3/solana'
import {
  ServerCredentialCreationOptions,
  ServerCredentialRequestOptions,
  WebAuthnApi,
  WebAuthnError,
} from './webauthn'
import type {
  RegistrationResponseJSON,
  AuthenticationResponseJSON,
  ServerCredentialResponse,
} from './webauthn'
import {
  AuthenticationCredential,
  PublicKeyCredentialCreationOptionsFuture,
  PublicKeyCredentialRequestOptionsFuture,
  RegistrationCredential,
} from './webauthn.dom'

/** One of the providers supported by GoTrue. Use the `custom:` prefix for custom OIDC providers (e.g. `custom:my-oidc-provider`). */
export type Provider =
  | 'apple'
  | 'azure'
  | 'bitbucket'
  | 'discord'
  | 'facebook'
  | 'figma'
  | 'github'
  | 'gitlab'
  | 'google'
  | 'kakao'
  | 'keycloak'
  | 'linkedin'
  | 'linkedin_oidc'
  | 'notion'
  | 'slack'
  | 'slack_oidc'
  | 'spotify'
  | 'twitch'
  /** Uses OAuth 1.0a */
  | 'twitter'
  /** Uses OAuth 2.0 */
  | 'x'
  | 'workos'
  | 'zoom'
  | 'fly'
  | `custom:${string}`

export type AuthChangeEventMFA = 'MFA_CHALLENGE_VERIFIED'

export type AuthChangeEvent =
  | 'INITIAL_SESSION'
  | 'PASSWORD_RECOVERY'
  | 'SIGNED_IN'
  | 'SIGNED_OUT'
  | 'TOKEN_REFRESHED'
  | 'USER_UPDATED'
  | AuthChangeEventMFA

/**
 * Provide your own global lock implementation instead of the default
 * implementation. The function should acquire a lock for the duration of the
 * `fn` async function, such that no other client instances will be able to
 * hold it at the same time.
 *
 * @experimental
 *
 * @param name Name of the lock to be acquired.
 * @param acquireTimeout If negative, no timeout should occur. If positive it
 *                       should throw an Error with an `isAcquireTimeout`
 *                       property set to true if the operation fails to be
 *                       acquired after this much time (ms).
 * @param fn The operation to execute when the lock is acquired.
 */
export type LockFunc = <R>(name: string, acquireTimeout: number, fn: () => Promise<R>) => Promise<R>

export type GoTrueClientOptions = {
  /* The URL of the GoTrue server. */
  url?: string
  /* Any additional headers to send to the GoTrue server. */
  headers?: { [key: string]: string }
  /* Optional key name used for storing tokens in local storage. */
  storageKey?: string
  /**
   * Set to "true" if you want to automatically detect OAuth grants in the URL and sign in the user.
   * Set to "false" to disable automatic detection.
   * Set to a function to provide custom logic for determining if a URL contains a Supabase auth callback.
   * The function receives the current URL and parsed parameters, and should return true if the URL
   * should be processed as a Supabase auth callback, or false to ignore it.
   *
   * This is useful when your app uses other OAuth providers (e.g., Facebook Login) that also return
   * access_token in the URL fragment, which would otherwise be incorrectly intercepted by Supabase Auth.
   *
   * @example
   * ```ts
   * detectSessionInUrl: (url, params) => {
   *   // Ignore Facebook OAuth redirects
   *   if (url.pathname === '/facebook/redirect') return false
   *   // Use default detection for other URLs
   *   return Boolean(params.access_token || params.error_description)
   * }
   * ```
   */
  detectSessionInUrl?: boolean | ((url: URL, params: { [parameter: string]: string }) => boolean)
  /* Set to "true" if you want to automatically refresh the token before expiring. */
  autoRefreshToken?: boolean
  /* Set to "true" if you want to automatically save the user session into local storage. If set to false, session will just be saved in memory. */
  persistSession?: boolean
  /* Provide your own local storage implementation to use instead of the browser's local storage. */
  storage?: SupportedStorage
  /**
   * Stores the user object in a separate storage location from the rest of the session data. When non-null, `storage` will only store a JSON object containing the access and refresh token and some adjacent metadata, while `userStorage` will only contain the user object under the key `storageKey + '-user'`.
   *
   * When this option is set and cookie storage is used, `getSession()` and other functions that load a session from the cookie store might not return back a user. It's very important to always use `getUser()` to fetch a user object in those scenarios.
   *
   * @experimental
   */
  userStorage?: SupportedStorage
  /* A custom fetch implementation. */
  fetch?: Fetch
  /* If set to 'pkce' PKCE flow. Defaults to the 'implicit' flow otherwise */
  flowType?: AuthFlowType
  /* If debug messages are emitted. Can be used to inspect the behavior of the library. If set to a function, the provided function will be used instead of `console.log()` to perform the logging. */
  debug?: boolean | ((message: string, ...args: any[]) => void)
  /**
   * Provide your own locking mechanism based on the environment. By default
   * the client coordinates refreshes itself (single-flight via
   * `refreshingDeferred` + commit guard) and relies on the GoTrue server to
   * resolve cross-tab refresh races. Passing a custom lock opts into a
   * legacy path that wraps every auth operation in your supplied lock — this
   * path is preserved for backwards compatibility (typically React Native
   * `processLock` or Node multi-process setups).
   *
   * @deprecated Custom locks still work in v2.x for backwards compatibility.
   * The legacy lock path will be removed in v3 — drop this option from your
   * constructor options before upgrading.
   */
  lock?: LockFunc
  /**
   * Set to "true" if there is a custom authorization header set globally.
   * @experimental
   */
  hasCustomAuthorizationHeader?: boolean
  /**
   * If there is an error with the query, throwOnError will reject the promise by
   * throwing the error instead of returning it as part of a successful response.
   */
  throwOnError?: boolean
  /**
   * The maximum time in milliseconds to wait for acquiring the custom lock
   * supplied via the `lock` option. Only consulted when a custom `lock` is
   * passed — the default lockless path doesn't use this timeout.
   *
   * @default 5000
   *
   * @deprecated Only used by the legacy lock path. Will be removed in v3
   * along with the `lock` option.
   */
  lockAcquireTimeout?: number

  /**
   * If true, skips automatic initialization in constructor. Useful for SSR
   * contexts where initialization timing must be controlled to prevent race
   * conditions with HTTP response generation.
   *
   * @default false
   */
  skipAutoInitialize?: boolean

  /**
   * Opt-in flags for experimental features. These APIs may change without
   * notice and are disabled by default.
   *
   * @experimental
   */
  experimental?: ExperimentalFeatureFlags
}

export type ExperimentalFeatureFlags = {
  /**
   * Enables passkey support:
   *   - `auth.signInWithPasskey()`, `auth.registerPasskey()`
   *   - `auth.passkey.*`
   *   - `auth.admin.passkey.*`
   *
   * Defaults to `false`. Calling any passkey method while this flag is
   * disabled throws a descriptive error at call time.
   */
  passkey?: boolean
}

const WeakPasswordReasons = ['length', 'characters', 'pwned'] as const

export type WeakPasswordReasons = (typeof WeakPasswordReasons)[number]
export type WeakPassword = {
  reasons: WeakPasswordReasons[]
  message: string
}

/**
 * Resolve mapped types and show the derived keys and their types when hovering in
 * VS Code, instead of just showing the names those mapped types are defined with.
 */
export type Prettify<T> = T extends Function ? T : { [K in keyof T]: T[K] }

/**
 * A stricter version of TypeScript's Omit that only allows omitting keys that actually exist.
 * This prevents typos and ensures type safety at compile time.
 * Unlike regular Omit, this will error if you try to omit a non-existent key.
 */
export type StrictOmit<T, K extends keyof T> = Omit<T, K>

/**
 * a shared result type that encapsulates errors instead of throwing them, allows you to optionally specify the ErrorType
 */
export type RequestResult<T, ErrorType extends Error = AuthError> =
  | {
      data: T
      error: null
    }
  | {
      data: null
      error: Error extends AuthError ? AuthError : ErrorType
    }

/**
 * similar to RequestResult except it allows you to destructure the possible shape of the success response
 *  {@link RequestResult}
 */
export type RequestResultSafeDestructure<T> =
  | { data: T; error: null }
  | {
      data: T extends object ? { [K in keyof T]: null } : null
      error: AuthError
    }

export type AuthResponse = RequestResultSafeDestructure<{
  user: User | null
  session: Session | null
}>

export type AuthResponsePassword = RequestResultSafeDestructure<{
  user: User | null
  session: Session | null
  weak_password?: WeakPassword | null
}>

/**
 * AuthOtpResponse is returned when OTP is used.
 *
 * {@link AuthResponse}
 */
export type AuthOtpResponse = RequestResultSafeDestructure<{
  user: null
  session: null
  messageId?: string | null
}>

export type AuthTokenResponse = RequestResultSafeDestructure<{
  user: User
  session: Session
}>

export type AuthTokenResponsePassword = RequestResultSafeDestructure<{
  user: User
  session: Session
  weakPassword?: WeakPassword
}>

export type OAuthResponse =
  | {
      data: {
        provider: Provider
        url: string
      }
      error: null
    }
  | {
      data: {
        provider: Provider
        url: null
      }
      error: AuthError
    }

export type SSOResponse = RequestResult<{
  /**
   * URL to open in a browser which will complete the sign-in flow by
   * taking the user to the identity provider's authentication flow.
   *
   * On browsers you can set the URL to `window.location.href` to take
   * the user to the authentication flow.
   */
  url: string
}>

export type UserResponse = RequestResultSafeDestructure<{
  user: User
}>

export interface Session {
  /**
   * The oauth provider token. If present, this can be used to make external API requests to the oauth provider used.
   */
  provider_token?: string | null
  /**
   * The oauth provider refresh token. If present, this can be used to refresh the provider_token via the oauth provider's API.
   * Not all oauth providers return a provider refresh token. If the provider_refresh_token is missing, please refer to the oauth provider's documentation for information on how to obtain the provider refresh token.
   */
  provider_refresh_token?: string | null
  /**
   * The access token jwt. It is recommended to set the JWT_EXPIRY to a shorter expiry value.
   */
  access_token: string
  /**
   * A one-time used refresh token that never expires.
   */
  refresh_token: string
  /**
   * The number of seconds until the token expires (since it was issued). Returned when a login is confirmed.
   */
  expires_in: number
  /**
   * A timestamp of when the token will expire. Returned when a login is confirmed.
   */
  expires_at?: number
  token_type: 'bearer'

  /**
   * When using a separate user storage, accessing properties of this object will throw an error.
   */
  user: User
}

const AMRMethods = [
  'password',
  'otp',
  'oauth',
  'totp',
  'mfa/totp',
  'mfa/phone',
  'mfa/webauthn',
  'anonymous',
  'sso/saml',
  'magiclink',
  'web3',
  'oauth_provider/authorization_code',
] as const

export type AMRMethod = (typeof AMRMethods)[number] | (string & {})

/**
 * An authentication method reference (AMR) entry.
 *
 * An entry designates what method was used by the user to verify their
 * identity and at what time.
 *
 * Note: Custom access token hooks can return AMR claims as either:
 * - An array of AMREntry objects (detailed format with timestamps)
 * - An array of strings (RFC-8176 compliant format)
 *
 * @see {@link GoTrueMFAApi#getAuthenticatorAssuranceLevel}.
 */
export interface AMREntry {
  /** Authentication method name. */
  method: AMRMethod

  /**
   * Timestamp when the method was successfully used. Represents number of
   * seconds since 1st January 1970 (UNIX epoch) in UTC.
   */
  timestamp: number
}

export interface UserIdentity {
  id: string
  user_id: string
  identity_data?: {
    [key: string]: any
  }
  identity_id: string
  provider: string
  created_at?: string
  last_sign_in_at?: string
  updated_at?: string
}

const FactorTypes = ['totp', 'phone', 'webauthn'] as const

/**
 * Type of factor. `totp` and `phone` supported with this version
 */
export type FactorType = (typeof FactorTypes)[number]

const FactorVerificationStatuses = ['verified', 'unverified'] as const

/**
 * The verification status of the factor, default is `unverified` after `.enroll()`, then `verified` after the user verifies it with `.verify()`
 */
type FactorVerificationStatus = (typeof FactorVerificationStatuses)[number]

/**
 * A MFA factor.
 *
 * @see {@link GoTrueMFAApi#enroll}
 * @see {@link GoTrueMFAApi#listFactors}
 * @see {@link GoTrueAdminMFAApi#listFactors}
 */
export type Factor<
  Type extends FactorType = FactorType,
  Status extends FactorVerificationStatus = (typeof FactorVerificationStatuses)[number],
> = {
  /** ID of the factor. */
  id: string

  /** Friendly name of the factor, useful to disambiguate between multiple factors. */
  friendly_name?: string

  /**
   * Type of factor. `totp` and `phone` supported with this version
   */
  factor_type: Type

  /**
   * The verification status of the factor, default is `unverified` after `.enroll()`, then `verified` after the user verifies it with `.verify()`
   */
  status: Status

  created_at: string
  updated_at: string
  last_challenged_at?: string
}

export interface UserAppMetadata {
  /**
   * The first provider that the user used to sign up with.
   */
  provider?: string
  /**
   * A list of all providers that the user has linked to their account.
   */
  providers?: string[]
  [key: string]: any
}

export interface UserMetadata {
  [key: string]: any
}

export interface User {
  id: string
  app_metadata: UserAppMetadata
  user_metadata: UserMetadata
  aud: string
  confirmation_sent_at?: string
  recovery_sent_at?: string
  email_change_sent_at?: string
  new_email?: string
  new_phone?: string
  invited_at?: string
  action_link?: string
  email?: string
  phone?: string
  created_at: string
  confirmed_at?: string
  email_confirmed_at?: string
  phone_confirmed_at?: string
  last_sign_in_at?: string
  role?: string
  updated_at?: string
  identities?: UserIdentity[]
  is_anonymous?: boolean
  is_sso_user?: boolean
  factors?: (Factor<FactorType, 'verified'> | Factor<FactorType, 'unverified'>)[]
  deleted_at?: string
  banned_until?: string
}

export interface UserAttributes {
  /**
   * The user's current password
   *
   * This is only ever present when the user is resetting
   * their password and GOTRUE_SECURITY_UPDATE_PASSWORD_REQUIRE_CURRENT_PASSWORD is true.
   *
   */
  current_password?: string

  /**
   * The user's email.
   */
  email?: string

  /**
   * The user's phone.
   */
  phone?: string

  /**
   * The user's password.
   */
  password?: string

  /**
   * The nonce sent for reauthentication if the user's password is to be updated.
   *
   * Call reauthenticate() to obtain the nonce first.
   */
  nonce?: string

  /**
   * A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
   *
   * The `data` should be a JSON object that includes user-specific info, such as their first and last name.
   *
   */
  data?: object
}

export interface AdminUserAttributes extends Omit<UserAttributes, 'data'> {
  /**
   * A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
   *
   *
   * The `user_metadata` should be a JSON object that includes user-specific info, such as their first and last name.
   *
   * Note: When using the GoTrueAdminApi and wanting to modify a user's metadata,
   * this attribute is used instead of UserAttributes data.
   *
   */
  user_metadata?: object

  /**
   * A custom data object to store the user's application specific metadata. This maps to the `auth.users.app_metadata` column.
   *
   * Only a service role can modify.
   *
   * The `app_metadata` should be a JSON object that includes app-specific info, such as identity providers, roles, and other
   * access control information.
   */
  app_metadata?: object

  /**
   * Sets the user's email as confirmed when true, or unconfirmed when false.
   *
   * Only a service role can modify.
   */
  email_confirm?: boolean

  /**
   * Sets the user's phone as confirmed when true, or unconfirmed when false.
   *
   * Only a service role can modify.
   */
  phone_confirm?: boolean

  /**
   * Determines how long a user is banned for.
   *
   * The format for the ban duration follows a strict sequence of decimal numbers with a unit suffix.
   * Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
   *
   * For example, some possible durations include: '300ms', '2h45m'.
   *
   * Setting the ban duration to 'none' lifts the ban on the user.
   */
  ban_duration?: string | 'none'

  /**
   * The `role` claim set in the user's access token JWT.
   *
   * When a user signs up, this role is set to `authenticated` by default. You should only modify the `role` if you need to provision several levels of admin access that have different permissions on individual columns in your database.
   *
   * Setting this role to `service_role` is not recommended as it grants the user admin privileges.
   */
  role?: string

  /**
   * The `password_hash` for the user's password.
   *
   * Allows you to specify a password hash for the user. This is useful for migrating a user's password hash from another service.
   *
   * Supports bcrypt, scrypt (firebase), and argon2 password hashes.
   */
  password_hash?: string

  /**
   * The `id` for the user.
   *
   * Allows you to overwrite the default `id` set for the user.
   */
  id?: string
}

export interface Subscription {
  /**
   * A unique identifier for this subscription, set by the client.
   * This is an internal identifier used for managing callbacks and should not be
   * relied upon by application code. Use the unsubscribe() method to remove listeners.
   */
  id: string | symbol
  /**
   * The function to call every time there is an event. eg: (eventName) => {}
   */
  callback: (event: AuthChangeEvent, session: Session | null) => void
  /**
   * Call this to remove the listener.
   */
  unsubscribe: () => void
}

export type SignInAnonymouslyCredentials = {
  options?: {
    /**
     * A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
     *
     * The `data` should be a JSON object that includes user-specific info, such as their first and last name.
     */
    data?: object
    /** Verification token received when the user completes the captcha on the site. */
    captchaToken?: string
  }
}

export type SignUpWithPasswordCredentials = PasswordCredentialsBase & {
  options?: {
    emailRedirectTo?: string // only for email
    data?: object
    captchaToken?: string
    channel?: 'sms' | 'whatsapp' // only for phone
  }
}

type PasswordCredentialsBase =
  | { email: string; password: string }
  | { phone: string; password: string }

export type SignInWithPasswordCredentials = PasswordCredentialsBase & {
  options?: {
    captchaToken?: string
  }
}

export type SignInWithPasswordlessCredentials =
  | {
      /** The user's email address. */
      email: string
      options?: {
        /** The redirect url embedded in the email link */
        emailRedirectTo?: string
        /** If set to false, this method will not create a new user. Defaults to true. */
        shouldCreateUser?: boolean
        /**
         * A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
         *
         * The `data` should be a JSON object that includes user-specific info, such as their first and last name.
         */
        data?: object
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
      }
    }
  | {
      /** The user's phone number. */
      phone: string
      options?: {
        /** If set to false, this method will not create a new user. Defaults to true. */
        shouldCreateUser?: boolean
        /**
         * A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
         *
         * The `data` should be a JSON object that includes user-specific info, such as their first and last name.
         */
        data?: object
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
        /** Messaging channel to use (e.g. whatsapp or sms) */
        channel?: 'sms' | 'whatsapp'
      }
    }

export type AuthFlowType = 'implicit' | 'pkce' | (string & {})
export type SignInWithOAuthCredentials = {
  /** One of the providers supported by GoTrue. */
  provider: Provider
  options?: {
    /** A URL to send the user to after they are confirmed. */
    redirectTo?: string
    /** A space-separated list of scopes granted to the OAuth application. */
    scopes?: string
    /** An object of query params */
    queryParams?: { [key: string]: string }
    /** If set to true does not immediately redirect the current browser context to visit the OAuth authorization page for the provider. */
    skipBrowserRedirect?: boolean
  }
}

export type SignInWithIdTokenCredentials = {
  /** Provider name or OIDC `iss` value identifying which provider should be used to verify the provided token. Supported names: `google`, `apple`, `azure`, `facebook`, `kakao`. Use the `custom:` prefix for custom OIDC providers (e.g. `custom:my-oidc-provider`). */
  provider: 'google' | 'apple' | 'azure' | 'facebook' | 'kakao' | `custom:${string}` | (string & {})
  /** OIDC ID token issued by the specified provider. The `iss` claim in the ID token must match the supplied provider. Some ID tokens contain an `at_hash` which require that you provide an `access_token` value to be accepted properly. If the token contains a `nonce` claim you must supply the nonce used to obtain the ID token. */
  token: string
  /** If the ID token contains an `at_hash` claim, then the hash of this value is compared to the value in the ID token. */
  access_token?: string
  /** If the ID token contains a `nonce` claim, then the hash of this value is compared to the value in the ID token. */
  nonce?: string
  options?: {
    /** Verification token received when the user completes the captcha on the site. */
    captchaToken?: string
  }
}

export type SolanaWallet = {
  signIn?: (...inputs: SolanaSignInInput[]) => Promise<SolanaSignInOutput | SolanaSignInOutput[]>
  publicKey?: {
    toBase58: () => string
  } | null

  signMessage?: (message: Uint8Array, encoding?: 'utf8' | string) => Promise<Uint8Array> | undefined
}

export type SolanaWeb3Credentials =
  | {
      chain: 'solana'

      /** Wallet interface to use. If not specified will default to `window.solana`. */
      wallet?: SolanaWallet

      /** Optional statement to include in the Sign in with Solana message. Must not include new line characters. Most wallets like Phantom **require specifying a statement!** */
      statement?: string

      options?: {
        /** URL to use with the wallet interface. Some wallets do not allow signing a message for URLs different from the current page. */
        url?: string

        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string

        signInWithSolana?: Partial<
          Omit<SolanaSignInInput, 'version' | 'chain' | 'domain' | 'uri' | 'statement'>
        >
      }
    }
  | {
      chain: 'solana'

      /** Sign in with Solana compatible message. Must include `Issued At`, `URI` and `Version`. */
      message: string

      /** Ed25519 signature of the message. */
      signature: Uint8Array

      options?: {
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
      }
    }

export type EthereumWallet = EIP1193Provider

export type EthereumWeb3Credentials =
  | {
      chain: 'ethereum'

      /** Wallet interface to use. If not specified will default to `window.ethereum`. */
      wallet?: EthereumWallet

      /** Optional statement to include in the Sign in with Ethereum message. Must not include new line characters. Most wallets like Phantom **require specifying a statement!** */
      statement?: string

      options?: {
        /** URL to use with the wallet interface. Some wallets do not allow signing a message for URLs different from the current page. */
        url?: string

        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string

        signInWithEthereum?: Partial<
          Omit<EthereumSignInInput, 'version' | 'domain' | 'uri' | 'statement'>
        >
      }
    }
  | {
      chain: 'ethereum'

      /** Sign in with Ethereum compatible message. Must include `Issued At`, `URI` and `Version`. */
      message: string

      /** Ethereum curve (secp256k1) signature of the message. */
      signature: Hex

      options?: {
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
      }
    }

export type Web3Credentials = SolanaWeb3Credentials | EthereumWeb3Credentials

export type VerifyOtpParams = VerifyMobileOtpParams | VerifyEmailOtpParams | VerifyTokenHashParams
export interface VerifyMobileOtpParams {
  /** The user's phone number. */
  phone: string
  /** The otp sent to the user's phone number. */
  token: string
  /** The user's verification type. */
  type: MobileOtpType
  options?: {
    /** A URL to send the user to after they are confirmed. */
    redirectTo?: string

    /**
     * Verification token received when the user completes the captcha on the site.
     *
     * @deprecated
     */
    captchaToken?: string
  }
}
export interface VerifyEmailOtpParams {
  /** The user's email address. */
  email: string
  /** The otp sent to the user's email address. */
  token: string
  /** The user's verification type. */
  type: EmailOtpType
  options?: {
    /** A URL to send the user to after they are confirmed. */
    redirectTo?: string

    /** Verification token received when the user completes the captcha on the site.
     *
     * @deprecated
     */
    captchaToken?: string
  }
}

export interface VerifyTokenHashParams {
  /** The token hash used in an email link */
  token_hash: string

  /** The user's verification type. */
  type: EmailOtpType
}

export type MobileOtpType = 'sms' | 'phone_change' | (string & {})
export type EmailOtpType =
  | 'signup'
  | 'invite'
  | 'magiclink'
  | 'recovery'
  | 'email_change'
  | 'email'
  | (string & {})

export type ResendParams =
  | {
      type: Extract<EmailOtpType, 'signup' | 'email_change'>
      email: string
      options?: {
        /** A URL to send the user to after they have signed-in. */
        emailRedirectTo?: string
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
      }
    }
  | {
      type: Extract<MobileOtpType, 'sms' | 'phone_change'>
      phone: string
      options?: {
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
      }
    }

export type SignInWithSSO =
  | {
      /** UUID of the SSO provider to invoke single-sign on to. */
      providerId: string

      options?: {
        /** A URL to send the user to after they have signed-in. */
        redirectTo?: string
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
        /**
         * If set to true, the redirect will not happen on the client side.
         * This parameter is used when you wish to handle the redirect yourself.
         * Defaults to false.
         */
        skipBrowserRedirect?: boolean
      }
    }
  | {
      /** Domain name of the organization for which to invoke single-sign on. */
      domain: string

      options?: {
        /** A URL to send the user to after they have signed-in. */
        redirectTo?: string
        /** Verification token received when the user completes the captcha on the site. */
        captchaToken?: string
        /**
         * If set to true, the redirect will not happen on the client side.
         * This parameter is used when you wish to handle the redirect yourself.
         * Defaults to false.
         */
        skipBrowserRedirect?: boolean
      }
    }

export type GenerateSignupLinkParams = {
  type: 'signup'
  email: string
  password: string
  options?: Pick<GenerateLinkOptions, 'data' | 'redirectTo'>
}

export type GenerateInviteOrMagiclinkParams = {
  type: 'invite' | 'magiclink'
  /** The user's email */
  email: string
  options?: Pick<GenerateLinkOptions, 'data' | 'redirectTo'>
}

export type GenerateRecoveryLinkParams = {
  type: 'recovery'
  /** The user's email */
  email: string
  options?: Pick<GenerateLinkOptions, 'redirectTo'>
}

export type GenerateEmailChangeLinkParams = {
  type: 'email_change_current' | 'email_change_new'
  /** The user's email */
  email: string
  /**
   * The user's new email. Only required if type is 'email_change_current' or 'email_change_new'.
   */
  newEmail: string
  options?: Pick<GenerateLinkOptions, 'redirectTo'>
}

export interface GenerateLinkOptions {
  /**
   * A custom data object to store the user's metadata. This maps to the `auth.users.raw_user_meta_data` column.
   *
   * The `data` should be a JSON object that includes user-specific info, such as their first and last name.
   */
  data?: object
  /** The URL which will be appended to the email link generated. */
  redirectTo?: string
}

export type GenerateLinkParams =
  | GenerateSignupLinkParams
  | GenerateInviteOrMagiclinkParams
  | GenerateRecoveryLinkParams
  | GenerateEmailChangeLinkParams

export type GenerateLinkResponse = RequestResultSafeDestructure<{
  properties: GenerateLinkProperties
  user: User
}>

/** The properties related to the email link generated  */
export type GenerateLinkProperties = {
  /**
   * The email link to send to the user.
   * The action_link follows the following format: auth/v1/verify?type={verification_type}&token={hashed_token}&redirect_to={redirect_to}
   * */
  action_link: string
  /**
   * The raw email OTP.
   * You should send this in the email if you want your users to verify using an OTP instead of the action link.
   * */
  email_otp: string
  /**
   * The hashed token appended to the action link.
   * */
  hashed_token: string
  /** The URL appended to the action link. */
  redirect_to: string
  /** The verification type that the email link is associated to. */
  verification_type: GenerateLinkType
}

export type GenerateLinkType =
  | 'signup'
  | 'invite'
  | 'magiclink'
  | 'recovery'
  | 'email_change_current'
  | 'email_change_new'

export type MFAEnrollParams = MFAEnrollTOTPParams | MFAEnrollPhoneParams | MFAEnrollWebauthnParams

export type MFAUnenrollParams = {
  /** ID of the factor being unenrolled. */
  factorId: string
}

type MFAVerifyParamsBase = {
  /** ID of the factor being verified. Returned in enroll(). */
  factorId: string
  /** ID of the challenge being verified. Returned in challenge(). */
  challengeId: string
}

type MFAVerifyTOTPParamFields = {
  /** Verification code provided by the user. */
  code: string
}

export type MFAVerifyTOTPParams = MFAVerifyParamsBase & MFAVerifyTOTPParamFields

type MFAVerifyPhoneParamFields = MFAVerifyTOTPParamFields

export type MFAVerifyPhoneParams = MFAVerifyParamsBase & MFAVerifyPhoneParamFields

type MFAVerifyWebauthnParamFieldsBase = {
  /** Relying party ID */
  rpId: string
  /** Relying party origins */
  rpOrigins?: string[]
}

type MFAVerifyWebauthnCredentialParamFields<T extends 'create' | 'request' = 'create' | 'request'> =
  {
    /** Operation type */
    type: T
    /** Creation response from the authenticator (for enrollment/unverified factors) */
    credential_response: T extends 'create' ? RegistrationCredential : AuthenticationCredential
  }

/**
 * WebAuthn-specific fields for MFA verification.
 * Supports both credential creation (registration) and request (authentication) flows.
 * @template T - Type of WebAuthn operation: 'create' for registration, 'request' for authentication
 */
export type MFAVerifyWebauthnParamFields<T extends 'create' | 'request' = 'create' | 'request'> = {
  webauthn: MFAVerifyWebauthnParamFieldsBase & MFAVerifyWebauthnCredentialParamFields<T>
}

/**
 * Parameters for WebAuthn MFA verification.
 * Used to verify WebAuthn credentials after challenge.
 * @template T - Type of WebAuthn operation: 'create' for registration, 'request' for authentication
 * @see {@link https://w3c.github.io/webauthn/#sctn-verifying-assertion W3C WebAuthn Spec - Verifying an Authentication Assertion}
 */
export type MFAVerifyWebauthnParams<T extends 'create' | 'request' = 'create' | 'request'> =
  MFAVerifyParamsBase & MFAVerifyWebauthnParamFields<T>

export type MFAVerifyParams = MFAVerifyTOTPParams | MFAVerifyPhoneParams | MFAVerifyWebauthnParams

type MFAChallengeParamsBase = {
  /** ID of the factor to be challenged. Returned in enroll(). */
  factorId: string
}

const MFATOTPChannels = ['sms', 'whatsapp'] as const
export type MFATOTPChannel = (typeof MFATOTPChannels)[number]

export type MFAChallengeTOTPParams = MFAChallengeParamsBase

type MFAChallengePhoneParamFields<Channel extends MFATOTPChannel = MFATOTPChannel> = {
  /** Messaging channel to use (e.g. whatsapp or sms). Only relevant for phone factors */
  channel: Channel
}

export type MFAChallengePhoneParams = MFAChallengeParamsBase & MFAChallengePhoneParamFields

/** WebAuthn parameters for WebAuthn factor challenge */
type MFAChallengeWebauthnParamFields = {
  webauthn: {
    /** Relying party ID */
    rpId: string
    /** Relying party origins*/
    rpOrigins?: string[]
  }
}

/**
 * Parameters for initiating a WebAuthn MFA challenge.
 * Includes Relying Party information needed for WebAuthn ceremonies.
 * @see {@link https://w3c.github.io/webauthn/#sctn-rp-operations W3C WebAuthn Spec - Relying Party Operations}
 */
export type MFAChallengeWebauthnParams = MFAChallengeParamsBase & MFAChallengeWebauthnParamFields

export type MFAChallengeParams =
  | MFAChallengeTOTPParams
  | MFAChallengePhoneParams
  | MFAChallengeWebauthnParams

type MFAChallengeAndVerifyParamsBase = Omit<MFAVerifyParamsBase, 'challengeId'>

type MFAChallengeAndVerifyTOTPParamFields = MFAVerifyTOTPParamFields

type MFAChallengeAndVerifyTOTPParams = MFAChallengeAndVerifyParamsBase &
  MFAChallengeAndVerifyTOTPParamFields

export type MFAChallengeAndVerifyParams = MFAChallengeAndVerifyTOTPParams

/**
 * Data returned after successful MFA verification.
 * Contains new session tokens and updated user information.
 */
export type AuthMFAVerifyResponseData = {
  /** New access token (JWT) after successful verification. */
  access_token: string

  /** Type of token, always `bearer`. */
  token_type: 'bearer'

  /** Number of seconds in which the access token will expire. */
  expires_in: number

  /** Refresh token you can use to obtain new access tokens when expired. */
  refresh_token: string

  /** Updated user profile. */
  user: User
}

/**
 * Response type for MFA verification operations.
 * Returns session tokens on successful verification.
 */
export type AuthMFAVerifyResponse = RequestResult<AuthMFAVerifyResponseData>

export type AuthMFAEnrollResponse =
  | AuthMFAEnrollTOTPResponse
  | AuthMFAEnrollPhoneResponse
  | AuthMFAEnrollWebauthnResponse

export type AuthMFAUnenrollResponse = RequestResult<{
  /** ID of the factor that was successfully unenrolled. */
  id: string
}>

type AuthMFAChallengeResponseBase<T extends FactorType> = {
  /** ID of the newly created challenge. */
  id: string

  /** Factor Type which generated the challenge */
  type: T

  /** Timestamp in UNIX seconds when this challenge will no longer be usable. */
  expires_at: number
}

type AuthMFAChallengeTOTPResponseFields = {
  /** no extra fields for now, kept for consistency and for possible future changes  */
}

export type AuthMFAChallengeTOTPResponse = RequestResult<
  AuthMFAChallengeResponseBase<'totp'> & AuthMFAChallengeTOTPResponseFields
>

type AuthMFAChallengePhoneResponseFields = {
  /** no extra fields for now, kept for consistency and for possible future changes  */
}

export type AuthMFAChallengePhoneResponse = RequestResult<
  AuthMFAChallengeResponseBase<'phone'> & AuthMFAChallengePhoneResponseFields
>

type AuthMFAChallengeWebauthnResponseFields = {
  webauthn:
    | {
        type: 'create'
        credential_options: { publicKey: PublicKeyCredentialCreationOptionsFuture }
      }
    | {
        type: 'request'
        credential_options: { publicKey: PublicKeyCredentialRequestOptionsFuture }
      }
}

/**
 * Response type for WebAuthn MFA challenge.
 * Contains credential creation or request options from the server.
 * @see {@link https://w3c.github.io/webauthn/#sctn-credential-creation W3C WebAuthn Spec - Credential Creation}
 */
export type AuthMFAChallengeWebauthnResponse = RequestResult<
  AuthMFAChallengeResponseBase<'webauthn'> & AuthMFAChallengeWebauthnResponseFields
>

type AuthMFAChallengeWebauthnResponseFieldsJSON = {
  webauthn:
    | {
        type: 'create'
        credential_options: { publicKey: ServerCredentialCreationOptions }
      }
    | {
        type: 'request'
        credential_options: { publicKey: ServerCredentialRequestOptions }
      }
}

/**
 * JSON-serializable version of WebAuthn challenge response.
 * Used for server communication with base64url-encoded binary fields.
 */
export type AuthMFAChallengeWebauthnResponseDataJSON = AuthMFAChallengeResponseBase<'webauthn'> &
  AuthMFAChallengeWebauthnResponseFieldsJSON

/**
 * Server response type for WebAuthn MFA challenge.
 * Contains JSON-formatted WebAuthn options ready for browser API.
 */
export type AuthMFAChallengeWebauthnServerResponse =
  RequestResult<AuthMFAChallengeWebauthnResponseDataJSON>

export type AuthMFAChallengeResponse =
  | AuthMFAChallengeTOTPResponse
  | AuthMFAChallengePhoneResponse
  | AuthMFAChallengeWebauthnResponse

/** response of ListFactors, which should contain all the types of factors that are available, this ensures we always include all */
export type AuthMFAListFactorsResponse<T extends typeof FactorTypes = typeof FactorTypes> =
  RequestResult<
    {
      /** All available factors (verified and unverified). */
      all: Factor[]

      // Dynamically create a property for each factor type with only verified factors
    } & {
      [K in T[number]]: Factor<K, 'verified'>[]
    }
  >

export type AuthenticatorAssuranceLevels = 'aal1' | 'aal2' | (string & {})

export type AuthMFAGetAuthenticatorAssuranceLevelResponse = RequestResult<{
  /** Current AAL level of the session. */
  currentLevel: AuthenticatorAssuranceLevels | null

  /**
   * Next possible AAL level for the session. If the next level is higher
   * than the current one, the user should go through MFA.
   *
   * @see {@link GoTrueMFAApi#challenge}
   */
  nextLevel: AuthenticatorAssuranceLevels | null

  /**
   * A list of all authentication methods attached to this session. Use
   * the information here to detect the last time a user verified a
   * factor, for example if implementing a step-up scenario.
   *
   * Supports both RFC-8176 compliant format (string[]) and detailed format (AMREntry[]).
   * - String format: ['password', 'otp'] - RFC-8176 compliant
   * - Object format: [{ method: 'password', timestamp: 1234567890 }] - includes timestamps
   */
  currentAuthenticationMethods: AMREntry[] | string[]
}>

/**
 * Contains the full multi-factor authentication API.
 *
 */
export interface GoTrueMFAApi {
  /**
   * Starts the enrollment process for a new Multi-Factor Authentication (MFA)
   * factor. This method creates a new `unverified` factor.
   * To verify a factor, present the QR code or secret to the user and ask them to add it to their
   * authenticator app.
   * The user has to enter the code from their authenticator app to verify it.
   *
   * Upon verifying a factor, all other sessions are logged out and the current session's authenticator level is promoted to `aal2`.
   *
   * @category Auth
   * @subcategory Auth MFA
   *
   * @remarks
   * - Use `totp` or `phone` as the `factorType` and use the returned `id` to create a challenge.
   * - To create a challenge, see [`mfa.challenge()`](/docs/reference/javascript/auth-mfa-challenge).
   * - To verify a challenge, see [`mfa.verify()`](/docs/reference/javascript/auth-mfa-verify).
   * - To create and verify a TOTP challenge in a single step, see [`mfa.challengeAndVerify()`](/docs/reference/javascript/auth-mfa-challengeandverify).
   * - To generate a QR code for the `totp` secret in Next.js, you can do the following:
   * ```html
   * <Image src={data.totp.qr_code} alt={data.totp.uri} layout="fill"></Image>
   * ```
   * - The `challenge` and `verify` steps are separated when using Phone factors as the user will need time to receive and input the code obtained from the SMS in challenge.
   *
   * @example Enroll a time-based, one-time password (TOTP) factor
   * ```js
   * const { data, error } = await supabase.auth.mfa.enroll({
   *   factorType: 'totp',
   *   friendlyName: 'your_friendly_name'
   * })
   *
   * // Use the id to create a challenge.
   * // The challenge can be verified by entering the code generated from the authenticator app.
   * // The code will be generated upon scanning the qr_code or entering the secret into the authenticator app.
   * const { id, type, totp: { qr_code, secret, uri }, friendly_name } = data
   * const challenge = await supabase.auth.mfa.challenge({ factorId: id });
   * ```
   *
   * @exampleResponse Enroll a time-based, one-time password (TOTP) factor
   * ```json
   * {
   *   data: {
   *     id: '<ID>',
   *     type: 'totp'
   *     totp: {
   *       qr_code: '<QR_CODE_AS_SVG_DATA>',
   *       secret: '<SECRET>',
   *       uri: '<URI>',
   *     }
   *     friendly_name?: 'Important app'
   *   },
   *   error: null
   * }
   * ```
   *
   * @example Enroll a Phone Factor
   * ```js
   * const { data, error } = await supabase.auth.mfa.enroll({
   *   factorType: 'phone',
   *   friendlyName: 'your_friendly_name',
   *   phone: '+12345678',
   * })
   *
   * // Use the id to create a challenge and send an SMS with a code to the user.
   * const { id, type, friendly_name, phone } = data
   *
   * const challenge = await supabase.auth.mfa.challenge({ factorId: id });
   * ```
   *
   * @exampleResponse Enroll a Phone Factor
   * ```json
   * {
   *   data: {
   *     id: '<ID>',
   *     type: 'phone',
   *     friendly_name?: 'Important app',
   *     phone: '+5787123456'
   *   },
   *   error: null
   * }
   * ```
   */
  enroll(params: MFAEnrollTOTPParams): Promise<AuthMFAEnrollTOTPResponse>
  enroll(params: MFAEnrollPhoneParams): Promise<AuthMFAEnrollPhoneResponse>
  enroll(params: MFAEnrollWebauthnParams): Promise<AuthMFAEnrollWebauthnResponse>
  enroll(params: MFAEnrollParams): Promise<AuthMFAEnrollResponse>

  /**
   * Prepares a challenge used to verify that a user has access to a MFA
   * factor.
   *
   * @category Auth
   * @subcategory Auth MFA
   *
   * @remarks
   * - An [enrolled factor](/docs/reference/javascript/auth-mfa-enroll) is required before creating a challenge.
   * - To verify a challenge, see [`mfa.verify()`](/docs/reference/javascript/auth-mfa-verify).
   * - A phone factor sends a code to the user upon challenge. The channel defaults to `sms` unless otherwise specified.
   *
   * @example Create a challenge for a factor
   * ```js
   * const { data, error } = await supabase.auth.mfa.challenge({
   *   factorId: '34e770dd-9ff9-416c-87fa-43b31d7ef225'
   * })
   * ```
   *
   * @exampleResponse Create a challenge for a factor
   * ```json
   * {
   *   data: {
   *     id: '<ID>',
   *     type: 'totp',
   *     expires_at: 1700000000
   *   },
   *   error: null
   * }
   * ```
   *
   * @example Create a challenge for a phone factor
   * ```js
   * const { data, error } = await supabase.auth.mfa.challenge({
   *   factorId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
   * })
   * ```
   *
   * @exampleResponse Create a challenge for a phone factor
   * ```json
   * {
   *   data: {
   *     id: '<ID>',
   *     type: 'phone',
   *     expires_at: 1700000000
   *   },
   *   error: null
   * }
   * ```
   *
   * @example Create a challenge for a phone factor (WhatsApp)
   * ```js
   * const { data, error } = await supabase.auth.mfa.challenge({
   *   factorId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
   *   channel: 'whatsapp',
   * })
   * ```
   *
   * @exampleResponse Create a challenge for a phone factor (WhatsApp)
   * ```json
   * {
   *   data: {
   *     id: '<ID>',
   *     expires_at: 1700000000
   *   },
   *   error: null
   * }
   * ```
   */
  challenge(params: MFAChallengeTOTPParams): Promise<Prettify<AuthMFAChallengeTOTPResponse>>
  challenge(params: MFAChallengePhoneParams): Promise<Prettify<AuthMFAChallengePhoneResponse>>
  challenge(params: MFAChallengeWebauthnParams): Promise<Prettify<AuthMFAChallengeWebauthnResponse>>
  challenge(params: MFAChallengeParams): Promise<AuthMFAChallengeResponse>

  /**
   * Verifies a code against a challenge. The verification code is
   * provided by the user by entering a code seen in their authenticator app.
   *
   * @category Auth
   * @subcategory Auth MFA
   *
   * @remarks
   * - To verify a challenge, please [create a challenge](/docs/reference/javascript/auth-mfa-challenge) first.
   *
   * @example Verify a challenge for a factor
   * ```js
   * const { data, error } = await supabase.auth.mfa.verify({
   *   factorId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
   *   challengeId: '4034ae6f-a8ce-4fb5-8ee5-69a5863a7c15',
   *   code: '123456'
   * })
   * ```
   *
   * @exampleResponse Verify a challenge for a factor
   * ```json
   * {
   *   data: {
   *     access_token: '<ACCESS_TOKEN>',
   *     token_type: 'Bearer',
   *     expires_in: 3600,
   *     refresh_token: '<REFRESH_TOKEN>',
   *     user: {
   *       id: '11111111-1111-1111-1111-111111111111',
   *       aud: 'authenticated',
   *       role: 'authenticated',
   *       email: 'example@email.com',
   *       email_confirmed_at: '2024-01-01T00:00:00Z',
   *       phone: '',
   *       confirmation_sent_at: '2024-01-01T00:00:00Z',
   *       confirmed_at: '2024-01-01T00:00:00Z',
   *       last_sign_in_at: '2024-01-01T00:00:00Z',
   *       app_metadata: {
   *         provider: 'email',
   *         providers: [
   *           "email",
   *         ]
   *       },
   *       user_metadata: {},
   *       identities: [
   *         {
   *           "identity_id": "22222222-2222-2222-2222-222222222222",
   *           "id": "11111111-1111-1111-1111-111111111111",
   *           "user_id": "11111111-1111-1111-1111-111111111111",
   *           "identity_data": {
   *             "email": "example@email.com",
   *             "email_verified": true,
   *             "phone_verified": false,
   *             "sub": "11111111-1111-1111-1111-111111111111"
   *           },
   *           "provider": "email",
   *           "last_sign_in_at": "2024-01-01T00:00:00Z",
   *           "created_at": "2024-01-01T00:00:00Z",
   *           "updated_at": "2024-01-01T00:00:00Z",
   *           "email": "email@example.com"
   *         },
   *       ],
   *       created_at: '2024-01-01T00:00:00Z',
   *       updated_at: '2024-01-01T00:00:00Z',
   *       is_anonymous: false,
   *       factors: [
   *         "id": '<ID>',
   *         "friendly_name": 'Important Auth App',
   *         "factor_type": 'totp',
   *         "status": 'verified',
   *         "created_at": "2024-01-01T00:00:00Z",
   *         "updated_at": "2024-01-01T00:00:00Z"
   *       ]
   *     }
   *   }
   *   error: null
   * }
   * ```
   */
  verify(params: MFAVerifyTOTPParams): Promise<AuthMFAVerifyResponse>
  verify(params: MFAVerifyPhoneParams): Promise<AuthMFAVerifyResponse>
  verify(params: MFAVerifyWebauthnParams): Promise<AuthMFAVerifyResponse>
  verify(params: MFAVerifyParams): Promise<AuthMFAVerifyResponse>

  /**
   * Unenroll removes a MFA factor.
   * A user has to have an `aal2` authenticator level in order to unenroll a `verified` factor.
   *
   * @category Auth
   * @subcategory Auth MFA
   *
   * @example Unenroll a factor
   * ```js
   * const { data, error } = await supabase.auth.mfa.unenroll({
   *   factorId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
   * })
   * ```
   *
   * @exampleResponse Unenroll a factor
   * ```json
   * {
   *   data: {
   *     id: '<FACTOR_ID>'
   *   },
   *   error: null
   * }
   * ```
   */
  unenroll(params: MFAUnenrollParams): Promise<AuthMFAUnenrollResponse>

  /**
   * Helper method which creates a challenge and immediately uses the given code to verify against it thereafter. The verification code is
   * provided by the user by entering a code seen in their authenticator app.
   *
   * @category Auth
   * @subcategory Auth MFA
   *
   * @remarks
   * - Intended for use with only TOTP factors.
   * - An [enrolled factor](/docs/reference/javascript/auth-mfa-enroll) is required before invoking `challengeAndVerify()`.
   * - Executes [`mfa.challenge()`](/docs/reference/javascript/auth-mfa-challenge) and [`mfa.verify()`](/docs/reference/javascript/auth-mfa-verify) in a single step.
   *
   * @example Create and verify a challenge for a factor
   * ```js
   * const { data, error } = await supabase.auth.mfa.challengeAndVerify({
   *   factorId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
   *   code: '123456'
   * })
   * ```
   *
   * @exampleResponse Create and verify a challenge for a factor
   * ```json
   * {
   *   data: {
   *     access_token: '<ACCESS_TOKEN>',
   *     token_type: 'Bearer',
   *     expires_in: 3600,
   *     refresh_token: '<REFRESH_TOKEN>',
   *     user: {
   *       id: '11111111-1111-1111-1111-111111111111',
   *       aud: 'authenticated',
   *       role: 'authenticated',
   *       email: 'example@email.com',
   *       email_confirmed_at: '2024-01-01T00:00:00Z',
   *       phone: '',
   *       confirmation_sent_at: '2024-01-01T00:00:00Z',
   *       confirmed_at: '2024-01-01T00:00:00Z',
   *       last_sign_in_at: '2024-01-01T00:00:00Z',
   *       app_metadata: {
   *         provider: 'email',
   *         providers: [
   *           "email",
   *         ]
   *       },
   *       user_metadata: {},
   *       identities: [
   *         {
   *           "identity_id": "22222222-2222-2222-2222-222222222222",
   *           "id": "11111111-1111-1111-1111-111111111111",
   *           "user_id": "11111111-1111-1111-1111-111111111111",
   *           "identity_data": {
   *             "email": "example@email.com",
   *             "email_verified": true,
   *             "phone_verified": false,
   *             "sub": "11111111-1111-1111-1111-111111111111"
   *           },
   *           "provider": "email",
   *           "last_sign_in_at": "2024-01-01T00:00:00Z",
   *           "created_at": "2024-01-01T00:00:00Z",
   *           "updated_at": "2024-01-01T00:00:00Z",
   *           "email": "email@example.com"
   *         },
   *       ],
   *       created_at: '2024-01-01T00:00:00Z',
   *       updated_at: '2024-01-01T00:00:00Z',
   *       is_anonymous: false,
   *       factors: [
   *         "id": '<ID>',
   *         "friendly_name": 'Important Auth App',
   *         "factor_type": 'totp',
   *         "status": 'verified',
   *         "created_at": "2024-01-01T00:00:00Z",
   *         "updated_at": "2024-01-01T00:00:00Z"
   *       ]
   *     }
   *   }
   *   error: null
   * }
   * ```
   */
  challengeAndVerify(params: MFAChallengeAndVerifyParams): Promise<AuthMFAVerifyResponse>

  /**
   * Returns the list of MFA factors enabled for this user.
   *
   * @see {@link GoTrueMFAApi#enroll}
   * @see {@link GoTrueMFAApi#getAuthenticatorAssuranceLevel}
   * @see {@link GoTrueClient#getUser}
   *
   *
   * @category Auth
   * @subcategory Auth MFA
   */
  listFactors(): Promise<AuthMFAListFactorsResponse>

  /**
   * Returns the Authenticator Assurance Level (AAL) for the active session.
   *
   * - `aal1` (or `null`) means that the user's identity has been verified only
   * with a conventional login (email+password, OTP, magic link, social login,
   * etc.).
   * - `aal2` means that the user's identity has been verified both with a conventional login and at least one MFA factor.
   *
   * When called without a JWT parameter, this method is fairly quick (microseconds)
   * and rarely uses the network. When a JWT is provided (useful in server-side
   * environments like Edge Functions where no session is stored), this method
   * will make a network request to validate the user and fetch their MFA factors.
   *
   * @param jwt Takes in an optional access token JWT. If no JWT is provided, the JWT from the current session is used.
   *
   * @category Auth
   * @subcategory Auth MFA
   *
   * @remarks
   * - Authenticator Assurance Level (AAL) is the measure of the strength of an authentication mechanism.
   * - In Supabase, having an AAL of `aal1` refers to having the 1st factor of authentication such as an email and password or OAuth sign-in while `aal2` refers to the 2nd factor of authentication such as a time-based, one-time-password (TOTP) or Phone factor.
   * - If the user has a verified factor, the `nextLevel` field will return `aal2`, else, it will return `aal1`.
   * - An optional `jwt` parameter can be passed to check the AAL level of a specific JWT instead of the current session.
   *
   * @example Get the AAL details of a session
   * ```js
   * const { data, error } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel()
   * const { currentLevel, nextLevel, currentAuthenticationMethods } = data
   * ```
   *
   * @exampleResponse Get the AAL details of a session
   * ```json
   * {
   *   data: {
   *     currentLevel: 'aal1',
   *     nextLevel: 'aal2',
   *     currentAuthenticationMethods: [
   *       {
   *         method: 'password',
   *         timestamp: 1700000000
   *       }
   *     ]
   *   }
   *   error: null
   * }
   * ```
   *
   * @example Get the AAL details for a specific JWT
   * ```js
   * const { data, error } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel(jwt)
   * ```
   */
  getAuthenticatorAssuranceLevel(
    jwt?: string
  ): Promise<AuthMFAGetAuthenticatorAssuranceLevelResponse>

  // namespace for the webauthn methods
  webauthn: WebAuthnApi
}

/**
 * @experimental
 */
export type AuthMFAAdminDeleteFactorResponse = RequestResult<{
  /** ID of the factor that was successfully deleted. */
  id: string
}>
/**
 * @experimental
 */
export type AuthMFAAdminDeleteFactorParams = {
  /** ID of the MFA factor to delete. */
  id: string

  /** ID of the user whose factor is being deleted. */
  userId: string
}

/**
 * @experimental
 */
export type AuthMFAAdminListFactorsResponse = RequestResult<{
  /** All factors attached to the user. */
  factors: Factor[]
}>

/**
 * @experimental
 */
export type AuthMFAAdminListFactorsParams = {
  /** ID of the user. */
  userId: string
}

/**
 * Contains the full multi-factor authentication administration API.
 *
 * @experimental
 */
export interface GoTrueAdminMFAApi {
  /**
   * Lists all factors associated to a user.
   *
   *
   * @category Auth
   * @subcategory Auth Admin
   *
   * @example List all factors for a user
   * ```js
   * const { data, error } = await supabase.auth.admin.mfa.listFactors()
   * ```
   *
   * @exampleResponse List all factors for a user
   * ```json
   * {
   *   data: {
   *     factors: Factor[
   *       {
   *         id: '<ID>',
   *         friendly_name: 'Auth App Factor',
   *         factor_type: 'totp',
   *         status: 'verified',
   *         created_at: '2024-01-01T00:00:00Z',
   *         updated_at: '2024-01-01T00:00:00Z'
   *       }
   *     ]
   *   },
   *   error: null
   * }
   * ```
   */
  listFactors(params: AuthMFAAdminListFactorsParams): Promise<AuthMFAAdminListFactorsResponse>

  /**
   * Deletes a factor on a user. This will log the user out of all active
   * sessions if the deleted factor was verified.
   *
   * @see {@link GoTrueMFAApi#unenroll}
   *
   * @experimental
   *
   * @category Auth
   * @subcategory Auth Admin
   *
   * @example Delete a factor for a user
   * ```js
   * const { data, error } = await supabase.auth.admin.mfa.deleteFactor({
   *   id: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
   *   userId: 'a89baba7-b1b7-440f-b4bb-91026967f66b',
   * })
   * ```
   *
   * @exampleResponse Delete a factor for a user
   * ```json
   * {
   *   data: {
   *     id: '34e770dd-9ff9-416c-87fa-43b31d7ef225'
   *   },
   *   error: null
   * }
   * ```
   */
  deleteFactor(params: AuthMFAAdminDeleteFactorParams): Promise<AuthMFAAdminDeleteFactorResponse>
}

type AnyFunction = (...args: any[]) => any
type MaybePromisify<T> = T | Promise<T>

type PromisifyMethods<T> = {
  [K in keyof T]: T[K] extends AnyFunction
    ? (...args: Parameters<T[K]>) => MaybePromisify<ReturnType<T[K]>>
    : T[K]
}

export type SupportedStorage = PromisifyMethods<
  Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>
> & {
  /**
   * If set to `true` signals to the library that the storage medium is used
   * on a server and the values may not be authentic, such as reading from
   * request cookies. Implementations should not set this to true if the client
   * is used on a server that reads storage information from authenticated
   * sources, such as a secure database or file.
   */
  isServer?: boolean
}

export type InitializeResult = { error: AuthError | null }

export type CallRefreshTokenResult = RequestResult<Session>

export type Pagination = {
  [key: string]: any
  nextPage: number | null
  lastPage: number
  total: number
}

export type PageParams = {
  /** The page number */
  page?: number
  /** Number of items returned per page */
  perPage?: number
}

export type SignOut = {
  /**
   * Determines which sessions should be
   * logged out. Global means all
   * sessions by this account. Local
   * means only this session. Others
   * means all other sessions except the
   * current one. When using others,
   * there is no sign-out event fired on
   * the current session!
   */
  scope?: 'global' | 'local' | 'others'
}

type MFAEnrollParamsBase<T extends FactorType> = {
  /** The type of factor being enrolled. */
  factorType: T
  /** Human readable name assigned to the factor. */
  friendlyName?: string
}

type MFAEnrollTOTPParamFields = {
  /** Domain which the user is enrolled with. */
  issuer?: string
}

export type MFAEnrollTOTPParams = MFAEnrollParamsBase<'totp'> & MFAEnrollTOTPParamFields

type MFAEnrollPhoneParamFields = {
  /** Phone number associated with a factor. Number should conform to E.164 format */
  phone: string
}
export type MFAEnrollPhoneParams = MFAEnrollParamsBase<'phone'> & MFAEnrollPhoneParamFields

type MFAEnrollWebauthnFields = {
  /** no extra fields for now, kept for consistency and for possible future changes  */
}

/**
 * Parameters for enrolling a WebAuthn factor.
 * Creates an unverified WebAuthn factor that must be verified with a credential.
 * @see {@link https://w3c.github.io/webauthn/#sctn-registering-a-new-credential W3C WebAuthn Spec - Registering a New Credential}
 */
export type MFAEnrollWebauthnParams = MFAEnrollParamsBase<'webauthn'> & MFAEnrollWebauthnFields

type AuthMFAEnrollResponseBase<T extends FactorType> = {
  /** ID of the factor that was just enrolled (in an unverified state). */
  id: string

  /** Type of MFA factor.*/
  type: T

  /** Friendly name of the factor, useful for distinguishing between factors **/
  friendly_name?: string
}

type AuthMFAEnrollTOTPResponseFields = {
  /** TOTP enrollment information. */
  totp: {
    /** Contains a QR code encoding the authenticator URI. You can
     * convert it to a URL by prepending `data:image/svg+xml;utf-8,` to
     * the value. Avoid logging this value to the console. */
    qr_code: string

    /** The TOTP secret (also encoded in the QR code). Show this secret
     * in a password-style field to the user, in case they are unable to
     * scan the QR code. Avoid logging this value to the console. */
    secret: string

    /** The authenticator URI encoded within the QR code, should you need
     * to use it. Avoid loggin this value to the console. */
    uri: string
  }
}

export type AuthMFAEnrollTOTPResponse = RequestResult<
  AuthMFAEnrollResponseBase<'totp'> & AuthMFAEnrollTOTPResponseFields
>

type AuthMFAEnrollPhoneResponseFields = {
  /** Phone number of the MFA factor in E.164 format. Used to send messages  */
  phone: string
}

export type AuthMFAEnrollPhoneResponse = RequestResult<
  AuthMFAEnrollResponseBase<'phone'> & AuthMFAEnrollPhoneResponseFields
>

type AuthMFAEnrollWebauthnFields = {
  /** no extra fields for now, kept for consistency and for possible future changes  */
}

/**
 * Response type for WebAuthn factor enrollment.
 * Returns the enrolled factor ID and metadata.
 * @see {@link https://w3c.github.io/webauthn/#sctn-registering-a-new-credential W3C WebAuthn Spec - Registering a New Credential}
 */
export type AuthMFAEnrollWebauthnResponse = RequestResult<
  AuthMFAEnrollResponseBase<'webauthn'> & AuthMFAEnrollWebauthnFields
>

export type JwtHeader = {
  alg: 'RS256' | 'ES256' | 'HS256' | (string & {})
  kid: string
  typ: string
}

export type RequiredClaims = {
  iss: string
  sub: string
  aud: string | string[]
  exp: number
  iat: number
  role: string
  aal: AuthenticatorAssuranceLevels
  session_id: string
}

/**
 * JWT Payload containing claims for Supabase authentication tokens.
 *
 * Required claims (iss, aud, exp, iat, sub, role, aal, session_id) are inherited from RequiredClaims.
 * All other claims are optional as they can be customized via Custom Access Token Hooks.
 *
 * @see https://supabase.com/docs/guides/auth/jwt-fields
 */
export interface JwtPayload extends RequiredClaims {
  // Standard optional claims (can be customized via custom access token hooks)
  email?: string
  phone?: string
  is_anonymous?: boolean

  // Optional claims
  jti?: string
  nbf?: number
  app_metadata?: UserAppMetadata
  user_metadata?: UserMetadata
  /**
   * Authentication Method References.
   * Supports both RFC-8176 compliant format (string[]) and detailed format (AMREntry[]).
   * - String format: ['password', 'otp'] - RFC-8176 compliant
   * - Object format: [{ method: 'password', timestamp: 1234567890 }] - includes timestamps
   */
  amr?: AMREntry[] | string[]

  // Special claims (only in anon/service role tokens)
  ref?: string

  // Allow custom claims via custom access token hooks
  [key: string]: any
}

export interface JWK {
  kty: 'RSA' | 'EC' | 'oct' | (string & {})
  key_ops: string[]
  alg?: string
  kid?: string
  [key: string]: any
}

export const SIGN_OUT_SCOPES = ['global', 'local', 'others'] as const
export type SignOutScope = (typeof SIGN_OUT_SCOPES)[number]

/**
 * OAuth client grant types supported by the OAuth 2.1 server.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClientGrantType = 'authorization_code' | 'refresh_token' | (string & {})

/**
 * OAuth client response types supported by the OAuth 2.1 server.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClientResponseType = 'code'

/**
 * OAuth client type indicating whether the client can keep credentials confidential.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClientType = 'public' | 'confidential'

/**
 * OAuth client registration type.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClientRegistrationType = 'dynamic' | 'manual'

/**
 * OAuth client token endpoint authentication method.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClientTokenEndpointAuthMethod =
  | 'none'
  | 'client_secret_basic'
  | 'client_secret_post'

/**
 * OAuth client object returned from the OAuth 2.1 server.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClient = {
  /** Unique identifier for the OAuth client */
  client_id: string
  /** Human-readable name of the OAuth client */
  client_name: string
  /** Client secret (only returned on registration and regeneration) */
  client_secret?: string
  /** Type of OAuth client */
  client_type: OAuthClientType
  /** Token endpoint authentication method */
  token_endpoint_auth_method: OAuthClientTokenEndpointAuthMethod
  /** Registration type of the client */
  registration_type: OAuthClientRegistrationType
  /** URI of the OAuth client */
  client_uri?: string
  /** URI of the OAuth client's logo */
  logo_uri?: string
  /** Array of allowed redirect URIs */
  redirect_uris: string[]
  /** Array of allowed grant types */
  grant_types: OAuthClientGrantType[]
  /** Array of allowed response types */
  response_types: OAuthClientResponseType[]
  /** Scope of the OAuth client */
  scope?: string
  /** Timestamp when the client was created */
  created_at: string
  /** Timestamp when the client was last updated */
  updated_at: string
}

/**
 * Parameters for creating a new OAuth client.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type CreateOAuthClientParams = {
  /** Human-readable name of the OAuth client */
  client_name: string
  /** URI of the OAuth client */
  client_uri?: string
  /** Array of allowed redirect URIs */
  redirect_uris: string[]
  /** Array of allowed grant types (optional, defaults to authorization_code and refresh_token) */
  grant_types?: OAuthClientGrantType[]
  /** Array of allowed response types (optional, defaults to code) */
  response_types?: OAuthClientResponseType[]
  /** Scope of the OAuth client */
  scope?: string
  /** Token endpoint authentication method (defaults to server default if not specified) */
  token_endpoint_auth_method?: OAuthClientTokenEndpointAuthMethod
}

/**
 * Parameters for updating an existing OAuth client.
 * All fields are optional. Only provided fields will be updated.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type UpdateOAuthClientParams = {
  /** Human-readable name of the OAuth client */
  client_name?: string
  /** URI of the OAuth client */
  client_uri?: string
  /** URI of the OAuth client's logo */
  logo_uri?: string
  /** Array of allowed redirect URIs */
  redirect_uris?: string[]
  /** Array of allowed grant types */
  grant_types?: OAuthClientGrantType[]
  /** Token endpoint authentication method */
  token_endpoint_auth_method?: OAuthClientTokenEndpointAuthMethod
}

/**
 * Response type for OAuth client operations.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClientResponse = RequestResult<OAuthClient>

/**
 * Response type for listing OAuth clients.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthClientListResponse =
  | {
      data: { clients: OAuthClient[]; aud: string } & Pagination
      error: null
    }
  | {
      data: { clients: [] }
      error: AuthError
    }

/**
 * Contains all OAuth client administration methods.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export interface GoTrueAdminOAuthApi {
  /**
   * Lists all OAuth clients with optional pagination.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory OAuth Admin
   */
  listClients(params?: PageParams): Promise<OAuthClientListResponse>

  /**
   * Creates a new OAuth client.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory OAuth Admin
   */
  createClient(params: CreateOAuthClientParams): Promise<OAuthClientResponse>

  /**
   * Gets details of a specific OAuth client.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory OAuth Admin
   */
  getClient(clientId: string): Promise<OAuthClientResponse>

  /**
   * Updates an existing OAuth client.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory OAuth Admin
   */
  updateClient(clientId: string, params: UpdateOAuthClientParams): Promise<OAuthClientResponse>

  /**
   * Deletes an OAuth client.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory OAuth Admin
   */
  deleteClient(clientId: string): Promise<{ data: null; error: AuthError | null }>

  /**
   * Regenerates the secret for an OAuth client.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory OAuth Admin
   */
  regenerateClientSecret(clientId: string): Promise<OAuthClientResponse>
}

/**
 * Type of custom identity provider.
 */
export type CustomProviderType = 'oauth2' | 'oidc'

/**
 * OIDC discovery document fields.
 * Populated when the server successfully fetches and validates the
 * provider's OpenID Connect discovery document.
 */
export type OIDCDiscoveryDocument = {
  /** The issuer identifier */
  issuer: string
  /** URL of the authorization endpoint */
  authorization_endpoint: string
  /** URL of the token endpoint */
  token_endpoint: string
  /** URL of the JSON Web Key Set */
  jwks_uri: string
  /** URL of the userinfo endpoint */
  userinfo_endpoint?: string
  /** URL of the revocation endpoint */
  revocation_endpoint?: string
  /** List of supported scopes */
  supported_scopes?: string[]
  /** List of supported response types */
  supported_response_types?: string[]
  /** List of supported subject types */
  supported_subject_types?: string[]
  /** List of supported ID token signing algorithms */
  supported_id_token_signing_algs?: string[]
}

/**
 * Custom OAuth/OIDC provider object returned from the admin API.
 */
export type CustomOAuthProvider = {
  /** Unique identifier (UUID) */
  id: string
  /** Provider type */
  provider_type: CustomProviderType
  /** Provider identifier (e.g. `custom:mycompany`) */
  identifier: string
  /** Human-readable name */
  name: string
  /** OAuth client ID */
  client_id: string
  /** Additional client IDs accepted during token validation */
  acceptable_client_ids?: string[]
  /** OAuth scopes requested during authorization */
  scopes?: string[]
  /**
   * Allowlist of raw identity provider claim keys to copy verbatim into the
   * user's `custom_claims` field (within `identity_data` and
   * `raw_user_meta_data`), e.g. `["groups", "org_id", "mail"]`. This is an
   * opt-in allowlist that defaults to empty (no claims captured) and operates
   * independently from `attribute_mapping`.
   */
  custom_claims_allowlist?: string[]
  /** Whether PKCE is enabled */
  pkce_enabled?: boolean
  /** Mapping of provider attributes to Supabase user attributes */
  attribute_mapping?: Record<string, any>
  /** Additional parameters sent with the authorization request */
  authorization_params?: Record<string, string>
  /** Whether the provider is enabled */
  enabled?: boolean
  /** Whether email is optional for this provider */
  email_optional?: boolean
  /** OIDC issuer URL */
  issuer?: string
  /** OIDC discovery URL */
  discovery_url?: string
  /** Whether to skip nonce check (OIDC) */
  skip_nonce_check?: boolean
  /** OAuth2 authorization URL */
  authorization_url?: string
  /** OAuth2 token URL */
  token_url?: string
  /** OAuth2 userinfo URL */
  userinfo_url?: string
  /** JWKS URI for token verification */
  jwks_uri?: string
  /** OIDC discovery document (OIDC providers only) */
  discovery_document?: OIDCDiscoveryDocument | null
  /** Timestamp when the provider was created */
  created_at: string
  /** Timestamp when the provider was last updated */
  updated_at: string
}

/**
 * Parameters for creating a new custom provider.
 */
export type CreateCustomProviderParams = {
  /** Provider type */
  provider_type: CustomProviderType
  /** Provider identifier (e.g. `custom:mycompany`) */
  identifier: string
  /** Human-readable name */
  name: string
  /** OAuth client ID */
  client_id: string
  /** OAuth client secret (write-only, not returned in responses) */
  client_secret: string
  /** Additional client IDs accepted during token validation */
  acceptable_client_ids?: string[]
  /** OAuth scopes requested during authorization */
  scopes?: string[]
  /**
   * Allowlist of raw identity provider claim keys to copy verbatim into the
   * user's `custom_claims` field (within `identity_data` and
   * `raw_user_meta_data`), e.g. `["groups", "org_id", "mail"]`. This is an
   * opt-in allowlist that defaults to empty (no claims captured) and operates
   * independently from `attribute_mapping`.
   */
  custom_claims_allowlist?: string[]
  /** Whether PKCE is enabled */
  pkce_enabled?: boolean
  /** Mapping of provider attributes to Supabase user attributes */
  attribute_mapping?: Record<string, any>
  /** Additional parameters sent with the authorization request */
  authorization_params?: Record<string, string>
  /** Whether the provider is enabled */
  enabled?: boolean
  /** Whether email is optional for this provider */
  email_optional?: boolean
  /** OIDC issuer URL */
  issuer?: string
  /** OIDC discovery URL */
  discovery_url?: string
  /** Whether to skip nonce check (OIDC) */
  skip_nonce_check?: boolean
  /** OAuth2 authorization URL */
  authorization_url?: string
  /** OAuth2 token URL */
  token_url?: string
  /** OAuth2 userinfo URL */
  userinfo_url?: string
  /** JWKS URI for token verification */
  jwks_uri?: string
}

/**
 * Parameters for updating an existing custom provider.
 * All fields are optional. Only provided fields will be updated.
 * `provider_type` and `identifier` are immutable and cannot be changed.
 */
export type UpdateCustomProviderParams = {
  /** Human-readable name */
  name?: string
  /** OAuth client ID */
  client_id?: string
  /** OAuth client secret (write-only, not returned in responses) */
  client_secret?: string
  /** Additional client IDs accepted during token validation */
  acceptable_client_ids?: string[]
  /** OAuth scopes requested during authorization */
  scopes?: string[]
  /**
   * Allowlist of raw identity provider claim keys to copy verbatim into the
   * user's `custom_claims` field (within `identity_data` and
   * `raw_user_meta_data`), e.g. `["groups", "org_id", "mail"]`. This is an
   * opt-in allowlist that defaults to empty (no claims captured) and operates
   * independently from `attribute_mapping`.
   */
  custom_claims_allowlist?: string[]
  /** Whether PKCE is enabled */
  pkce_enabled?: boolean
  /** Mapping of provider attributes to Supabase user attributes */
  attribute_mapping?: Record<string, any>
  /** Additional parameters sent with the authorization request */
  authorization_params?: Record<string, string>
  /** Whether the provider is enabled */
  enabled?: boolean
  /** Whether email is optional for this provider */
  email_optional?: boolean
  /** OIDC issuer URL */
  issuer?: string
  /** OIDC discovery URL */
  discovery_url?: string
  /** Whether to skip nonce check (OIDC) */
  skip_nonce_check?: boolean
  /** OAuth2 authorization URL */
  authorization_url?: string
  /** OAuth2 token URL */
  token_url?: string
  /** OAuth2 userinfo URL */
  userinfo_url?: string
  /** JWKS URI for token verification */
  jwks_uri?: string
}

/**
 * Parameters for listing custom providers.
 */
export type ListCustomProvidersParams = {
  /** Filter by provider type */
  type?: CustomProviderType
}

/**
 * Response type for custom provider operations.
 */
export type CustomProviderResponse = RequestResult<CustomOAuthProvider>

/**
 * Response type for listing custom providers.
 */
export type CustomProviderListResponse =
  | {
      data: { providers: CustomOAuthProvider[] }
      error: null
    }
  | {
      data: { providers: [] }
      error: AuthError
    }

/**
 * Contains all custom OIDC/OAuth provider administration methods.
 */
export interface GoTrueAdminCustomProvidersApi {
  /**
   * Lists all custom providers with optional type filter.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory Auth Admin
   */
  listProviders(params?: ListCustomProvidersParams): Promise<CustomProviderListResponse>

  /**
   * Creates a new custom OIDC/OAuth provider.
   *
   * For OIDC providers, the server fetches and validates the OpenID Connect discovery document
   * from the issuer's well-known endpoint (or the provided `discovery_url`) at creation time.
   * This may return a validation error (`error_code: "validation_failed"`) if the discovery
   * document is unreachable, not valid JSON, missing required fields, or if the issuer
   * in the document does not match the expected issuer.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory Auth Admin
   */
  createProvider(params: CreateCustomProviderParams): Promise<CustomProviderResponse>

  /**
   * Gets details of a specific custom provider by identifier.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory Auth Admin
   */
  getProvider(identifier: string): Promise<CustomProviderResponse>

  /**
   * Updates an existing custom provider.
   *
   * When `issuer` or `discovery_url` is changed on an OIDC provider, the server re-fetches and
   * validates the discovery document before persisting. This may return a validation error
   * (`error_code: "validation_failed"`) if the discovery document is unreachable, invalid, or
   * the issuer does not match.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory Auth Admin
   */
  updateProvider(
    identifier: string,
    params: UpdateCustomProviderParams
  ): Promise<CustomProviderResponse>

  /**
   * Deletes a custom provider.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory Auth Admin
   */
  deleteProvider(identifier: string): Promise<{ data: null; error: AuthError | null }>
}

/**
 * OAuth client details in an authorization request.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthAuthorizationClient = {
  /** Unique identifier for the OAuth client (UUID) */
  id: string
  /** Human-readable name of the OAuth client */
  name: string
  /** URI of the OAuth client's website */
  uri: string
  /** URI of the OAuth client's logo */
  logo_uri: string
}

/**
 * OAuth authorization details when user needs to provide consent.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 *
 * This response includes all information needed to display a consent page:
 * client details, user info, requested scopes, and where the user will be redirected.
 *
 * Note: `redirect_uri` is the base URI (e.g., "https://app.com/callback") without
 * query parameters. After consent, you'll receive a complete `redirect_url` with
 * the authorization code and state parameters appended.
 */
export type OAuthAuthorizationDetails = {
  /** The authorization ID used to approve or deny the request */
  authorization_id: string
  /** The OAuth client's registered redirect URI (base URI without query parameters) */
  redirect_uri: string
  /** OAuth client requesting authorization */
  client: OAuthAuthorizationClient
  /** User object associated with the authorization */
  user: {
    /** User ID (UUID) */
    id: string
    /** User email */
    email: string
  }
  /** Space-separated list of requested scopes (e.g., "openid profile email") */
  scope: string
}

/**
 * OAuth redirect response when user has already consented or after consent decision.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 *
 * This response is returned in three scenarios:
 * 1. User already consented to these scopes (auto-approved)
 * 2. User just approved the authorization request
 * 3. User just denied the authorization request
 *
 * The `redirect_url` is a complete URL ready for redirecting the user back to the
 * OAuth client, including authorization code (on success) or error (on denial) in
 * query parameters, along with the state parameter if one was provided.
 */
export type OAuthRedirect = {
  /** Complete redirect URL with authorization code and state parameters (e.g., "https://app.com/callback?code=xxx&state=yyy") */
  redirect_url: string
}

/**
 * Response type for getting OAuth authorization details.
 * Returns either full authorization details (if consent needed) or redirect URL (if already consented).
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 *
 * @example
 * ```typescript
 * const { data, error } = await supabase.auth.oauth.getAuthorizationDetails(authorizationId)
 *
 * if (error) {
 *   console.error('Error:', error)
 * } else if ('authorization_id' in data) {
 *   // User needs to provide consent - show consent page
 *   console.log('Client:', data.client.name)
 *   console.log('Scopes:', data.scope)
 *   console.log('Redirect URI:', data.redirect_uri)
 * } else {
 *   // User already consented - redirect immediately
 *   window.location.href = data.redirect_url
 * }
 * ```
 */
export type AuthOAuthAuthorizationDetailsResponse = RequestResult<
  OAuthAuthorizationDetails | OAuthRedirect
>

/**
 * Response type for OAuth consent decision (approve/deny).
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type AuthOAuthConsentResponse = RequestResult<OAuthRedirect>

/**
 * An OAuth grant representing a user's authorization of an OAuth client.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type OAuthGrant = {
  /** OAuth client information */
  client: OAuthAuthorizationClient
  /** Array of scopes granted to this client */
  scopes: string[]
  /** Timestamp when the grant was created (ISO 8601 date-time) */
  granted_at: string
}

/**
 * Response type for listing user's OAuth grants.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type AuthOAuthGrantsResponse = RequestResult<OAuthGrant[]>

/**
 * Response type for revoking an OAuth grant.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 */
export type AuthOAuthRevokeGrantResponse = RequestResult<{}>

/**
 * Contains all OAuth 2.1 authorization server user-facing methods.
 * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
 *
 * These methods are used to implement the consent page.
 */
export interface AuthOAuthServerApi {
  /**
   * Retrieves details about an OAuth authorization request.
   * Used to display consent information to the user.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * This method returns one of two response types:
   * - `OAuthAuthorizationDetails`: User needs to consent - show consent page with client info
   * - `OAuthRedirect`: User already consented - redirect immediately to the OAuth client
   *
   * Use type narrowing to distinguish between the responses:
   * ```typescript
   * if ('authorization_id' in data) {
   *   // Show consent page
   * } else {
   *   // Redirect to data.redirect_url
   * }
   * ```
   *
   * @param authorizationId - The authorization ID from the authorization request
   * @returns Authorization details or redirect URL depending on consent status
   *
   * @category Auth
   * @subcategory OAuth Server
   */
  getAuthorizationDetails(authorizationId: string): Promise<AuthOAuthAuthorizationDetailsResponse>

  /**
   * Approves an OAuth authorization request.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * After approval, the user's consent is stored and an authorization code is generated.
   * The response contains a complete redirect URL with the authorization code and state.
   *
   * @param authorizationId - The authorization ID to approve
   * @param options - Optional parameters
   * @param options.skipBrowserRedirect - If false (default), automatically redirects the browser to the OAuth client. If true, returns the redirect_url without automatic redirect (useful for custom handling).
   * @returns Redirect URL to send the user back to the OAuth client with authorization code
   *
   * @category Auth
   * @subcategory OAuth Server
   */
  approveAuthorization(
    authorizationId: string,
    options?: { skipBrowserRedirect?: boolean }
  ): Promise<AuthOAuthConsentResponse>

  /**
   * Denies an OAuth authorization request.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * After denial, the response contains a redirect URL with an OAuth error
   * (access_denied) to inform the OAuth client that the user rejected the request.
   *
   * @param authorizationId - The authorization ID to deny
   * @param options - Optional parameters
   * @param options.skipBrowserRedirect - If false (default), automatically redirects the browser to the OAuth client. If true, returns the redirect_url without automatic redirect (useful for custom handling).
   * @returns Redirect URL to send the user back to the OAuth client with error information
   *
   * @category Auth
   * @subcategory OAuth Server
   */
  denyAuthorization(
    authorizationId: string,
    options?: { skipBrowserRedirect?: boolean }
  ): Promise<AuthOAuthConsentResponse>

  /**
   * Lists all OAuth grants that the authenticated user has authorized.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * @returns Response with array of OAuth grants with client information and granted scopes
   *
   * @category Auth
   * @subcategory OAuth Server
   */
  listGrants(): Promise<AuthOAuthGrantsResponse>

  /**
   * Revokes a user's OAuth grant for a specific client.
   * Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
   *
   * Revocation marks consent as revoked, deletes active sessions for that OAuth client,
   * and invalidates associated refresh tokens.
   *
   * @param options - Revocation options
   * @param options.clientId - The OAuth client identifier (UUID) to revoke access for
   * @returns Empty response on successful revocation
   *
   * @category Auth
   * @subcategory OAuth Server
   */
  revokeGrant(options: { clientId: string }): Promise<AuthOAuthRevokeGrantResponse>
}

// --- Passkey Types ---

/** Response from POST /passkeys/registration/options */
export type PasskeyRegistrationOptionsResponse = {
  challenge_id: string
  options: ServerCredentialCreationOptions
  expires_at: number
}

/** Request body for POST /passkeys/registration/verify */
export type PasskeyRegistrationVerifyParams = {
  challenge_id: string
  credential: RegistrationResponseJSON
}

/** Response from POST /passkeys/registration/verify */
export type PasskeyMetadata = {
  id: string
  friendly_name?: string
  created_at: string
}

/** Response from POST /passkeys/authentication/options */
export type PasskeyAuthenticationOptionsResponse = {
  challenge_id: string
  options: ServerCredentialRequestOptions
  expires_at: number
}

/** Request body for POST /passkeys/authentication/verify */
export type PasskeyAuthenticationVerifyParams = {
  challenge_id: string
  credential: AuthenticationResponseJSON
}

/** Item in the passkeys list (GET /passkeys/ and admin list) */
export type PasskeyListItem = {
  id: string
  friendly_name?: string
  created_at: string
  last_used_at?: string
}

// --- Passkey SDK Method Parameter/Response Types ---

export type SignInWithPasskeyCredentials = {
  options?: {
    captchaToken?: string
    signal?: AbortSignal
  }
}

export type RegisterPasskeyCredentials = {
  options?: {
    signal?: AbortSignal
  }
}

export type VerifyPasskeyRegistrationParams = {
  /** Challenge ID from startRegistration */
  challengeId: string
  /** Serialized credential from navigator.credentials.create() */
  credential: ServerCredentialResponse
}

export type StartPasskeyAuthenticationParams = {
  options?: {
    captchaToken?: string
  }
}

export type VerifyPasskeyAuthenticationParams = {
  /** Challenge ID from startAuthentication */
  challengeId: string
  /** Serialized credential from navigator.credentials.get() */
  credential: ServerCredentialResponse
}

export type PasskeyUpdateParams = {
  /** UUID of the passkey to update */
  passkeyId: string
  /** New friendly name (max 120 chars) */
  friendlyName: string
}

export type PasskeyDeleteParams = {
  /** UUID of the passkey to delete */
  passkeyId: string
}

// --- Passkey Response Types ---

export type AuthPasskeyRegistrationOptionsResponse =
  RequestResult<PasskeyRegistrationOptionsResponse>
export type AuthPasskeyRegistrationVerifyResponse = RequestResult<
  PasskeyMetadata,
  WebAuthnError | AuthError
>
export type AuthPasskeyAuthenticationOptionsResponse =
  RequestResult<PasskeyAuthenticationOptionsResponse>
export type AuthPasskeyAuthenticationVerifyResponse = RequestResult<
  { session: Session | null; user: User | null },
  WebAuthnError | AuthError
>
export type AuthPasskeyListResponse = RequestResult<PasskeyListItem[]>
export type AuthPasskeyUpdateResponse = RequestResult<PasskeyListItem>
export type AuthPasskeyDeleteResponse = RequestResult<null>

// --- Passkey Admin Types ---

export type AuthPasskeyAdminListParams = {
  userId: string
}

export type AuthPasskeyAdminDeleteParams = {
  userId: string
  passkeyId: string
}

// --- Passkey Namespace Interfaces ---

/**
 * Lower-level two-step API and management methods for passkeys.
 * Access via `supabase.auth.passkey`.
 */
export interface AuthPasskeyApi {
  // Two-step registration
  /**
   * Starts the passkey registration ceremony. Fetches a registration challenge
   * and credential creation options from the server. Used as the first step of
   * a two-step registration flow when the caller wants to handle
   * `navigator.credentials.create()` themselves.
   *
   * @category Auth
   * @subcategory Auth Passkey
   */
  startRegistration(): Promise<AuthPasskeyRegistrationOptionsResponse>

  /**
   * Verifies a passkey registration credential against a previously issued
   * challenge. Used as the second step of a two-step registration flow.
   *
   * @category Auth
   * @subcategory Auth Passkey
   */
  verifyRegistration(
    params: VerifyPasskeyRegistrationParams
  ): Promise<AuthPasskeyRegistrationVerifyResponse>

  // Two-step authentication
  /**
   * Starts the passkey authentication ceremony. Fetches an authentication
   * challenge and credential request options from the server. Used as the
   * first step of a two-step sign-in flow when the caller wants to handle
   * `navigator.credentials.get()` themselves.
   *
   * @category Auth
   * @subcategory Auth Passkey
   */
  startAuthentication(
    params?: StartPasskeyAuthenticationParams
  ): Promise<AuthPasskeyAuthenticationOptionsResponse>

  /**
   * Verifies a passkey authentication credential against a previously issued
   * challenge. Used as the second step of a two-step sign-in flow.
   *
   * @category Auth
   * @subcategory Auth Passkey
   */
  verifyAuthentication(
    params: VerifyPasskeyAuthenticationParams
  ): Promise<AuthPasskeyAuthenticationVerifyResponse>

  // Management
  /**
   * Lists all passkeys registered for the currently signed-in user.
   *
   * @category Auth
   * @subcategory Auth Passkey
   */
  list(): Promise<AuthPasskeyListResponse>

  /**
   * Updates a passkey's friendly name.
   *
   * @category Auth
   * @subcategory Auth Passkey
   */
  update(params: PasskeyUpdateParams): Promise<AuthPasskeyUpdateResponse>

  /**
   * Deletes a passkey for the currently signed-in user.
   *
   * @category Auth
   * @subcategory Auth Passkey
   */
  delete(params: PasskeyDeleteParams): Promise<AuthPasskeyDeleteResponse>
}

export interface GoTrueAdminPasskeyApi {
  /**
   * Lists all passkeys registered for a specific user.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory Passkey Admin
   */
  listPasskeys(params: AuthPasskeyAdminListParams): Promise<AuthPasskeyListResponse>

  /**
   * Deletes a specific passkey for a specific user.
   *
   * This function should only be called on a server. Never expose your `service_role` key in the browser.
   *
   * @category Auth
   * @subcategory Passkey Admin
   */
  deletePasskey(params: AuthPasskeyAdminDeleteParams): Promise<AuthPasskeyDeleteResponse>
}
