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

import { HttpClient, HttpResponse } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { forkJoin, iif, isObservable, Observable, of, throwError } from 'rxjs';
import { map, mergeMap, tap } from 'rxjs/operators';
import { AUTH_OPTIONS } from '../../auth-options.token';
import { IAuthModuleConfig } from '../../auth.options';
import { ResourceSet } from '../../models/authorization.model';
import { AuthenticationService } from '../../services/authentication.service';
import { AuthorizationService } from '../../services/authorization.service';
import { asObservable } from '../../as-observable';

@Injectable()
export class AccessControlAuthorizationService extends AuthorizationService {
  constructor(
    @Inject(AUTH_OPTIONS) private config: IAuthModuleConfig,
    private authenticationService: AuthenticationService,
    private _httpClient: HttpClient
  ) {
    super();
    this.authenticationService.getRoles().subscribe((roles) => (this.roles = roles));

    this.authenticationService
      .isAuthenticated()
      .pipe(
        mergeMap((isAuthenticated) => iif(() => isAuthenticated, this.loadResourceSets(), of(undefined))),
        tap((resourceSets) => this.resourceSets$.next(resourceSets))
      )
      .subscribe();
  }

  /**
   * Returns ResourceSets provided by AccessControl for the logged in user.
   */
  private loadResourceSets(): Observable<ResourceSet[]> {
    const authServerBaseUrl = this.config.authServerBaseUrl ?? this.config.accessControlEndpoint;
    if (
      !(
        (!!authServerBaseUrl && typeof authServerBaseUrl === 'string' && authServerBaseUrl.length > 0) ||
        isObservable(authServerBaseUrl)
      )
    ) {
      const error = 'Empty authServerBaseUrl in ngAuth-Module Configuration. Please define an authServerBaseUrl.';
      console.error(error);
      return throwError(error);
    }

    const accessControlConfigurationEndpoint = asObservable(authServerBaseUrl);
    const accessControlConfigurationResourceSetName = asObservable(this.config.resourceSetName);

    return forkJoin([accessControlConfigurationEndpoint, accessControlConfigurationResourceSetName]).pipe(
      mergeMap(([accessControlEndpoint, resourceSetName]): Observable<HttpResponse<ResourceSet[]>> => {
        if (!accessControlEndpoint.endsWith('/')) {
          accessControlEndpoint += '/';
        }
        return this._httpClient.get<ResourceSet[]>(accessControlEndpoint + 'api/v1/ResourceSets/' + resourceSetName, {
          observe: 'response',
        });
      }),
      map((response) => response.body)
    );
  }
}
