import { Injectable } from '@angular/core';
import { ApmService } from '@elastic/apm-rum-angular';
import { SELECTED_ORGANIZATION_NUMBER_KEY } from '@msfa-constants/local-storage-keys';
import { UnsubscribeDirective } from '@msfa-directives/unsubscribe.directive';
import { Employee, mapResponseToEmployee } from '@msfa-models/employee.model';
import { mapResponseToOrganization, Organization } from '@msfa-models/organization.model';
import { mapResponseToRoles, Role } from '@msfa-models/role.model';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { filter, map, mapTo, shareReplay, startWith, switchMap } from 'rxjs/operators';
import { AuthenticationService } from './api/authentication.service';
import { UserApiService } from './api/user.api.service';

@Injectable({
  providedIn: 'root',
})
export class UserService extends UnsubscribeDirective {
  private _isLoggedIn$: Observable<boolean> = this.authenticationService.isLoggedIn$;
  private _selectedOrganizationNumber$ = new BehaviorSubject<string | null>(null);

  public organizations$: Observable<Organization[]> = this._isLoggedIn$.pipe(
    filter(loggedIn => !!loggedIn),
    switchMap(() =>
      this.userApiService
        .fetchOrganizations$()
        .pipe(map(({ data }) => data.map(organization => mapResponseToOrganization(organization))))
    ),
    shareReplay(1)
  );
  public selectedOrganization$: Observable<Organization | null> = combineLatest([
    this.organizations$,
    this._selectedOrganizationNumber$,
  ]).pipe(
    filter(([organizations]) => !!organizations?.length),
    map(([organizations, organizationNumber]) => {
      return organizationNumber
        ? organizations.find(organization => organization.organizationNumber === organizationNumber)
        : null;
    })
  );
  public user$: Observable<Employee> = combineLatest([this._isLoggedIn$, this.selectedOrganization$]).pipe(
    filter(([loggedIn, selectedOrganization]) => !!(loggedIn && selectedOrganization)),
    switchMap(() => this.userApiService.fetchCurrentUser$().pipe(map(({ data }) => mapResponseToEmployee(data)))),
    shareReplay(1)
  );
  public userRoles$: Observable<Role[]> = this.user$.pipe(map(({ roles }) => mapResponseToRoles(roles)));
  public userLoading$: Observable<boolean> = combineLatest([this._isLoggedIn$, this.selectedOrganization$]).pipe(
    filter(([isLoggedIn, selectedOrganization]) => !!(isLoggedIn && selectedOrganization)),
    switchMap(() => this.user$.pipe(mapTo(false), startWith(true))),
    startWith(false)
  );

  constructor(
    private authenticationService: AuthenticationService,
    private userApiService: UserApiService,
    private apmService: ApmService
  ) {
    super();
    this._setSelectedOrganizationNumberFromLocalStorage();
    super.unsubscribeOnDestroy(
      this.user$.subscribe(user => {
        this.apmService.apm.setUserContext({ id: user.id });
      })
    );
  }

  private _setSelectedOrganizationNumberFromLocalStorage(): void {
    this._selectedOrganizationNumber$.next(localStorage.getItem(SELECTED_ORGANIZATION_NUMBER_KEY));
  }

  public setSelectedOrganization(organization: Organization): void {
    localStorage.setItem(SELECTED_ORGANIZATION_NUMBER_KEY, organization.organizationNumber);
    this._selectedOrganizationNumber$.next(organization.organizationNumber);
  }
}
