import {Injectable} from "@angular/core";
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
import {Observable} from "rxjs";
import {HttpClient, HttpParams} from "@angular/common/http";
import {AppConfigService, MessagesService, ProgressIndicationService} from "@webbpm/base-package";
import {AuthenticationService} from "../authentication.service";
import {EsiaErrorDetail} from "../EsiaErrorDetail";
import {AccessChecker} from "../AccessChecker";

@Injectable({providedIn: 'root'})
export abstract class AuthGuard implements CanActivate {
  private cspTimeout: number;

  protected constructor(
      protected router: Router,
      private httpClient: HttpClient,
      private authenticationService: AuthenticationService,
      private messageService: MessagesService,
      private appConfigService: AppConfigService,
      private progressIndicationService: ProgressIndicationService
  ) {
    this.cspTimeout = this.appConfigService.getParamValue("csp_load_timeout");
  }

  public canActivate(route: ActivatedRouteSnapshot,
                     state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    this.progressIndicationService.showProgressBar();
    let url = new URL(window.location.href);
    if (!AccessChecker.checkBrowser()) {
      this.progressIndicationService.hideProgressBar();
      this.router.navigateByUrl('/home');
      return false;
    }

    return AccessChecker.checkCsp(this.cspTimeout)
        .then((cspCheckPassed) => {
          if (!cspCheckPassed) {
            this.router.navigateByUrl('/home');
            return Promise.reject("Navigation on /home")
          }
          return Promise.resolve(this.checkAccess());
        }).then((isAccess) => {
          let params = new URLSearchParams(url.search);
          let code = params.get('code');
          let state = params.get('state');
          let error = params.get('error');
          let errorDescription = params.get('error_description');
          if (isAccess) {
            return true;
          }
          if (error) {
            let errorMessage =
                this.messageService.getUnknowErrorMessage();
            let errorCode = this.extractCode(error);
            if (errorCode) {
              errorMessage = EsiaErrorDetail.getDescription(errorCode);
            }
            let consoleError = error + ', error description = ' + errorDescription;
            this.messageService.error(errorMessage);
            console.error(consoleError);
            return false;
          }
          if (code && state) {
            this.progressIndicationService.showProgressBar();
            const params = new HttpParams().set('code', code).set('state', state);
            this.httpClient.get("esia/auth",
                                {
                                  params: params,
                                  responseType: 'text',
                                  observe: 'response',
                                  headers: {
                                    "Error-intercept-skip": "true"
                                  }
                                })
                .toPromise()
                .then(
                    () => {
                      this.progressIndicationService.hideProgressBar();
                      window.open(url.origin + url.pathname, "_self");
                    })
                .catch(reason => {
                  const json = JSON.parse(reason.error);
                  json.messages.forEach((errorMessage) => {
                    this.messageService.error(errorMessage, json);
                  })
                })
                .finally(() => this.progressIndicationService.hideProgressBar());
            return false;
          }
          else {
            return this.authenticationService.redirectToEsia().toPromise().catch((reason) => {
              console.error(reason);
              return false;
            });
          }
        }).catch((reason) => {
          if (reason !== 'Navigation on /home') {
            console.error(reason);
          }
          return false
        });
  }

  private checkAccess(): boolean {
    return this.authenticationService.isAuthenticated();
  }

  private extractCode(message: string): string | null {
    const regex = /ESIA-\d{6}/;
    const match = message.match(regex);
    return match ? match[0] : null;
  }
}