import {
  CommonApiService,
  MassTalkSaveRequest,
  FileTalkShareRequest,
  FileTalkShareMultiRequest,
  FileTalkShareMultiResponse
} from '@ucap-webmessenger/api-common';
import { KEY_ENVIRONMENTS_INFO } from './../../../types/environment.type';
import { Injectable, Inject } from '@angular/core';

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

import { Store, select } from '@ngrx/store';

import { NGXLogger } from 'ngx-logger';

import { of } from 'rxjs';
import {
  tap,
  switchMap,
  map,
  catchError,
  exhaustMap,
  withLatestFrom,
  concatMap,
  take
} from 'rxjs/operators';
import moment from 'moment';
import {
  InfoData,
  Info,
  InfoResponse,
  EventProtocolService,
  SVC_TYPE_EVENT,
  SSVC_TYPE_EVENT_INFO_DATA,
  SSVC_TYPE_EVENT_INFO_RES,
  SendResponse,
  ReadResponse,
  DelResponse,
  CancelResponse,
  EventType,
  ReadNotification,
  SSVC_TYPE_EVENT_SEND_RES,
  SSVC_TYPE_EVENT_SEND_NOTI,
  EventJson,
  StickerEventJson,
  FileEventJson,
  BundleImageEventJson,
  MassTextEventJson,
  TranslationEventJson,
  MassTranslationEventJson,
  decodeFileEventJson,
  decodeBundleImageEventJson
} from '@ucap-webmessenger/protocol-event';

import * as ChatStore from '@app/store/messenger/chat';
import * as RoomStore from '@app/store/messenger/room';
import * as SyncStore from '@app/store/messenger/sync';

import {
  NativeService,
  UCAP_NATIVE_SERVICE,
  WindowState
} from '@ucap-webmessenger/native';
import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core';

import {
  info,
  infoSuccess,
  infoFailure,
  send,
  sendSuccess,
  sendFailure,
  appendInfoList,
  newInfo,
  sendNotification,
  readNotification,
  cancelNotification,
  delNotification,
  recallInfoList,
  read,
  readFailure,
  del,
  delFailure,
  delInfoList,
  cancel,
  cancelFailure,
  forward,
  forwardAfterRoomOpen,
  sendMass,
  sendMassFailure,
  infoMoreSuccess,
  infoIntervalClear,
  fileInfo,
  fileInfoSuccess,
  fileInfoFailure,
  roomOpenAfterForward,
  infoForSearch,
  infoForSearchEnd,
  infoAll,
  forwardFailure
} from './actions';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import {
  RoomInfo,
  RoomProtocolService,
  OpenResponse
} from '@ucap-webmessenger/protocol-room';
import { LoginInfo, KEY_LOGIN_INFO, EnvironmentsInfo } from '@app/types';
import { Dictionary } from '@ngrx/entity';
import { openSuccess, openFailure } from '../room';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import { StatusCode } from '@ucap-webmessenger/api';
import {
  FileProtocolService,
  SSVC_TYPE_FILE_INFO_DATA,
  SSVC_TYPE_FILE_INFO_CHECK_DATA,
  SSVC_TYPE_FILE_INFO_RES,
  FileInfo,
  FileDownloadInfo,
  InfoData as FileInfoData,
  InfoCheckData as FileInfoCheckData,
  InfoResponse as FileInfoResponse,
  FileType
} from '@ucap-webmessenger/protocol-file';
import { environment } from '../../../../environments/environment';
import {
  AlertDialogComponent,
  AlertDialogResult,
  AlertDialogData,
  DialogService
} from '@ucap-webmessenger/ui';
import { TranslateService } from '@ngx-translate/core';

@Injectable()
export class Effects {
  // selectedRoomForInfo$ = createEffect(() =>
  //   this.actions$.pipe(
  //     ofType(ChatStore.selectedRoom),
  //     map(action => {
  //       return info({
  //         roomSeq: action.roomSeq,
  //         baseSeq: 0,
  //         requestCount: environment.productConfig.CommonSetting.eventRequestDefaultCount
  //       });
  //     })
  //   )
  // );

