/*
 * Copyright (C) 2019 Robert Bosch GmbH Copyright (C) 2019 Robert Bosch Manufacturing Solutions GmbH, Germany. All rights reserved.
 */

import { APP_BASE_HREF, LocationStrategy } from '@angular/common';
import { Inject, Injectable, OnDestroy, Optional } from '@angular/core';
import { AbstractSecurityStorage, OidcSecurityService, OpenIdConfiguration } from 'angular-auth-oidc-client';
import { Observable, Subject, switchMap } from 'rxjs';
import { AUTH_OPTIONS } from '../../auth-options.token';
import { IMacmaAuthModuleConfig } from '../macma-auth.options';
import { MacmaStorageService, STORAGE_SCOPE_REDIRECT_QUERY_PARAMETER } from './macma-storage.service';
import { map } from 'rxjs/operators';

@Injectable()
export class OauthService implements OnDestroy {
  protected componentDestroyed$ = new Subject<void>();

  private openIdFlowConfiguration: OpenIdConfiguration;
  private _desiredRealm: string;

  constructor(
    @Inject(AUTH_OPTIONS) private config: IMacmaAuthModuleConfig,
    @Inject(AbstractSecurityStorage) private storageService: MacmaStorageService,
    @Optional() @Inject(APP_BASE_HREF) private baseHref: string,
    private locationStrategy: LocationStrategy
  ) {
    if (!this.baseHref) {
      this.baseHref = '/';
    }
  }

  get desiredRealm() {
    return this._desiredRealm;
  }

  ngOnDestroy(): void {
    this.componentDestroyed$.next();
    this.componentDestroyed$.complete();
  }

  setRealm(desiredRealm: string): void {
    if (desiredRealm === this._desiredRealm) {
      return;
    }
    this._desiredRealm = desiredRealm;
  }

  logoff(oidcSecurityService: OidcSecurityService): Observable<unknown> {
    return this.updateRedirectUrls(oidcSecurityService, false).pipe(switchMap(() => oidcSecurityService.logoff()));
  }

  login(oidcSecurityService: OidcSecurityService, usePopup = this.config.usePopup) {
    this.updateRedirectUrls(oidcSecurityService, usePopup).subscribe(() => {
      // if you need to add extra parameters to the login
      // let culture = 'de-CH';
      // this.oidcSecurityService.setCustomRequestParameters({ 'ui_locales': culture });

      if (!usePopup) {
        // redirect login
        oidcSecurityService.authorize();
      } else {
        // popup / iframe login
        const intendedWidth = 0.8;
        const intendedHeight = 0.8;
        const minWidth = 320;
        const minHeight = 320;
        const screenWidth = screen.availWidth;
        const screenHeight = screen.availHeight;

        const width = Math.max(minWidth, Math.floor(intendedWidth * screenWidth));
        const height = Math.max(minHeight, Math.floor(intendedHeight * screenHeight));
        const top = Math.max(0, Math.floor((screenHeight - height) / 2));
        const left = Math.max(0, Math.floor((screenWidth - width) / 2));

        oidcSecurityService.authorize(undefined, {
          urlHandler: (authUrl) => {
            // handle the authorization URL
            let loginWindow;
            try {
              loginWindow = window.open(
                authUrl,
                '_blank',
                'toolbar=0,location=0,menubar=0,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height
              );
            } catch (e) {
              // In iframe with sandbox attribute and without allow-popups token, an InvalidAccessError will be thrown.
              console.warn('Opening popup was blocked. Maybe popups are blocked or not allowed in iframe?', e);
            }

            // if popup cannot be opened, try with regular redirection login
            if (!loginWindow) {
              this.login(oidcSecurityService, false);
            }
          },
        });
      }
    });
  }

  private getLoginRedirectUrl() {
    return this.getHostUrl() + this.baseHref + this.config.authAssetsPath + 'login.html';
  }

  private getRenewRedirectUrl() {
    return this.getHostUrl() + this.baseHref + this.config.authAssetsPath + 'renew.html';
  }

  private getHostUrl() {
    return window.location.protocol + '//' + window.location.host;
  }

  public updateRedirectUrls(oidcSecurityService: OidcSecurityService, forPopup): Observable<void> {
    // must match the one used during auth code exchange at /token endpoint
    const redirectUrl = new URL(window.location.href);
    redirectUrl.hash = '';
    if (this.storageService.storageScope) {
      redirectUrl.searchParams.set(STORAGE_SCOPE_REDIRECT_QUERY_PARAMETER, this.storageService.storageScope);
    }
    const baseHrefReg = new RegExp(`^${this.locationStrategy.getBaseHref()}`);
    let postLoginRoute = this.locationStrategy.path(true).replace(baseHrefReg, '');
    if (!postLoginRoute.startsWith('/')) {
      postLoginRoute = `/${postLoginRoute}`;
    }
    console.log('Updating redirect url to ' + redirectUrl + ' and post-login-route to ' + postLoginRoute);

    return oidcSecurityService.getConfiguration().pipe(map(config => {
      this.openIdFlowConfiguration = config;
      this.openIdFlowConfiguration.silentRenewUrl = this.getRenewRedirectUrl();

      // redirect login
      this.openIdFlowConfiguration.redirectUrl = redirectUrl.toString();
      if (forPopup) {
        // popup / iframe login
        this.openIdFlowConfiguration.redirectUrl = this.getLoginRedirectUrl();
      }

      const hasStorage = typeof Storage !== 'undefined';
      if (hasStorage) {
        this.storageService.write('redirectUrl', this.openIdFlowConfiguration.redirectUrl);
        this.storageService.write('postLoginRoute', postLoginRoute);
      }

      this.openIdFlowConfiguration.postLoginRoute = postLoginRoute;
      this.openIdFlowConfiguration.postLogoutRedirectUri = redirectUrl.toString();
    }));
  }

  getCurrentOpenIdFlowConfiguration() {
    return this.openIdFlowConfiguration;
  }
}
