import { Injectable, Injector } from '@angular/core';
import {HttpRequest,HttpHandler,HttpEvent,HttpInterceptor,HttpErrorResponse} from '@angular/common/http';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { AuthService } from '../../services/auth.service';
import { catchError, filter, switchMap, take, timeout } from 'rxjs/operators';
import { MiscService } from '../services/misc.service';
import { ServerException } from '../../services/app.server.response';
import { ErrorMessages } from '../../utils/enums';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private isRefreshing = false;
  token: any;
  private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
  private logoutChannel = new BroadcastChannel('logout_channel');

  constructor(private injector: Injector,private authService:AuthService) {}

  intercept(request: HttpRequest<any>, handler: HttpHandler): Observable<HttpEvent<any>> {
    this.logoutChannel.onmessage = async (event) => {
      if (event.data === 'logout') {
        localStorage.clear();
        window.location.href = '/#/auth';
      }
    };
    if (this.authService.getToken()) {
      request = this.addToken(request, this.authService.getToken());
    }
    return handler.handle(request).pipe(catchError(error => {
      if (error instanceof HttpErrorResponse && error.status === 401) {
        return this.handleAuthError(request, handler);
      } else {
        this.handleServerError(error);
        return throwError(error);
      }
    }))as Observable<HttpEvent<any>>;
  }
  private handleAuthError(request: HttpRequest<any>, handler: HttpHandler) {
    if (!this.isRefreshing) {
      this.isRefreshing = true;
      this.refreshTokenSubject.next(null);
      let authService: AuthService = this.injector.get(AuthService);
      return authService.refreshToken().pipe(
        switchMap((response: any) => {
          this.isRefreshing = false;
          this.refreshTokenSubject.next(response.token);
          return handler.handle(this.addToken(request, response.token)).pipe(catchError(error => {
            this.handleServerError(error);
            return throwError(error);
          }));
        }));
    } else {
      return this.refreshTokenSubject.pipe(
        filter(token => token != null),
        take(1),
        switchMap(token => {
          return handler.handle(this.addToken(request, token));
        }));
    }
  }
  private handleServerError(error: HttpErrorResponse) {
    let url: string = error.url as string;
    let moduleName: string = "";
    if (url != null && url != undefined) {
      moduleName = url.split(':').length > 2 ?
        url.split(':')[2].split('/')[1] :
        url.split('/')[3];
    }
    let authService: AuthService = this.injector.get(AuthService);
    let miscService: MiscService = this.injector.get(MiscService);
    switch (error.status) {
      case 400:
        let errorResponse: ServerException = error as ServerException;
        if (errorResponse.error && errorResponse.error.errorCode != null) {
          errorResponse.error.arguments.forEach((argument, index) => {
            if (miscService.getErrorMessageTranslation(argument) != argument) {
              errorResponse.error.arguments[index] = miscService.getErrorMessageTranslation(argument);
            }
          });
          miscService.handleError(errorResponse.error.errorCode);
        } else {
          miscService.handleError(ErrorMessages.BAD_REQUEST, [moduleName.toUpperCase()]);
        }
        break;

      case 401:
        miscService.handleError(ErrorMessages.UNAUTHORIZED_REQUEST,[error.error.message]);
        authService.logout();
        break;

      case 403:
         miscService.handleError(ErrorMessages.FORBIDDEN_REQUEST,[]);
        authService.logout();
        break;

      case 500:
        miscService.handleError(ErrorMessages.INTERNAL_SERVER_ERROR,[]);
        break;

      case 0:
        miscService.handleError(ErrorMessages.CONNECTION_ERROR,[]);
        break;
    }
  }
  private addToken(request: HttpRequest<any>, token: string) {
    return request.clone({
      setHeaders: {
        'Authorization': `Bearer ${token}`
      }
    });
  }

}