  selectedRoomForInfo$ = createEffect(() =>
    this.actions$.pipe(
      ofType(RoomStore.infoSuccess),
      map(action => {
        const roomInfo = action.roomInfo;
        let requestCount =
          environment.productConfig.CommonSetting.eventRequestInitCount;

        // 여기까지 읽음 처리..
        if (!!roomInfo.finalEventSeq && !!roomInfo.lastReadEventSeq) {
          requestCount = roomInfo.finalEventSeq - roomInfo.lastReadEventSeq;

          // 기존 요청개수보다 요청할 갯수가 적을 경우 기본값.
          if (
            environment.productConfig.CommonSetting.eventRequestInitCount >=
            requestCount
          ) {
            requestCount =
              environment.productConfig.CommonSetting.eventRequestInitCount;
          } else {
            // 여기까지 읽음 처리를 위한 최대 요청 개수 제한.
            if (
              environment.productConfig.CommonSetting
                .readHereShowMaximumEventCount < requestCount
            ) {
              requestCount =
                environment.productConfig.CommonSetting
                  .readHereEventRequestCount;
            } else {
              requestCount = requestCount + 5;
            }
          }
        }

        return info({
          roomSeq: roomInfo.roomSeq,
          baseSeq: 0,
          requestCount
        });
      })
    )
  );

