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

import { AuthenticationService } from './user/authentication.service';

@Injectable()
export class InterceptorService implements HttpInterceptor
{
  private readonly router = inject(Router);
  private readonly authService = inject(AuthenticationService);

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>
  {
    const currentUser = this.authService.currentUserValue;
    if (currentUser?.authenticationToken)
      request = this.addAuthenticationToken(request, currentUser.authenticationToken);

    const xsrfToken = this.getCookieValue('OnlineSalesAutoCrop.X-XSRF-TOKEN');
    if (xsrfToken)
      request = this.addXsrfTokenToken(request, xsrfToken);

    const expired = currentUser?.expires && new Date(currentUser?.expires) < new Date();
    return next.handle(request)
      .pipe(
        catchError((error: any) =>
        {
          let errorMsg = '';
          //if (error.status === 899)
          //{
          //  this.sessionExpired();
          //}
          //else
          if (error.status === 401)
          {
            this.relogin();
            errorMsg = 'Please Login again...';
          }
          else if (error.status === 403 || expired)
          {
            this.sessionExpired();
            errorMsg = 'Session expired<br/>Please Login again...';
          }
          else if (error.status === 415)
          {
            errorMsg = '<b>Error Code: 415</b><br/>Unsupported Media Type...';
          }
          else if (error.status === 429)
          {
            errorMsg = `<b>Server Error: 429</b><br/>${error.error}`;
          }
          else if (error.status === 500)
          {
            if (error.error)
            {
              errorMsg = `${this.parseErrors(error.error)}`;
            }
            else if (error?.statusText && error.statusText === 'Internal Server Error')
            {
              errorMsg = '<b>Error Code: 500</b><br/>Internal Server Error...';
            }
            else
            {
              errorMsg = '<b>Error Code: 500</b><br/>Internal Server Error...';
            }
          }
          else if (error.status === 502)
          {
            errorMsg = '<b>Server Error: 502</b><br/>Bad Gateway...';
          }
          else if (error.status === 503)
          {
            errorMsg = '<b>Server Error: 503</b><br/>Service Unavailable...';
          }
          else if (error.status === 504)
          {
            errorMsg = '<b>Server Error: 504</b><br/>Gateway Timeout Error...';
          }
          else if (error.status === 508)
          {
            errorMsg = '<b>Server Error: 508</b><br/>The server detected an infinite loop while processing the request...';
          }
          else if (error.status === 511)
          {
            errorMsg = '<b>Server Error: 511</b><br/>Network Authentication Required...';
          }
          else if (!navigator.onLine)
          {
            errorMsg = '<b>Server Error</b><br/>No Internet Connection...';
          }
          else if (error.error instanceof ErrorEvent)
          {
            if (error.error.stack)
              errorMsg = `${error.error.stack}`;
            else
              errorMsg = `${error.error.message}`;
          }
          else if (error.error)
            {
            if (error.status === 0 && error.ok === false && error.statusText === 'Unknown Error' && error.message?.includes('/api/v1/'))
              {
                errorMsg = '<b>Server Error:</b> Api server is not running...';
              }
              else if (error.error.returnMessage)
              {
                errorMsg = `${this.parseErrors(error.error.returnMessage)}`;
              }
              else if (error.error.errors)
              {
                errorMsg = `${this.parseErrors(error.error.errors)}`;
              }
              else if (error.error.validationErrors)
              {
                errorMsg = `<b>Validation Errors</b><br/>${this.parseErrors(error.error.validationErrors)}`;
              }
              else if (error.error.businessErrors)
              {
                errorMsg = `<b>Business Validation Errors</b><br/>${this.parseErrors(error.error.businessErrors)}`;
              }
              else if (error.status === 200 && error.ok === false && error.message?.includes('Http failure during parsing'))
              {
                errorMsg = 'Method not found...';
              }
              else if (error.message)
              {
                errorMsg = `${error.message}`;
              }
            }
            else
            {
              errorMsg = `<b>Error Code: ${error.status}</b><br/>Message: ${error.stack}`;
            }

          return throwError(() => errorMsg);
        })
      );
  }

  private getCookieValue(cookieName: string): string
  {
    const allCookies = decodeURIComponent(document.cookie).split('; ');
    for (const cookie of allCookies)
    {
      if (cookie.startsWith(cookieName + '='))
      {
        return cookie.substring(cookieName.length + 1);
      }
    }

    return '';
  }

  private addAuthenticationToken(req: HttpRequest<any>, token: string): HttpRequest<any>
  {
    return req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) });
  }

  private addXsrfTokenToken(req: HttpRequest<any>, xsrfToken: string): HttpRequest<any>
  {
    return req.clone({ headers: req.headers.set('OnlineSalesAutoCrop.X-XSRF-TOKEN', xsrfToken) });
  }

  private sessionExpired(): void
  {
    this.authService?.sessionExpired()?.subscribe(
      {
        next: () =>
        {
          this.relogin();
        },
        error: () =>
        {
          this.relogin();
        }
      });
  }

  private relogin(): void
  {
    this.authService.loggedout();
    this.router.navigate(['/login']);
  }

  public parseErrors(errors: any): string
  {
    let retVal: string = '';
    if (errors)
    {
      if (Array.isArray(errors))
      {
        for (const element of errors)
        {
          retVal += element + '<br/>';
        }
      }
      else
      {
        for (const propery in errors)
        {
          if (Array.isArray(errors[propery]) === false)
            continue;

          let propValue: string = '';
          propValue += errors[propery];
          if (!propValue.includes(', ') && !propValue.includes(','))
            propValue = propValue.replace(',', '<br/>');

          if (propValue && propValue.length > 0)
            retVal += propValue + '<br/>';
          else
            retVal += propValue;
        }
      }
      if (retVal.endsWith('<br/>'))
        retVal = retVal.substring(0, (retVal.length - 5))
    }

    return retVal;
  }
}
