import { Box } from './../modules/box';
import { HttpClient } from 'aurelia-fetch-client';
import { Auth0Client, CacheLocation } from '@auth0/auth0-spa-js';
import { Aurelia, Container, inject } from "aurelia-framework";
import { Router } from "aurelia-router";
import { Datacontext } from '../data/data-context';
import { Config } from '../config';
import { BaseOAuthService, OAuthClientWrapper } from './oauth-service';
import { I18N } from 'aurelia-i18n';
import { UIInternal } from '../utils/ui-internal';

const LOGIN_LOOP_DETECTION = 'LOGIN_LOOP_DETECTION';
const LOGIN_LOOP_DETECTION_LIMIT = 2;

class Auth0ClientWrapper extends OAuthClientWrapper {
  private client: Auth0Client;

  public async init() {
    this.client = new Auth0Client({
      domain: this.config.domain,
      clientId: this.config.clientId,
      //useRefreshTokens: true,
      cacheLocation: this.getCacheLocation(),
      authorizationParams: {
        redirect_uri: this.config.callback_uri,
        audience: this.config.audience,
        scope: "openid profile",
        ui_locales: "fr en nl",
      }
    });

    try {
      await this.accessToken();
      sessionStorage.removeItem(LOGIN_LOOP_DETECTION);
    } catch (e) {
      if (['missing_refresh_token', 'invalid_grant', 'login_required'].indexOf(e.error) >= 0) {
        // Detect login loop (i.e. third party cookies disabled)
        let loginLoopDetectionValue = sessionStorage.getItem(LOGIN_LOOP_DETECTION);
        let loginLoopDetectionCounter = loginLoopDetectionValue == null ? 0 : parseInt(loginLoopDetectionValue);
        if (++loginLoopDetectionCounter > LOGIN_LOOP_DETECTION_LIMIT) {
          // Show loop error dialog
          let i18n = Container.instance.get(I18N);
          // Auto detect locale (user is not yet logged)
          let t = await UIInternal.autoDetectAndSetLocale(i18n);
          await Container.instance.get(Box)
            .showError(t('common:clientExceptions.auth0LoginLoop.message'), t('common:clientExceptions.auth0LoginLoop.title'), null, false, true)
            .whenClosed(() => { });
          loginLoopDetectionCounter = 0;
        }
        sessionStorage.setItem(LOGIN_LOOP_DETECTION, loginLoopDetectionCounter.toString());

        this.login();
      } else {
        throw e;
      }
    }

    return this;
  }

  public async accessToken(): Promise<string> {
    return await this.client.getTokenSilently();
  }

  protected loginWithRedirect() {
    return this.client.loginWithRedirect({
      authorizationParams: {
        redirect_uri: this.config.callback_uri,
        scope: 'all app_metadata ',
        ui_locales: navigator?.language?.slice(0, 2) ?? "fr"
      }
    });
  }

  public async handleSignInRedirect(): Promise<void> {
    await this.client.handleRedirectCallback();
  }

  public async isAuthenticated(): Promise<boolean> {
    /*  try {
       // Force fetching of token (silently and/or from cache)
       await this.accessToken();
     } catch { } */
    return await this.client.isAuthenticated();
  }

  public async logout(): Promise<void> {
    this.client.logout({
      logoutParams: { returnTo: this.config.logout_uri }
    });
  }

  public async getUserProfile(): Promise<any> {
    return await this.client.getUser();
  }

  private getCacheLocation(): CacheLocation {
    let cacheLocation: CacheLocation = this.isBrowserBuiltOnChromium() ? 'memory' : 'localstorage';
    if (this.cacheLocationCanBeOverwriten(cacheLocation)) {
      cacheLocation = this.config.cacheLocation as CacheLocation;
    }
    return cacheLocation;
  }

  private isBrowserBuiltOnChromium(): boolean {
    return !!(window as any).chrome;
  }

  private cacheLocationCanBeOverwriten(cacheLocation: CacheLocation): boolean {
    return (this.config?.cacheLocation === 'localstorage' || this.config?.cacheLocation === 'memory') && this.config.cacheLocation !== cacheLocation;
  }
}

@inject(Router, Aurelia, Datacontext, Config, HttpClient)
export class Auth0Service extends BaseOAuthService<Auth0ClientWrapper> {

  constructor(router: Router, app: Aurelia, dataContext: Datacontext, config: Config, httpClient: HttpClient) {
    super(router, app, dataContext, config, httpClient);
  }

  protected makeClient(): Promise<Auth0ClientWrapper> {
    return new Auth0ClientWrapper(this.config.authConfig).init();
  }

  public get usernameType(): "email" | "username" {
    return "email";
  }

  public async onUserPermissionsChanged(): Promise<void> {

  }
}