  info$ = createEffect(
    () => {
      let infoList: Info<EventJson>[];

      return this.actions$.pipe(
        ofType(info),
        tap(() => {
          infoList = [];
        }),
        switchMap(req => {
          return this.eventProtocolService.info(req).pipe(
            map(res => {
              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_EVENT_INFO_DATA:
                  infoList.push(...(res as InfoData).infoList);
                  break;
                case SSVC_TYPE_EVENT_INFO_RES:
                  {
                    if (req.baseSeq === 0) {
                      this.store.dispatch(
                        infoSuccess({
                          infoList,
                          res: res as InfoResponse,
                          remainInfo:
                            infoList.length === req.requestCount ? true : false
                        })
                      );
                    } else {
                      this.store.dispatch(
                        infoMoreSuccess({
                          infoList,
                          res: res as InfoResponse,
                          remainInfo:
                            infoList.length === req.requestCount ? true : false
                        })
                      );
                    }

                    if (req.baseSeq === 0) {
                      // 최초 이벤트 목록 조회
                      // SSVC_TYPE_EVENT_READ_REQ 수행.
                      const maxSeq = Math.max.apply(
                        Math,
                        infoList.map(v => v.seq)
                      );
                      this.store.dispatch(
                        read({
                          roomSeq: req.roomSeq,
                          lastReadSeq: Number(maxSeq)
                        })
                      );

                      // File 정보 수집.
                      this.store.dispatch(
                        fileInfo({
                          req: {
                            roomSeq: req.roomSeq,
                            // { 파일타입 } cf) I : 이미지 V: 동영상 F: 파일 "" 빈값이면 모든 타입을 내려줌
                            type: FileType.All
                          }
                        })
                      );
                    }
                  }
                  break;
              }
            }),
            catchError(error => of(infoFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  infoForSearch$ = createEffect(
    () => {
      let infoList: Info<EventJson>[];

      return this.actions$.pipe(
        ofType(infoForSearch),
        tap(() => {
          infoList = [];
        }),
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.event.infoSearchListProcessing as boolean
            )
          )
        ),
        switchMap(([action, processing]) => {
          return this.eventProtocolService.info(action.req).pipe(
            map(async res => {
              const req = action.req;

              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_EVENT_INFO_DATA:
                  infoList.push(...(res as InfoData).infoList);
                  break;
                case SSVC_TYPE_EVENT_INFO_RES:
                  {
                    if (req.baseSeq === 0) {
                      this.store.dispatch(
                        infoSuccess({
                          infoList,
                          res: res as InfoResponse,
                          remainInfo:
                            infoList.length === req.requestCount ? true : false
                        })
                      );
                    } else {
                      this.store.dispatch(
                        infoMoreSuccess({
                          infoList,
                          res: res as InfoResponse,
                          remainInfo:
                            infoList.length === req.requestCount ? true : false
                        })
                      );
                    }

                    // 검색어가 있을경우 조회된 이벤트 리스트 중 검색어를 찾고, 없으면 재귀한다.
                    if (!!action.searchText && infoList.length > 0) {
                      const searchList = infoList.filter(event => {
                        let contents = '';
                        if (event.type === EventType.Character) {
                          contents = event.sentMessage;
                        } else if (
                          event.type === EventType.Sticker &&
                          !!event.sentMessageJson
                        ) {
                          contents = (event.sentMessageJson as StickerEventJson)
                            .chat;
                        } else if (
                          event.type === EventType.File &&
                          !!event.sentMessageJson
                        ) {
                          contents = (event.sentMessageJson as FileEventJson)
                            .fileName;
                        } else if (
                          event.type === EventType.MassText &&
                          !!event.sentMessageJson
                        ) {
                          contents = (event.sentMessageJson as MassTextEventJson)
                            .content;
                        } else if (
                          event.type === EventType.Translation &&
                          !!event.sentMessageJson
                        ) {
                          contents = (event.sentMessageJson as TranslationEventJson)
                            .original;
                        } else if (
                          event.type === EventType.MassTranslation &&
                          !!event.sentMessageJson
                        ) {
                          contents = (event.sentMessageJson as MassTranslationEventJson)
                            .original;
                        }

                        return contents.indexOf(action.searchText) > -1;
                      });
                      if (
                        searchList.length === 0 &&
                        infoList.length === action.req.requestCount &&
                        processing
                      ) {
                        // 재귀
                        this.store.dispatch(
                          infoForSearch({
                            req: {
                              roomSeq: req.roomSeq,
                              baseSeq: infoList[0].seq,
                              requestCount: req.requestCount
                            },
                            searchText: action.searchText
                          })
                        );

                        // // 재귀하지 않는다.
                        // this.store.dispatch(infoForSearchEnd({}));
                      } else {
                        if (infoList.length < action.req.requestCount) {
                          this.store.dispatch(infoForSearchEnd({}));

                          await this.dialogService.open<
                            AlertDialogComponent,
                            AlertDialogData,
                            AlertDialogResult
                          >(AlertDialogComponent, {
                            width: '360px',
                            disableClose: true,
                            data: {
                              title: '',
                              message: this.translateService.instant(
                                'chat.noMoreEvents'
                              )
                            }
                          });
                        }
                      }
                    }
                  }
                  break;
              }
            }),
            catchError(error => of(infoFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  infoAll$ = createEffect(
    () => {
      let infoList: Info<EventJson>[];

      return this.actions$.pipe(
        ofType(infoAll),
        tap(() => {
          infoList = [];
        }),
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.event.infoSearchListProcessing as boolean
            )
          )
        ),
        switchMap(([params, processing]) => {
          const req = params.req;
          const mergedInfoList = params.infoList;

          return this.eventProtocolService.info(req).pipe(
            map(async res => {
              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_EVENT_INFO_DATA:
                  infoList.push(...(res as InfoData).infoList);
                  break;
                case SSVC_TYPE_EVENT_INFO_RES:
                  {
                    if (!!infoList && 0 < infoList.length) {
                      infoList = infoList.sort((a, b) => a.seq - b.seq);
                    }
                    if (
                      infoList.length > 0 &&
                      infoList.length >= req.requestCount &&
                      processing
                    ) {
                      // 재귀
                      this.store.dispatch(
                        infoAll({
                          req: {
                            roomSeq: req.roomSeq,
                            baseSeq: infoList[0].seq,
                            requestCount: req.requestCount
                          },
                          infoList: !!mergedInfoList
                            ? [...infoList, ...mergedInfoList]
                            : infoList
                        })
                      );
                    } else {
                      this.store.dispatch(
                        infoMoreSuccess({
                          infoList: !!mergedInfoList
                            ? [...infoList, ...mergedInfoList]
                            : infoList,
                          res: res as InfoResponse,
                          remainInfo:
                            infoList.length === req.requestCount ? true : false
                        })
                      );

                      this.store.dispatch(infoForSearchEnd({}));
                    }
                  }
                  break;
              }
            }),
            catchError(error => of(infoFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  fileInfo$ = createEffect(
    () => {
      let fileInfoList: FileInfo[];
      let fileInfoCheckList: FileDownloadInfo[];

      return this.actions$.pipe(
        ofType(fileInfo),
        tap(() => {
          fileInfoList = [];
          fileInfoCheckList = [];
        }),
        switchMap(action => {
          return this.fileProtocolService.info(action.req).pipe(
            map(res => {
              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_FILE_INFO_DATA:
                  fileInfoList.push(...(res as FileInfoData).fileInfos);
                  break;
                case SSVC_TYPE_FILE_INFO_CHECK_DATA:
                  fileInfoCheckList.push(
                    ...(res as FileInfoCheckData).fileDownloadInfos
                  );
                  break;
                case SSVC_TYPE_FILE_INFO_RES:
                  {
                    this.store.dispatch(
                      fileInfoSuccess({
                        fileInfoList,
                        fileInfoCheckList,
                        res: res as FileInfoResponse
                      })
                    );
                  }
                  break;
              }
            }),
            catchError(error => of(fileInfoFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  infoIntervalClear$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(infoIntervalClear),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.room.roomInfo as RoomInfo)
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.event.infoList.entities as Dictionary<
                  Info<EventJson>
                >
            )
          )
        ),
        map(([action, roomInfo, eventList]) => {
          if (roomInfo.isTimeRoom && roomInfo.timeRoomInterval > 0) {
            const delEventSeq: number[] = [];
            // tslint:disable-next-line: forin
            for (const key in eventList) {
              const event: Info<EventJson> = eventList[key];
              if (
                new Date().getTime() -
                  moment(event.sendDate)
                    .toDate()
                    .getTime() >=
                roomInfo.timeRoomInterval * 1000
              ) {
                delEventSeq.push(event.seq);
              }
            }

            if (delEventSeq.length > 0) {
              this.store.dispatch(delInfoList({ eventSeqs: delEventSeq }));
            }
          }
        })
      );
    },
    { dispatch: false }
  );

  read$ = createEffect(() =>
    this.actions$.pipe(
      ofType(read),
      exhaustMap(req =>
        this.eventProtocolService.read(req).pipe(
          map((res: ReadResponse) => {
            return readNotification(res as ReadNotification);
          }),
          catchError(error => of(readFailure({ error })))
        )
      )
    )
  );

  readNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(readNotification),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.room.roomInfo as RoomInfo)
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.account.authentication.loginRes as LoginResponse
            )
          )
        ),
        // map(([action, roomInfo]) => ([action.noti, roomInfo])),
        tap(([action, roomInfo, loginRes]) => {
          // 현재 오픈된 방에 대한 read noti/res 가 유입될 경우 roomUserList 의 lastReadSeq 를 갱신한다.
          if (
            !!roomInfo &&
            !!roomInfo.roomSeq &&
            roomInfo.roomSeq === action.roomSeq
          ) {
            this.store.dispatch(RoomStore.updateRoomUserLastReadSeq(action));
          }

          // noti/res 를 일으킨 주체가 본인이라면 현재 오픈된 방 여부에 상관없이 해당방에 대한 noReadCnt 를 초기화 한다.
          if (action.SENDER_SEQ === loginRes.userSeq) {
            this.store.dispatch(
              SyncStore.updateUnreadCount({
                roomSeq: action.roomSeq,
                noReadCnt: 0
              })
            );
          }
        })
      );
    },
    { dispatch: false }
  );

  send$ = createEffect(() =>
    this.actions$.pipe(
      ofType(send),
      concatMap(action =>
        this.eventProtocolService.send(action.req).pipe(
          map((res: SendResponse) => {
            return sendSuccess({
              senderSeq: action.senderSeq,
              res
            });
          }),
          catchError(error => of(sendFailure({ error })))
        )
      )
    )
  );

  sendSuccess$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(sendSuccess),
        tap(action => {
          const res = action.res;

          this.store.dispatch(
            newInfo({
              roomSeq: res.roomSeq,
              info: res.info,
              SVC_TYPE: res.SVC_TYPE,
              SSVC_TYPE: res.SSVC_TYPE
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  sendNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(sendNotification),
        map(action => action.noti),
        tap(noti => {
          this.store.dispatch(
            newInfo({
              roomSeq: noti.roomSeq,
              info: noti.info,
              SVC_TYPE: noti.SVC_TYPE,
              SSVC_TYPE: noti.SSVC_TYPE
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  forward$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(forward),
        tap(action => {
          const loginResInfo: LoginResponse = this.sessionStorageService.get<
            LoginResponse
          >(KEY_LOGIN_RES_INFO);

          const environmentsInfo = this.sessionStorageService.get<
            EnvironmentsInfo
          >(KEY_ENVIRONMENTS_INFO);
          if (!!action.trgtRoomSeq) {
            // 대화전달 후 방오픈. Exist roomSeq.
            if (action.req.eventType === EventType.File) {
              const fileEventJson: FileEventJson = decodeFileEventJson(
                action.req.sentMessage
              );
              const req: FileTalkShareRequest = {
                userSeq: loginResInfo.userSeq,
                deviceType: environmentsInfo.deviceType,
                token: loginResInfo.tokenString,
                attachmentsSeq: fileEventJson.attachmentSeq.toString(),
                roomSeq: action.trgtRoomSeq,
                synapKey: ''
              };
              this.commonApiService
                .fileTalkShare(req)
                .pipe(
                  take(1),
                  map(res => {
                    if (res.statusCode === StatusCode.Success) {
                      action = {
                        ...action,
                        req: {
                          ...action.req,
                          sentMessage: res.returnJson
                        }
                      };

                      this.store.dispatch(roomOpenAfterForward(action));
                    } else {
                      this.store.dispatch(forwardFailure({ error: res }));
                    }
                  }),
                  catchError(error => of(forwardFailure({ error })))
                )
                .subscribe();
            } else if (action.req.eventType === EventType.BundleImage) {
              const fileEventJson: BundleImageEventJson = decodeBundleImageEventJson(
                action.req.sentMessage
              );
              const req: FileTalkShareMultiRequest = {
                userSeq: loginResInfo.userSeq,
                deviceType: environmentsInfo.deviceType,
                token: loginResInfo.tokenString,
                attachmentsSeq: fileEventJson.attachmentSeq.toString(),
                roomSeq: action.trgtRoomSeq
              };

              this.commonApiService
                .fileTalkShareMulti(req)
                .pipe(
                  take(1),
                  map(res => {
                    if (res.statusCode === StatusCode.Success) {
                      action = {
                        ...action,
                        req: {
                          ...action.req,
                          sentMessage: res.returnJson
                        }
                      };

                      this.store.dispatch(roomOpenAfterForward(action));
                    } else {
                      this.store.dispatch(forwardFailure({ error: res }));
                    }
                  }),
                  catchError(error => of(forwardFailure({ error })))
                )
                .subscribe();
            } else {
              this.store.dispatch(roomOpenAfterForward(action));
            }
          } else if (!!action.trgtUserSeqs && action.trgtUserSeqs.length > 0) {
            // 방오픈 후 대화전달.
            this.store.dispatch(forwardAfterRoomOpen(action));
          }
        })
      );
    },
    { dispatch: false }
  );

  forwardAfterRoomOpen$ = createEffect(() =>
    this.actions$.pipe(
      ofType(forwardAfterRoomOpen),
      exhaustMap(action => {
        return this.roomProtocolService
          .open({
            divCd: 'forwardOpen',
            userSeqs: action.trgtUserSeqs
          })
          .pipe(
            map((res: OpenResponse) => {
              return openSuccess({ res });
            }),
            map(res => {
              const loginResInfo: LoginResponse = this.sessionStorageService.get<
                LoginResponse
              >(KEY_LOGIN_RES_INFO);

              const environmentsInfo = this.sessionStorageService.get<
                EnvironmentsInfo
              >(KEY_ENVIRONMENTS_INFO);

              if (action.req.eventType === EventType.File) {
                const fileEventJson: FileEventJson = decodeFileEventJson(
                  action.req.sentMessage
                );
                const req: FileTalkShareRequest = {
                  userSeq: loginResInfo.userSeq,
                  deviceType: environmentsInfo.deviceType,
                  token: loginResInfo.tokenString,
                  attachmentsSeq: fileEventJson.attachmentSeq.toString(),
                  roomSeq: action.trgtRoomSeq,
                  synapKey: ''
                };
                this.commonApiService
                  .fileTalkShare(req)
                  .pipe(
                    take(1),
                    map(resFileShare => {
                      if (resFileShare.statusCode === StatusCode.Success) {
                        action = {
                          ...action,
                          req: {
                            ...action.req,
                            sentMessage: resFileShare.returnJson
                          }
                        };

                        this.store.dispatch(
                          send({
                            senderSeq: action.senderSeq,
                            req: {
                              roomSeq: res.res.roomSeq,
                              eventType: action.req.eventType,
                              sentMessage: action.req.sentMessage
                            }
                          })
                        );
                      } else {
                        this.store.dispatch(forwardFailure({ error: res }));
                      }
                    }),
                    catchError(error => of(forwardFailure({ error })))
                  )
                  .subscribe();
              } else if (action.req.eventType === EventType.BundleImage) {
                const fileEventJson: BundleImageEventJson = decodeBundleImageEventJson(
                  action.req.sentMessage
                );
                const req: FileTalkShareMultiRequest = {
                  userSeq: loginResInfo.userSeq,
                  deviceType: environmentsInfo.deviceType,
                  token: loginResInfo.tokenString,
                  attachmentsSeq: fileEventJson.attachmentSeq.toString(),
                  roomSeq: action.trgtRoomSeq
                };

                this.commonApiService
                  .fileTalkShareMulti(req)
                  .pipe(
                    take(1),
                    map(resMultiShare => {
                      if (resMultiShare.statusCode === StatusCode.Success) {
                        action = {
                          ...action,
                          req: {
                            ...action.req,
                            sentMessage: resMultiShare.returnJson
                          }
                        };

                        this.store.dispatch(
                          send({
                            senderSeq: action.senderSeq,
                            req: {
                              roomSeq: res.res.roomSeq,
                              eventType: action.req.eventType,
                              sentMessage: action.req.sentMessage
                            }
                          })
                        );
                      } else {
                        this.store.dispatch(
                          forwardFailure({ error: resMultiShare })
                        );
                      }
                    }),
                    catchError(error => of(forwardFailure({ error })))
                  )
                  .subscribe();
              } else {
                this.store.dispatch(
                  send({
                    senderSeq: action.senderSeq,
                    req: {
                      roomSeq: res.res.roomSeq,
                      eventType: action.req.eventType,
                      sentMessage: action.req.sentMessage
                    }
                  })
                );
              }

              return res;
            }),
            catchError(error => of(openFailure({ error })))
          );
      })
    )
  );

  roomOpenAfterForward$ = createEffect(() =>
    this.actions$.pipe(
      ofType(roomOpenAfterForward),
      concatMap(action => {
        return this.eventProtocolService
          .send({
            roomSeq: action.trgtRoomSeq,
            eventType: action.req.eventType,
            sentMessage: action.req.sentMessage
          })
          .pipe(
            map((res: SendResponse) => {
              this.store.dispatch(
                newInfo({
                  roomSeq: res.roomSeq,
                  info: res.info,
                  SVC_TYPE: res.SVC_TYPE,
                  SSVC_TYPE: res.SSVC_TYPE
                })
              );
              return ChatStore.selectedRoom({ roomSeq: action.trgtRoomSeq });
            }),
            catchError(error => of(sendFailure({ error })))
          );
      })
    )
  );

  sendMass$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(sendMass),
        exhaustMap(action => {
          const loginResInfo: LoginResponse = this.sessionStorageService.get<
            LoginResponse
          >(KEY_LOGIN_RES_INFO);

          const environmentsInfo = this.sessionStorageService.get<
            EnvironmentsInfo
          >(KEY_ENVIRONMENTS_INFO);

          const req: MassTalkSaveRequest = {
            userSeq: loginResInfo.userSeq,
            deviceType: environmentsInfo.deviceType,
            token: loginResInfo.tokenString,
            content: action.req.sentMessage.replace(/"/g, '\\"'),
            roomSeq: action.req.roomSeq
          };

          return this.commonApiService.massTalkSave(req).pipe(
            map(res => {
              if (res.statusCode === StatusCode.Success) {
                this.store.dispatch(
                  send({
                    senderSeq: action.senderSeq,
                    req: {
                      roomSeq: res.roomSeq,
                      eventType: EventType.MassText,
                      sentMessage: res.returnJson
                    }
                  })
                );
              } else {
                this.store.dispatch(sendMassFailure({ error: res }));
              }
            }),
            catchError(error => of(sendMassFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  newInfo$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(newInfo),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.room.roomInfo as RoomInfo)
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.sync.room.entities as Dictionary<RoomInfo>
            )
          )
        ),
        tap(([action, roomInfo, trgtRoomInfos]) => {
          // opened room :: event add
          if (!!roomInfo && roomInfo.roomSeq === action.roomSeq) {
            if (
              action.SVC_TYPE === SVC_TYPE_EVENT &&
              action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI
            ) {
              this.store.dispatch(appendInfoList({ info: action.info }));

              const windowState = this.nativeService.getWindowState();

              if (
                !!windowState &&
                windowState.windowState !== WindowState.Minimized &&
                windowState.windowState !== WindowState.Hidden &&
                windowState.windowFocusState ===
                  ElectronBrowserWindowChannel.Focus
              ) {
                this.store.dispatch(
                  read({
                    roomSeq: action.roomSeq,
                    lastReadSeq: action.info.seq
                  })
                );
              } else {
                // Blur 상태 일 때
              }
            }

            if (
              action.info.type === EventType.File ||
              action.info.type === EventType.BundleImage
            ) {
              // File 정보 수집.
              this.store.dispatch(
                fileInfo({
                  req: {
                    roomSeq: action.roomSeq,
                    // { 파일타입 } cf) I : 이미지 V: 동영상 F: 파일 "" 빈값이면 모든 타입을 내려줌
                    type: FileType.All
                  }
                })
              );
            }
          } else {
            // not opened room :: unread count increased
            if (
              action.SVC_TYPE === SVC_TYPE_EVENT &&
              action.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_RES
            ) {
              /**
               * 다른 디바이스에서 대화를 송신 할경우 RES 가 noti 로 유입될 수 있다.
               * 이때 unread count 를 중가하지 않는다.
               */
            } else {
              if (
                !!trgtRoomInfos &&
                !!trgtRoomInfos[action.roomSeq] &&
                action.info.type !== EventType.Join &&
                action.info.type !== EventType.Exit &&
                action.info.type !== EventType.ForcedExit
              ) {
                const noReadCnt = trgtRoomInfos[action.roomSeq].noReadCnt;
                this.store.dispatch(
                  SyncStore.updateUnreadCount({
                    roomSeq: action.roomSeq,
                    noReadCnt: noReadCnt + 1
                  })
                );
              }
            }
          }

          // 대화 > 리스트 :: finalEventMessage refresh
          this.store.dispatch(ChatStore.newEventMessage(action));
        })
      );
    },
    { dispatch: false }
  );

  cancel$ = createEffect(() =>
    this.actions$.pipe(
      ofType(cancel),
      exhaustMap(req =>
        this.eventProtocolService.cancel(req).pipe(
          map((res: CancelResponse) => {
            return cancelNotification({ noti: res });
          }),
          catchError(error => of(cancelFailure({ error })))
        )
      )
    )
  );

  cancelNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(cancelNotification),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.room.roomInfo as RoomInfo)
          )
        ),
        tap(([action, roomInfo]) => {
          // 현재 방이 오픈되어 있으면 방내용 갱신
          if (!!roomInfo && roomInfo.roomSeq === action.noti.roomSeq) {
            this.store.dispatch(
              recallInfoList({ eventSeq: action.noti.eventSeq })
            );
          }
          // 대화 > 리스트의 항목 갱신
          const loginInfo = this.sessionStorageService.get<LoginInfo>(
            KEY_LOGIN_INFO
          );
          this.store.dispatch(
            SyncStore.refreshRoom({
              roomSeq: action.noti.roomSeq,
              isDetail: false,
              localeCode: loginInfo.localeCode
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  del$ = createEffect(() =>
    this.actions$.pipe(
      ofType(del),
      exhaustMap(req =>
        this.eventProtocolService.del(req).pipe(
          map((res: DelResponse) => {
            return delNotification({ noti: res });
          }),
          catchError(error => of(delFailure({ error })))
        )
      )
    )
  );

  delNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(delNotification),
        map(action => action.noti),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.room.roomInfo as RoomInfo)
          )
        ),
        tap(([noti, roomInfo]) => {
          // 현재 방이 오픈되어 있으면 방내용 갱신
          if (!!roomInfo && roomInfo.roomSeq === noti.roomSeq) {
            this.store.dispatch(delInfoList({ eventSeqs: [noti.eventSeq] }));
          }

          // 대화 > 리스트의 항목 갱신
          const loginInfo = this.sessionStorageService.get<LoginInfo>(
            KEY_LOGIN_INFO
          );
          this.store.dispatch(
            SyncStore.refreshRoom({
              roomSeq: noti.roomSeq,
              isDetail: false,
              localeCode: loginInfo.localeCode
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  constructor(
    private actions$: Actions,
    private store: Store<any>,
    @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
    private commonApiService: CommonApiService,
    private eventProtocolService: EventProtocolService,
    private fileProtocolService: FileProtocolService,
    private roomProtocolService: RoomProtocolService,
    private sessionStorageService: SessionStorageService,
    private translateService: TranslateService,
    private dialogService: DialogService,
    private logger: NGXLogger
  ) {}
}