import { interval, timer } from 'rxjs';
import { map, exhaustMap, tap, takeUntil, take } from 'rxjs/operators';

import { Injectable, Inject } from '@angular/core';
import { Router } from '@angular/router';

import { Actions, createEffect, ofType } from '@ngrx/effects';

import { LoginTry } from '@ucap/pi';

import { LoginResponse } from '@ucap/protocol-authentication';
import { NativeService } from '@ucap/native';

import { LogService } from '@ucap/ng-logger';
import { SessionStorageService } from '@ucap/ng-web-storage';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { LoginActions } from '@ucap/ng-store-authentication';

import { AppKey } from '@app/types/app-key.type';
import { AppAuthenticationService } from '@app/services/app-authentication.service';
import { AppActions } from '@app/store/actions';

import { environment } from '@environments';

@Injectable()
export class Effects {
  readonly intervalForRetry =
    environment.productConfig.authentication.intervalForRetry;
  readonly maxTryCount = environment.productConfig.authentication.maxTryCount;

  webLoginSuccess$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(LoginActions.webLoginSuccess),
        tap((params) => {
          this.nativeService.checkForUpdates(params.login2Response.version);
          this.appAuthenticationService.postWebLogin(
            {
              companyCode: params.companyCode,
              loginId: params.loginId,
              loginPw: params.loginPw
            },
            params.rememberMe,
            params.autoLogin
          );
          this.router.navigate(['/']);
        })
      ),
    { dispatch: false }
  );

  webLoginFailure$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(LoginActions.webLoginFailure),
        tap((params) => {
          let loginTry = this.sessionStorageService.get<LoginTry>(
            AppKey.LoginTry
          );
          loginTry = loginTry || {
            failCount: 0,
            lastFailTime: null,
            remainTimeForNextTry: null
          };

          if (null === loginTry.lastFailTime) {
            loginTry = {
              failCount: loginTry.failCount + 1,
              lastFailTime: new Date().getTime(),
              remainTimeForNextTry: null
            };
          } else {
            const diff = Math.round(
              (new Date().getTime() - loginTry.lastFailTime) / 1000
            );

            if (diff > this.intervalForRetry) {
              loginTry = {
                failCount: 1,
                lastFailTime: new Date().getTime(),
                remainTimeForNextTry: null
              };
            } else {
              loginTry.failCount++;
            }
          }

          if (this.maxTryCount <= loginTry.failCount) {
            loginTry.remainTimeForNextTry = this.intervalForRetry;

            const waitTimer$ = timer(1000 * this.intervalForRetry);
            interval(1000)
              .pipe(takeUntil(waitTimer$))
              .subscribe(
                (v) => {
                  loginTry.remainTimeForNextTry = this.intervalForRetry - v;
                  this.sessionStorageService.set<LoginTry>(
                    AppKey.LoginTry,
                    loginTry
                  );
                },
                (error) => {},
                () => {
                  loginTry = {
                    failCount: 0,
                    lastFailTime: null,
                    remainTimeForNextTry: null
                  };
                  this.sessionStorageService.set<LoginTry>(
                    AppKey.LoginTry,
                    loginTry
                  );
                }
              );
          }

          this.sessionStorageService.set<LoginTry>(AppKey.LoginTry, loginTry);
        })
      ),
    { dispatch: false }
  );

  sessionCreated$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(LoginActions.sessionCreated),
        tap((params) => {
          this.appAuthenticationService.setLoginSession({ alive: true });
        })
      ),
    { dispatch: false }
  );

  sessionDestroyed$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(LoginActions.sessionDestroyed),
        tap((params) => {
          this.appAuthenticationService.setLoginSession({ alive: false });
        })
      ),
    { dispatch: false }
  );

  constructor(
    private actions$: Actions,
    private sessionStorageService: SessionStorageService,
    @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
    private appAuthenticationService: AppAuthenticationService,
    private router: Router,
    private logService: LogService
  ) {}
}
