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

import { of, Observable } from 'rxjs';
import { catchError, exhaustMap, map, tap, switchMap } from 'rxjs/operators';

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

import {
  PiService,
  Login2Response,
  UserTermsActionResponse
} from '@ucap-webmessenger/pi';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import {
  DialogService,
  ConfirmDialogComponent,
  ConfirmDialogData,
  ConfirmDialogResult,
  SnackBarService,
  AlertDialogComponent,
  AlertDialogData,
  AlertDialogResult
} from '@ucap-webmessenger/ui';

import {
  loginRedirect,
  logout,
  logoutConfirmation,
  logoutConfirmationDismiss,
  webLogin,
  webLoginSuccess,
  webLoginFailure,
  postLogin,
  privacyAgree,
  privacyDisagree,
  privacyAgreeFailure,
  privacyAgreeSuccess,
  increaseLoginFailCount,
  initialLoginFailCount,
  logoutInitialize,
  userPasswordSet,
  userPasswordSetSuccess,
  userPasswordSetFailure
} from './actions';
import {
  LoginInfo,
  KEY_LOGIN_INFO,
  EnvironmentsInfo,
  KEY_ENVIRONMENTS_INFO
} from '@app/types';
import { AppAuthenticationService } from '@app/services/authentication.service';
import { NGXLogger } from 'ngx-logger';
import { Store } from '@ngrx/store';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';

import {
  AuthenticationProtocolService,
  LoginResponse
} from '@ucap-webmessenger/protocol-authentication';
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import { ProtocolService } from '@ucap-webmessenger/protocol';

