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,
  KEY_URL_INFO
} from '@app/types';
import { AppAuthenticationService } from '@app/services/authentication.service';
import { NGXLogger } from 'ngx-logger';
import { Store } from '@ngrx/store';
import {
  SessionStorageService,
  LocalStorageService
} 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';
import { DaesangUrlInfoResponse } from '@ucap-webmessenger/api-external';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';
import { DaesangCipherService } from '@ucap-webmessenger/daesang';

@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.protocolService.disconnect();
          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.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: '로그아웃 하시겠습니까?'
            }
          });

          if (!!result && !!result.choice && result.choice) {
            return true;
          } else {
            return false;
          }
        } 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) {
            // [Daesang]
            const urlInfo: DaesangUrlInfoResponse = this.sessionStorageService.get<
              DaesangUrlInfoResponse
            >(KEY_URL_INFO);

            await this.dialogService.open<
              AlertDialogComponent,
              AlertDialogData,
              AlertDialogResult
            >(AlertDialogComponent, {
              data: {
                title: '비밀번호 만기',
                html: `비밀번호가 만료되었습니다.<br/>비밀번호 변경 후 다시 로그인 하세요.`
              }
            });

            const passwordChangeUrl = urlInfo.webLink.filter(
              weblink => weblink.key === 'WebLinkChgPassword'
            );
            if (passwordChangeUrl.length > 0) {
              const appUserInfo = this.localStorageService.encGet<AppUserInfo>(
                KEY_APP_USER_INFO,
                environment.customConfig.appKey
              );
              const loginPw = appUserInfo.loginPw;
              const loginId = appUserInfo.loginId;

              const url = passwordChangeUrl[0].url
                .replace(/(\(%USER_ID%\))/g, loginId)
                .replace(
                  /(\(%USER_PASS%\))/g,
                  this.daesangCipherService.encryptForWebLink(loginPw)
                );

              this.nativeService.openDefaultBrowser(url);
            }

            this.store.dispatch(logout());

            // // [GROUP]
            // 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 {
            //   this.store.dispatch(logout());
            //   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 localStorageService: LocalStorageService,
    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,
    private daesangCipherService: DaesangCipherService
  ) {}
}