import { Injectable, Inject } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpHeaders, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError, tap, finalize } from 'rxjs/operators';
import { AppLogsService } from '../applogs.service';
import { AuthorizeService } from '../authorize.service';
import * as Utils from '../../utils';
import * as Const from '../../consts';
import { InMemoryDataService } from '../in-memory-data.service';
import { ENVIRONMENT_TOKEN, IENVIRONMENT } from '../../injection-tokens';

@Injectable({
  providedIn: 'root'
})

export class HttpInterceptorService implements HttpInterceptor {
  constructor(
    private appLogsservice: AppLogsService,
    private authorizeService: AuthorizeService,
    private mockDataService: InMemoryDataService,
    @Inject(ENVIRONMENT_TOKEN) private environment: IENVIRONMENT,
  ) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const clone = request.clone(this.prepareOptions(request.headers));

    const guid = Utils.guid();
    const operation = `${request.method}: ${request.url}`;
    this.appLogsservice.dlog('%c# HTTP Request ' + operation, { guid: guid }, 'color:' + Const.EN_AppColors.startRequestColor);
    let ok: string;

    const splittedURL = request.url.split(/\//g);
    let querySufix: string = '';
    const base: number = splittedURL[4] ? 4 : 3;
    if (request.method === 'GET') {
      querySufix = splittedURL[base] + (splittedURL.length < base + 2 ? ': list' : '');
    } else {
      for (let i = base; i < splittedURL.length; i++) {
        querySufix += (i === base ? '' : '/') + splittedURL[i];
      }
    }

    const mockDataResponse: Observable<HttpEvent<any>> = this.mockDataService.parseMockData(request, guid);
    if (mockDataResponse) return mockDataResponse;

    return next.handle(clone)
      .pipe(
        tap({
            next: (event: HttpEvent<any>) => ok = event instanceof HttpResponse ? 'SUCCEDED' : '',
            error: (error: HttpErrorResponse) => ok = 'FAILED',
            complete: () => { this.appLogsservice.dlog(querySufix, { guid: guid }, 'color:' + Const.EN_AppColors.returnRequestColor); }
          }
        ),

        //retry(2),
        catchError((error: HttpErrorResponse) => {

          // this.appLogsservice.error(Utils.getErrorMessageAsText(error), { error });
          this.appLogsservice.exception(error);

          if (error.status === Const.cnst_ErrorCode4Unauthorized) {  // 401 handled in auth.interceptor
            // this.authorizeService.loginPopup(Const.cnst_JWT_Expired);
            this.authorizeService.tryRenewJWTAsync();
          }
          return throwError(() => error);
        }),
        // Log when response observable either completes or errors
        // finalize(
        //   () => { this.appLogsservice.log(querySufix, { guid: guid }, 'color:' + Const.EN_AppColors.returnRequestColor); }
        // ),
      )
      ;
  }


  prepareOptions(sentHeader?: HttpHeaders, withBearer: boolean = true): any {
    let headerAdditionalKeys: HttpHeaders = null;
    if (!!this.environment.headerData) headerAdditionalKeys = new HttpHeaders(this.environment.headerData);

    let header: HttpHeaders = null;
    if (sentHeader) header = sentHeader;
    else {
      header = new HttpHeaders();
      header = header
        .append('Content-Type', 'application/json')
        .append('Accept', 'application/json');
    }
    //if (headerAdditionalKeys) header = header.append(headerAdditionalKeys.getAll);

    if (withBearer && this.authorizeService.useJWT && this.authorizeService.HR_JWT) header = header.append('Authorization', 'Bearer ' + this.authorizeService.HR_JWT.idToken);
    const withCredent: boolean = this.authorizeService.loginStatus === this.authorizeService.EN_LoginStatus_Windows ||
      this.authorizeService.loginStatus === this.authorizeService.EN_LoginStatus_Unknown && !this.authorizeService.useJWT && !headerAdditionalKeys;
    return {
      headers: header,
      withCredentials: withCredent
    };
  }
  /*   intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    const users: User[] = [
          { id: 1, username: 'test', password: 'test', firstName: 'Test', lastName: 'User' }
      ];

      const authHeader = request.headers.get('Authorization');
      const isLoggedIn = authHeader && authHeader.startsWith('Bearer fake-jwt-token');

      // wrap in delayed observable to simulate server api call
      return of(null).pipe(mergeMap(() => {

          // authenticate - public
          if (request.url.endsWith('/users/authenticate') && request.method === 'POST') {
              const user = users.find(x => x.username === request.body.username && x.password === request.body.password);
              if (!user) return error('Username or password is incorrect');
              return ok({
                  id: user.id,
                  username: user.username,
                  firstName: user.firstName,
                  lastName: user.lastName,
                  token: `fake-jwt-token`
              });
          }

          // get all users
          if (request.url.endsWith('/users') && request.method === 'GET') {
              if (!isLoggedIn) return unauthorised();
              return ok(users);
          }

          // pass through any requests not handled above
          return next.handle(request);
      }))
          // call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
          .pipe(materialize())
          .pipe(delay(500))
          .pipe(dematerialize());

      // private helper functions

      function ok(body) {
          return of(new HttpResponse({ status: 200, body }));
      }

      function unauthorised() {
          return throwError({ status: 401, error: { message: 'Unauthorised' } });
      }

      function error(message) {
          return throwError({ status: 400, error: { message } });
      }
  }*/
}
// export let fakeBackendProvider = {
//     // use fake backend in place of Http service for backend-less development
//     provide: HTTP_INTERCEPTORS,
//     useClass: FakeBackendInterceptor,
//     multi: true
// };