import { environment } from '../../../../environments/environment';
import {
  ChangePasswordDialogComponent,
  ChangePasswordDialogData,
  ChangePasswordDialogResult
} from '@app/layouts/messenger/dialogs/account/change-password.dialog.component';
import {
  ServiceProtocolService,
  UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service';

@Injectable()
export class Effects {
  webLogin$ = createEffect(() =>
    this.actions$.pipe(
      ofType(webLogin),
      map(action => action),
      exhaustMap(
        (params: {
          loginInfo: LoginInfo;
          rememberMe: boolean;
          autoLogin: boolean;
        }) =>
          this.piService
            .login2({
              loginId: params.loginInfo.loginId,
              loginPw: params.loginInfo.loginPw,
              companyCode: params.loginInfo.companyCode
            })
            .pipe(
              map((res: Login2Response) => {
                if ('success' !== res.status.toLowerCase()) {
                  this.store.dispatch(increaseLoginFailCount({}));
                  return webLoginFailure({ error: 'Failed' });
                } else {
                  this.store.dispatch(initialLoginFailCount({}));
                  return webLoginSuccess({
                    loginInfo: params.loginInfo,
                    rememberMe: params.rememberMe,
                    autoLogin: params.autoLogin,
                    login2Response: res
                  });
                }
              }),
              catchError(error => of(webLoginFailure({ error })))
            )
      )
    )
  );

  webLoginSuccess$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(webLoginSuccess),
        switchMap(
          params =>
            new Observable<void>(subscriber => {
              this.nativeService
                .checkForUpdates(params.login2Response.version)
                .then((update: boolean) => {
                  if (!update) {
                    this.appAuthenticationService.login(
                      params.loginInfo,
                      params.rememberMe,
                      params.autoLogin
                    );
                    this.router.navigate(['/messenger']);
                  }
                  subscriber.next();
                })
                .catch(reason => {
                  subscriber.error(reason);
                })
                .finally(() => {
                  subscriber.complete();
                });
            })
        )
      ),
    { dispatch: false }
  );

  loginRedirect$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(loginRedirect),
        tap(authed => {
          // this.ngZone.run(() => {
          // location.href = this.router.parseUrl('/account/login').toString();
          this.router.navigate(['/account/login']);
          this.appAuthenticationService.logout();
          this.store.dispatch(logoutInitialize());
          // });
        })
      ),
    { dispatch: false }
  );

  logout$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(logout),
        switchMap(action => {
          return this.authenticationProtocolService.logout({}).pipe(
            map(res => {
              this.protocolService.disconnect();
              this.store.dispatch(loginRedirect());
            }),
            catchError(error => of(error))
          );
        })
      );
    },
    { dispatch: false }
  );

  logoutConfirmation$ = createEffect(() =>
    this.actions$.pipe(
      ofType(logoutConfirmation),
      exhaustMap(async () => {
        const loginRes = this.sessionStorageService.get<LoginResponse>(
          KEY_LOGIN_RES_INFO
        );

        if (!!loginRes && loginRes.userSeq) {
          const result = await this.dialogService.open<
            ConfirmDialogComponent,
            ConfirmDialogData,
            ConfirmDialogResult
          >(ConfirmDialogComponent, {
            data: {
              title: 'Logout',
              message: '로그아웃 하시겠습니까?'
            }
          });

          return result.choice;
        } else {
          return false;
        }
      }),
      map(result => (result ? logout() : logoutConfirmationDismiss()))
    )
  );

  postLogin$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(postLogin),
        map(action => action.loginRes),
        tap(async loginRes => {
          const loginInfo = this.sessionStorageService.get<LoginInfo>(
            KEY_LOGIN_INFO
          );
          const environmentsInfo = this.sessionStorageService.get<
            EnvironmentsInfo
          >(KEY_ENVIRONMENTS_INFO);

          if (
            environment.productConfig.authentication
              .usePrivateInformationAgree &&
            !loginRes.privateInformationAgree
          ) {
            const privacyTotalUrl = this.piService.privacyTotalUrl({
              companyCode: loginInfo.companyCode,
              userSeq: loginRes.userSeq,
              token: loginRes.tokenString,
              deviceType: environmentsInfo.deviceType,
              localeCode: loginInfo.localeCode,
              textOnly: 'true'
            });

            const result = await this.dialogService.open<
              ConfirmDialogComponent,
              ConfirmDialogData,
              ConfirmDialogResult
            >(ConfirmDialogComponent, {
              width: '100%',
              height: '500px',
              disableClose: true,
              data: {
                title: '개인정보 동의',
                html: `<iframe id="ifm_privacy" src="${privacyTotalUrl}" style="width: 100%;height: 300px;" />`
              }
            });

            if (result.choice) {
              this.store.dispatch(privacyAgree({ loginRes }));
            } else {
              this.store.dispatch(privacyDisagree());
              return;
            }
          }

          if (loginInfo.initPw) {
            this.store.dispatch(logout());

            const passwordInitStep1Url = this.piService.passwordInitStep1Url({
              localeCode: loginInfo.localeCode
            });

            const result = await this.dialogService.open<
              ConfirmDialogComponent,
              ConfirmDialogData,
              ConfirmDialogResult
            >(ConfirmDialogComponent, {
              width: '100%',
              height: '500px',
              disableClose: true,
              data: {
                title: '패스워드 초기화',
                html: `<iframe id="ifm_privacy" src="${passwordInitStep1Url}" style="width: 100%;height: 300px;" />`
              }
            });

            if (result.choice) {
            } else {
              return;
            }
          }

          if (!loginRes.passwordValid) {
            const result = await this.dialogService.open<
              ChangePasswordDialogComponent,
              ChangePasswordDialogData,
              ChangePasswordDialogResult
            >(ChangePasswordDialogComponent, {
              width: '500px',
              height: '500px',
              disableClose: false,
              data: {
                loginId: loginInfo.loginId,
                encryptedLoginPw: loginInfo.loginPw,
                phoneNumber: loginRes.userInfo.hpNumber
              }
            });

            if (!!result && result.choice) {
              this.store.dispatch(
                userPasswordSet({
                  req: {
                    companyCode: loginInfo.companyCode,
                    loginId: loginInfo.loginId,
                    oldLoginPw: result.currentLoginPw,
                    newLoginPw: result.newLoginPw
                  }
                })
              );
            } else {
              return;
            }
          }
        })
      ),
    { dispatch: false }
  );

  privacyAgree$ = createEffect(() =>
    this.actions$.pipe(
      ofType(privacyAgree),
      map(action => {
        const loginInfo = this.sessionStorageService.get<LoginInfo>(
          KEY_LOGIN_INFO
        );
        const environmentsInfo = this.sessionStorageService.get<
          EnvironmentsInfo
        >(KEY_ENVIRONMENTS_INFO);

        return {
          loginInfo,
          environmentsInfo,
          loginResponse: action.loginRes
        };
      }),
      exhaustMap(params =>
        this.piService
          .userTermsAction({
            userSeq: params.loginResponse.userSeq,
            token: params.loginResponse.tokenString,
            deviceType: params.environmentsInfo.deviceType
          })
          .pipe(
            map((res: UserTermsActionResponse) => {
              if ('00' !== res.responseCode) {
                return privacyAgreeFailure({ error: 'Failed' });
              } else {
                return privacyAgreeSuccess();
              }
            }),
            catchError(error => of(privacyAgreeFailure({ error })))
          )
      )
    )
  );

  userPasswordSet$ = createEffect(() =>
    this.actions$.pipe(
      ofType(userPasswordSet),
      map(action => action.req),
      exhaustMap(req =>
        this.serviceProtocolService.userPasswordSet(req).pipe(
          map((res: UserPasswordSetResponse) => {
            return userPasswordSetSuccess({
              res
            });
          }),
          catchError(error => of(userPasswordSetFailure({ error })))
        )
      )
    )
  );

  userPasswordSetSuccess$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(userPasswordSetSuccess),
        tap(async action => {
          await this.dialogService.open<
            AlertDialogComponent,
            AlertDialogData,
            AlertDialogResult
          >(AlertDialogComponent, {
            width: '360px',
            disableClose: true,
            data: {
              title: '비밀번호 변경',
              message: '비밀번호가 변경되었습니다. 다시 로그인하여 주십시오'
            }
          });

          this.store.dispatch(logout());
        })
      );
    },
    { dispatch: false }
  );

  userPasswordSetFailure$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(userPasswordSetFailure),
        tap(action => {
          this.snackBarService.open(
            `비밀번호 변경 중에 문제가 발생하였습니다.`,
            '',
            {
              duration: 3000,
              verticalPosition: 'bottom'
            }
          );
        })
      );
    },
    { dispatch: false }
  );

  constructor(
    private actions$: Actions,
    private ngZone: NgZone,
    private router: Router,
    private store: Store<any>,
    private sessionStorageService: SessionStorageService,
    private piService: PiService,
    private appAuthenticationService: AppAuthenticationService,
    private protocolService: ProtocolService,
    private authenticationProtocolService: AuthenticationProtocolService,
    private serviceProtocolService: ServiceProtocolService,
    private dialogService: DialogService,
    private snackBarService: SnackBarService,
    @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
    private logger: NGXLogger
  ) {}
}