import { KeycloakAdapter, KeycloakInstance, KeycloakLoginOptions, KeycloakLogoutOptions, KeycloakRegisterOptions } from 'keycloak-js';
import { createLogger } from './app/utils/utils';

const logger = createLogger('keycloak');
export type OptionsOrProvider<T> = Partial<T> | (() => Partial<T>);
/**
 * Create and immediately resolve a KeycloakPromise
 */
const createPromise = () => new Promise<void>(resolve => resolve());
/**
 * Resolve OptionsOrProvider: if it's an function, execute it, otherwise return it.
 */
const resolveOptions = <T>(opt: OptionsOrProvider<T>): Partial<T> => (typeof opt === 'function' ? opt() : opt);
/**
 *
 * Update options with the overrides given as OptionsOrProvider
 *
 * @param options
 * @param overrides
 * @returns
 */
const updateOptions = <T>(options: T, overrides: OptionsOrProvider<T>): T => Object.assign(options ?? <T>{}, resolveOptions(overrides));
/**
 * Keycloak adapter that supports options customization.
 *
 * All options can either be given as lazily evaluated provider functions (that will be evaluated
 * right before navigating) or eagerly evaluated objects. These options will have precedence
 * over options passed by keycloak-js.
 *
 * Cf. https://www.keycloak.org/docs/15.0/securing_apps/#custom-adapters
 *
 * Actual implementation copied more or less verbatim from
 * https://github.com/keycloak/keycloak-js-bower/blob/10.0.2/dist/keycloak.js#L1136
 *
 * @param kc Function that returns a Keycloak instance
 * @param loginOptions login options
 * @param logoutOptions logout options
 * @param registerOptions register options
 * @returns KeycloakAdapter
 */
export function customKeycloakAdapter(
  kc: () => KeycloakInstance,
  loginOptions: OptionsOrProvider<KeycloakLoginOptions> = {},
  logoutOptions: OptionsOrProvider<KeycloakLogoutOptions> = {},
  registerOptions: OptionsOrProvider<KeycloakRegisterOptions> = {},
): KeycloakAdapter {
  return {
    login: (options?: KeycloakLoginOptions): Promise<void> => {
      updateOptions(options, loginOptions);
      logger.info('Executing login. Options: ', options);
      window.location.replace(kc().createLoginUrl(options));
      return createPromise();
    },
    logout: (options?: KeycloakLogoutOptions): Promise<void> => {
      updateOptions(options, logoutOptions);
      logger.info('Executing logout. Options: ', options);
      window.location.replace(kc().createLogoutUrl(options));
      return createPromise();
    },
    register: (options?: KeycloakRegisterOptions): Promise<void> => {
      updateOptions(options, registerOptions);
      logger.info('Executing register. Options: ', options);
      window.location.replace(kc().createRegisterUrl(options));
      return createPromise();
    },
    accountManagement: (): Promise<void> => {
      const accountUrl = kc().createAccountUrl();
      logger.info('Executing account management');
      if (typeof accountUrl !== 'undefined') {
        window.location.href = accountUrl;
      } else {
        throw new Error('Not supported by the OIDC server');
      }
      return createPromise();
    },
    redirectUri: (options: { redirectUri: string }) => {
      if (options?.redirectUri) {
        return options.redirectUri;
      }
      if (kc().redirectUri) {
        return kc().redirectUri;
      }
      return window.location.href;
    },
  };
}
