import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';

import { AuthService } from '../../services/auth/auth.service';
import { Router } from '@angular/router';

import {
  LoaderDispatchers,
  ToastDispatchers,
} from '../../stores/index.dispatchers';
import { ToastStore } from '../../interfaces/store/toast.interface';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(
    private authService: AuthService,
    private router: Router,
    private loaderDispatchers: LoaderDispatchers,
    private toastDispatchers: ToastDispatchers,
  ) {}

  toastErrorStateByStatus: Record<
    HttpErrorResponse['status'],
    ToastStore['error']
  > = {
    401: {
      isVisible: true,
      message: 'Sua sessão expirou, faça login novamente.',
    } as ToastStore['error'],
    403: {
      isVisible: true,
      message: 'Você não tem permissão para acessar este recurso.',
    } as ToastStore['error'],
  };

  intercept(
    request: HttpRequest<unknown>,
    next: HttpHandler,
  ): Observable<HttpEvent<unknown>> {
    const authToken = this.authService.getAuthorizationToken();

    let authenticatedRequest: HttpRequest<unknown> | null = null;

    if (authToken) {
      authenticatedRequest = request.clone({
        headers: request.headers.set('Authorization', `Bearer ${authToken}`),
      });
    }

    const timeInit = Date.now();
    this.loaderDispatchers.showLoader();

    return next.handle(authenticatedRequest ?? request).pipe(
      tap({
        finalize: () => {
          const timeFinal = Date.now();
          const timeDiff = timeFinal - timeInit;
          const delay = 1200;

          if (timeDiff > delay) {
            this.loaderDispatchers.hideLoader();
            return;
          }

          setTimeout(
            () => this.loaderDispatchers.hideLoader(),
            delay - timeDiff,
          );
        },
      }),
      catchError((error) => {
        const err = error as HttpErrorResponse;

        const ignoreEndpoints = [
          '/user/exists/'
        ];

        if (ignoreEndpoints.some(endpointToIgnore => err.url?.includes(endpointToIgnore))) throw new Error();

        if (authenticatedRequest) {
          const toastErrorState = this.toastErrorStateByStatus[err.status];

          if (toastErrorState) {
            this.toastDispatchers.showError(toastErrorState);
          } else {
            this.toastDispatchers.showError({
              isVisible: true,
              message: 'Ocorreu um erro inesperado.',
            } as ToastStore['error']);
          }
        }

        if (err.status === 401) {
          this.authService.clearAuthorizationToken();
          this.router.navigate(['/app/auth/login']);
        }

        throw new Error();
      }),
    );
  }
}
