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
} from '@ucap-webmessenger/ui';

import {
  loginRedirect,
  logout,
  logoutConfirmation,
  logoutConfirmationDismiss,
  webLogin,
  webLoginSuccess,
  webLoginFailure,
  postLogin,
  privacyAgree,
  privacyDisagree,
  privacyAgreeFailure,
  privacyAgreeSuccess,
  changePassword,
  changePasswordFailure,
  changePasswordSuccess,
  increaseLoginFailCount,
  initialLoginFailCount,
  logoutInitialize
} 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 {
  ServiceProtocolService,
  UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service';
import { AuthenticationProtocolService } from '@ucap-webmessenger/protocol-authentication';

@Injectable()
export class Effects {
  webLogin$ = createEffect(() =>
    this.actions$.pipe(
      ofType(webLogin),
      map(action => action),
      exhaustMap((params: { loginInfo: LoginInfo; rememberMe: 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,
                  login2Response: res
                });
              }
            }),
            catchError(error => of(webLoginFailure({ error })))
          )
      )
    )
  );

  webLoginSuccess$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(webLoginSuccess),
        switchMap(
          params =>
            new Observable<void>(subscriber => {
              this.nativeService
                .checkForUpdates()
                .then((update: boolean) => {
                  if (!update) {
                    this.appAuthenticationService.login(
                      params.loginInfo,
                      params.rememberMe
                    );
                    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(() => {
            this.appAuthenticationService.logout();
            this.store.dispatch(logoutInitialize());
            location.href = this.router.parseUrl('/account/login').toString();
          });
        })
      ),
    { 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 result = await this.dialogService.open<
          ConfirmDialogComponent,
          ConfirmDialogData,
          ConfirmDialogResult
        >(ConfirmDialogComponent, {
          width: '220px',
          data: {
            title: 'Logout',
            message: 'Logout ?'
          }
        });

        return result.choice;
      }),
      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 (!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 (!!loginRes.passwordExpired) {
            const result = await this.dialogService.open<
              ConfirmDialogComponent,
              ConfirmDialogData,
              ConfirmDialogResult
            >(ConfirmDialogComponent, {
              width: '100%',
              height: '500px',
              disableClose: true,
              data: {
                title: '패스워드 만료',
                message: ''
              }
            });

            if (result.choice) {
            } 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 })))
          )
      )
    )
  );

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

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