import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree} from '@angular/router';
import {from, isObservable, map, Observable, of, switchMap} from 'rxjs';
import {AuthService} from '@services/auth.service';
import {MatDialog} from '@angular/material/dialog';
import {LoginDialogComponent} from '@shared-components/login/login.component';
import {catchError} from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  constructor(
    private authService: AuthService,
    private router: Router,
    private dialog: MatDialog
  ) {
  }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    const code = route.paramMap.get('code');
    if (code) {
      return this.authService.login(code).pipe(
        switchMap(() => {
          const result = this.canActivateInternal(route, state);
          if (route.routeConfig?.path === 'login/:code') {
            this.router.navigateByUrl('/', {replaceUrl: true}).then();
          }
          return isObservable(result) ? result : result instanceof Promise ? from(result) : of(result);
        }),
        catchError(() => of(this.router.createUrlTree(['/'])))
      );
    }
    return this.canActivateInternal(route, state);
  }

  private canActivateInternal(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    if (!this.authService.isAuthenticated$()) {
      this.router.createUrlTree(['/']);
      const dialogRef = this.dialog.open(LoginDialogComponent, {
        width: '400px',
      })
      return dialogRef.afterClosed().pipe(
        map(result => {
          if (result) {
            return this.router.createUrlTree([state.url]);
          }
          return this.router.createUrlTree(['/']);
        })
      );
    }

    const requiredAuthorizations = route.data['requiredAuthorizations'] as string[];

    if (!requiredAuthorizations || requiredAuthorizations.length === 0) {
      return true;
    }

    const userAuthorizations = this.authService.getUserAuthorizations();
    const hasAccess = requiredAuthorizations.some(auth => userAuthorizations.includes(auth));

    if (!hasAccess) {
      return this.router.createUrlTree(['/']);
    }

    return true;
  }
}
