import { Injectable } from '@angular/core';

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

import { of, Observable, TimeoutError } from 'rxjs';
import {
  catchError,
  exhaustMap,
  map,
  withLatestFrom,
  switchMap,
  tap,
  concatMap,
  timeoutWith,
  take,
  timeout,
  delay
} from 'rxjs/operators';

import { Store, select } from '@ngrx/store';
import { Dictionary } from '@ngrx/entity';
import { NGXLogger } from 'ngx-logger';
import { environment } from '../../../../environments/environment';

import {
  buddy2,
  buddy2Success,
  buddy2Failure,
  group2,
  group2Success,
  group2Failure,
  room,
  roomFailure,
  roomSuccess,
  updateRoomForNewEventMessage,
  refreshRoom,
  refreshRoomFailure,
  refreshRoomSuccess,
  createGroupAndBuddy,
  addBuddy,
  addBuddyFailure,
  delBuddy,
  delBuddyFailure,
  delBuddySuccess,
  updateBuddy,
  updateBuddySuccess,
  updateBuddyFailure,
  createGroup,
  createGroupSuccess,
  createGroupFailure,
  updateGroup,
  updateGroupFailure,
  updateGroupMember,
  delGroup,
  delGroupFailure,
  delGroupSuccess,
  delBuddyAndClear,
  moveGroupMember,
  moveGroupFromFailure,
  moveGroupToFailure,
  clearRoomUsers,
  phoneBookReqSyncDate,
  phoneBookReady,
  phoneBookReadySuccess,
  phoneBookReadyFailure,
  phoneBookReadyOk,
  phoneBookReadyOkSuccess,
  phoneBookReadyOkFailure,
  phoneBookSndNotification,
  phoneBookRcvSuccess,
  phoneBookRcvFailure,
  phoneBookRcv
} from './actions';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';
import {
  SyncProtocolService,
  SSVC_TYPE_SYNC_BUDDY2_DATA,
  BuddyResponse,
  BuddyDetailData,
  SSVC_TYPE_SYNC_GROUP_DATA2,
  GroupDetailData,
  GroupResponse,
  UserInfo,
  SSVC_TYPE_SYNC_BUDDY2_RES,
  SSVC_TYPE_SYNC_GROUP_RES2,
  SSVC_TYPE_SYNC_ROOM_DATA,
  SSVC_TYPE_SYNC_ROOM_USER,
  SSVC_TYPE_SYNC_ROOM_USER2,
  SSVC_TYPE_SYNC_ROOM_RES,
  RoomData,
  RoomUserData,
  RoomUserDetailData,
  RoomResponse,
  PhoneBookReadyResponse,
  PhoneBookReadyOkResponse,
  PhoneBookRcvResponse,
  PhoneBookSndNotification,
  PhoneBookSyncType,
  KEY_PHONE_BOOK,
  SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI,
  PhoneBookInfo
} from '@ucap-webmessenger/protocol-sync';
import {
  RoomInfo,
  UserInfoShort,
  UserInfo as RoomUserInfo,
  RoomProtocolService,
  SSVC_TYPE_ROOM_INFO_ROOM,
  SSVC_TYPE_ROOM_INFO_USER,
  SSVC_TYPE_ROOM_INFO_USER2,
  InfoData,
  UserShortData,
  UserData,
  SSVC_TYPE_ROOM_INFO_RES,
  RoomType
} from '@ucap-webmessenger/protocol-room';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import {
  GroupProtocolService,
  AddResponse as GroupAddResponse,
  UpdateResponse as GroupUpdateResponse,
  DelResponse as GroupDelResponse
} from '@ucap-webmessenger/protocol-group';
import {
  BuddyProtocolService,
  AddResponse as BuddyAddResponse,
  DelResponse as BuddyDelResponse,
  UpdateResponse as BuddyUpdateResponse
} from '@ucap-webmessenger/protocol-buddy';

import * as ChatStore from '@app/store/messenger/chat';
import * as RoomStore from '@app/store/messenger/room';
import {
  AlertDialogComponent,
  AlertDialogResult,
  DialogService,
  AlertDialogData
} from '@ucap-webmessenger/ui';
import { UserInfoSS } from '@ucap-webmessenger/protocol-query';
import { TranslateService } from '@ngx-translate/core';
import { LocalStorageService } from '@ucap-webmessenger/web-storage';
import { ProtocolService } from '@ucap-webmessenger/protocol';

@Injectable()
export class Effects {
  buddy2$ = createEffect(
    () => {
      let buddyList: UserInfo[];

      return this.actions$.pipe(
        ofType(buddy2),
        tap(() => {
          buddyList = [];
        }),
        switchMap(req => {
          return this.syncProtocolService.buddy2(req).pipe(
            map(res => {
              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_SYNC_BUDDY2_DATA:
                  buddyList.push(
                    ...(res as BuddyDetailData).buddyInfos.filter(
                      v => v.isBuddy && v.isActive
                    )
                  );
                  break;
                case SSVC_TYPE_SYNC_BUDDY2_RES:
                  this.store.dispatch(
                    buddy2Success({
                      buddyList,
                      syncDate: (res as BuddyResponse).syncDate
                    })
                  );
                  break;
              }
            }),
            catchError(error => of(buddy2Failure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  group2$ = createEffect(
    () => {
      let groupList: GroupDetailData[];

      return this.actions$.pipe(
        ofType(group2),
        tap(() => {
          groupList = [];
        }),
        concatMap(req => {
          return this.syncProtocolService.group2(req).pipe(
            map(res => {
              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_SYNC_GROUP_DATA2:
                  if ((res as GroupDetailData).isActive) {
                    groupList.push(res as GroupDetailData);
                  }
                  break;
                case SSVC_TYPE_SYNC_GROUP_RES2:
                  this.store.dispatch(
                    group2Success({
                      groupList: [...groupList],
                      syncDate: (res as GroupResponse).syncDate
                    })
                  );
                  break;
              }
            }),
            catchError(error => of(group2Failure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  room$ = createEffect(
    () => {
      let roomList: RoomInfo[];
      let roomUserInfoMap: {
        [param: string]: {
          userInfoShortList: UserInfoShort[];
          userInfoList: RoomUserInfo[];
        };
      };

      return this.actions$.pipe(
        ofType(room),
        tap(() => {
          roomList = [];
          roomUserInfoMap = {};
        }),
        switchMap(req => {
          return this.syncProtocolService.room(req).pipe(
            map(res => {
              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_SYNC_ROOM_DATA:
                  roomList.push(
                    ...(res as RoomData).roomInfos.filter(
                      v => v.isJoinRoom && v.roomType !== RoomType.Bot
                    )
                  );
                  break;
                case SSVC_TYPE_SYNC_ROOM_USER:
                  {
                    const roomUserData = res as RoomUserData;
                    if (!roomUserInfoMap[roomUserData.roomSeq]) {
                      roomUserInfoMap[roomUserData.roomSeq] = {
                        userInfoList: [],
                        userInfoShortList: []
                      };
                    }
                    roomUserInfoMap[
                      roomUserData.roomSeq
                    ].userInfoShortList.push(...roomUserData.userInfos);
                  }
                  break;
                case SSVC_TYPE_SYNC_ROOM_USER2:
                  {
                    const roomUserDetailData = res as RoomUserDetailData;
                    if (!roomUserInfoMap[roomUserDetailData.roomSeq]) {
                      roomUserInfoMap[roomUserDetailData.roomSeq] = {
                        userInfoList: [],
                        userInfoShortList: []
                      };
                    }
                    roomUserInfoMap[
                      roomUserDetailData.roomSeq
                    ].userInfoList.push(...roomUserDetailData.userInfos);
                  }
                  break;
                case SSVC_TYPE_SYNC_ROOM_RES:
                  {
                    const tmpRoomUserInfoMap: {
                      [param: string]: {
                        userInfoShortList: UserInfoShort[];
                        userInfoList: RoomUserInfo[];
                      };
                    } = {};

                    roomList.forEach(roomInfo => {
                      for (const key in roomUserInfoMap) {
                        if (
                          key === roomInfo.roomSeq &&
                          roomUserInfoMap.hasOwnProperty(key)
                        ) {
                          tmpRoomUserInfoMap[key] = roomUserInfoMap[key];
                        }
                      }
                    });

                    this.store.dispatch(
                      roomSuccess({
                        roomList,
                        roomUserInfoMap: tmpRoomUserInfoMap,
                        syncDate: (res as RoomResponse).syncDate
                      })
                    );
                  }
                  break;
              }
            }),
            catchError(error => of(roomFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  newEventMessageForRoomInfoList$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(ChatStore.newEventMessage),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.sync.room.ids as string[])
          ),
          this.store.pipe(
            select((state: any) => state.messenger.sync.room.syncDate as string)
          )
        ),
        tap(([action, roomSeqList, roomSyncDate]) => {
          const index = roomSeqList.findIndex(
            (roomSeq, i) => roomSeq === action.roomSeq
          );

          if (-1 === index) {
            const loginInfo = this.sessionStorageService.get<LoginInfo>(
              KEY_LOGIN_INFO
            );

            this.store.dispatch(
              room({
                syncDate: roomSyncDate,
                localeCode: loginInfo.localeCode
              })
            );
            return;
          }

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

  openRoom$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(ChatStore.openRoom),
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) =>
                state.account.authentication.loginRes as LoginResponse
            )
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.sync.room.entities as Dictionary<RoomInfo>
            )
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.sync.roomUser.entities as Dictionary<
                  RoomUserDetailData
                >
            )
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.sync.roomUserShort.entities as Dictionary<
                  RoomUserData
                >
            )
          )
        ),
        tap(
          async ([action, loginRes, roomInfos, roomUsers, roomUserShorts]) => {
            const userSeqList = [...action.userSeqList];
            let roomSeq = null;

            for (const key in roomUsers) {
              if (key === undefined) {
                continue;
              }

              if (roomUsers.hasOwnProperty(key)) {
                const element = roomUsers[key];
                if (userSeqList.length === element.userInfos.length) {
                  roomSeq = key;
                  if (!!action.isTimeRoom && action.isTimeRoom) {
                    // 타이머 방
                    if (!roomInfos[key].isTimeRoom) {
                      roomSeq = null;
                      continue;
                    }
                  } else {
                    // 일반 방
                    if (roomInfos[key].isTimeRoom) {
                      roomSeq = null;
                      continue;
                    }
                  }
                  // user check
                  for (const roomUserInfo of element.userInfos) {
                    if (-1 === userSeqList.indexOf(roomUserInfo.seq)) {
                      roomSeq = null;
                      break;
                    }
                  }
                }
              }
            }

            for (const key in roomUserShorts) {
              if (key === undefined) {
                continue;
              }

              if (roomUserShorts.hasOwnProperty(key)) {
                const element = roomUserShorts[key];
                if (userSeqList.length === element.userInfos.length) {
                  roomSeq = key;
                  if (!!action.isTimeRoom && action.isTimeRoom) {
                    // 타이머 방
                    if (!roomInfos[key].isTimeRoom) {
                      roomSeq = null;
                      continue;
                    }
                  } else {
                    // 일반 방
                    if (roomInfos[key].isTimeRoom) {
                      roomSeq = null;
                      continue;
                    }
                  }
                  // user check
                  for (const roomUserDetailData of element.userInfos) {
                    if (-1 === userSeqList.indexOf(roomUserDetailData.seq)) {
                      roomSeq = null;
                      break;
                    }
                  }
                }
              }
            }

            // 내 정보를 추가하여 방생성.
            userSeqList.push(loginRes.userSeq);

            this.logger.debug(
              'openRoom',
              'userSeqList',
              userSeqList,
              'roomSeq',
              roomSeq
            );
            if (!!roomSeq) {
              this.store.dispatch(ChatStore.selectedRoom({ roomSeq }));
              return;
            }

            if (
              environment.productConfig.CommonSetting.maxChatRoomUser <
              userSeqList.length
            ) {
              await this.dialogService.open<
                AlertDialogComponent,
                AlertDialogData,
                AlertDialogResult
              >(AlertDialogComponent, {
                data: {
                  title: this.translateService.instant('chat.errors.label'),
                  html: this.translateService.instant(
                    'chat.errors.maxCountOfRoomMemberWith',
                    {
                      maxCount:
                        environment.productConfig.CommonSetting.maxChatRoomUser
                    }
                  )
                }
              });
              return;
            }

            if (!!action.isTimeRoom && action.isTimeRoom) {
              // 타이머 방
              this.store.dispatch(
                RoomStore.openTimer({
                  req: {
                    divCd: 'DivCodeT',
                    roomName: '',
                    isTimerRoom: true,
                    timerRoomInterval:
                      environment.productConfig.CommonSetting
                        .timerRoomDefaultInterval, // 24h default
                    userSeqs: userSeqList
                  }
                })
              );
            } else {
              // 일반 방
              this.store.dispatch(
                RoomStore.open({
                  req: { divCd: 'DivCode', userSeqs: userSeqList }
                })
              );
            }
          }
        )
      ),
    { dispatch: false }
  );

  refreshRoom$ = createEffect(
    () => {
      let roomInfo: RoomInfo;
      let userInfoShortList: UserInfoShort[];
      let userInfoList: RoomUserInfo[];

      return this.actions$.pipe(
        ofType(refreshRoom),
        tap(() => {
          roomInfo = null;
          userInfoShortList = [];
          userInfoList = [];
        }),
        switchMap(req => {
          return this.roomProtocolService.info(req).pipe(
            map(res => {
              switch (res.SSVC_TYPE) {
                case SSVC_TYPE_ROOM_INFO_ROOM:
                  roomInfo = (res as InfoData).roomInfo;
                  break;
                case SSVC_TYPE_ROOM_INFO_USER:
                  userInfoShortList.push(...(res as UserShortData).userInfos);
                  break;
                case SSVC_TYPE_ROOM_INFO_USER2:
                  userInfoList.push(...(res as UserData).userInfos);
                  break;
                case SSVC_TYPE_ROOM_INFO_RES:
                  this.store.dispatch(
                    refreshRoomSuccess({
                      roomInfo,
                      userInfoShortList,
                      userInfoList
                    })
                  );
                  break;
              }
            }),
            catchError(error => of(refreshRoomFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  // 대화상대 초대 성공 후 처리.
  inviteSuccess$ = createEffect(() =>
    this.actions$.pipe(
      ofType(RoomStore.inviteSuccess),
      withLatestFrom(
        this.store.pipe(
          select((state: any) => state.messenger.room.roomInfo as RoomInfo)
        ),
        this.store.pipe(
          select((state: any) => state.messenger.sync.room.syncDate as string)
        )
      ),
      map(([action, roomInfo, roomSyncDate]) => {
        const loginInfo = this.sessionStorageService.get<LoginInfo>(
          KEY_LOGIN_INFO
        );

        this.store.dispatch(
          room({
            syncDate: roomSyncDate,
            localeCode: loginInfo.localeCode
          })
        );

        if (!!roomInfo && roomInfo.roomSeq === action.roomSeq) {
          return ChatStore.selectedRoom({ roomSeq: action.roomSeq });
        }
      })
    )
  );

  // 대화상대 강제퇴장 수행 후 처리.
  // exitForcingNotificationRes$ = createEffect(
  //   () => {
  //     return this.actions$.pipe(
  //       ofType(RoomStore.exitForcingNotificationRes),
  //       withLatestFrom(
  //         this.store.pipe(
  //           select((state: any) => state.messenger.room.roomInfo as RoomInfo)
  //         ),
  //         this.store.pipe(
  //           select((state: any) => state.messenger.sync.room.syncDate as string)
  //         )
  //       ),
  //       tap(([action, roomInfo, roomSyncDate]) => {
  //         if (!!roomInfo && roomInfo.roomSeq === action.noti.roomSeq) {
  //           this.store.dispatch(
  //             ChatStore.selectedRoom({ roomSeq: action.noti.roomSeq })
  //           );
  //         }

  //         const loginInfo = this.sessionStorageService.get<LoginInfo>(
  //           KEY_LOGIN_INFO
  //         );

  //         this.store.dispatch(
  //           room({
  //             syncDate: roomSyncDate,
  //             localeCode: loginInfo.localeCode
  //           })
  //         );
  //       })
  //     );
  //   },
  //   { dispatch: false }
  // );
  exitForcingSuccess$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(RoomStore.exitForcingSuccess),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.room.roomInfo as RoomInfo)
          ),
          this.store.pipe(
            select((state: any) => state.messenger.sync.room.syncDate as string)
          )
        ),
        tap(([action, roomInfo, roomSyncDate]) => {
          // ROOM::열려 있는 대화방에서 강퇴 인원의 isJoinRoom = false 로 변경.
          this.store.dispatch(
            RoomStore.exitNotificationOthers({
              roomSeq: action.res.roomSeq,
              trgtUser: action.res.userSeqs
            })
          );

          // SYNC::대화리스트의 대화자정보에서 강퇴 인원의 isJoinRoom = false 로 변경.
          this.store.dispatch(
            clearRoomUsers({
              roomSeq: action.res.roomSeq,
              userSeqs: action.res.userSeqs
            })
          );
          // if (!!roomInfo && roomInfo.roomSeq === action.res.roomSeq) {
          //   this.store.dispatch(
          //     ChatStore.selectedRoom({ roomSeq: action.res.roomSeq })
          //   );
          // }

          // const loginInfo = this.sessionStorageService.get<LoginInfo>(
          //   KEY_LOGIN_INFO
          // );

          // this.store.dispatch(
          //   room({
          //     syncDate: roomSyncDate,
          //     localeCode: loginInfo.localeCode
          //   })
          // );
        })
      );
    },
    { dispatch: false }
  );
  // 대화방에서 강제퇴장 받은 후 처리.
  exitForcingNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(RoomStore.exitForcingNotification),
        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(ChatStore.clearSelectedRoom());
          }

          // sync.roomList 에서 방 삭제.
          this.store.dispatch(
            RoomStore.exitSuccess({
              res: {
                roomSeq: action.noti.roomSeq
              }
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  createGroup$ = createEffect(() =>
    this.actions$.pipe(
      ofType(createGroup),
      map(action => action.groupName),
      exhaustMap(req => {
        return this.groupProtocolService
          .add({
            groupName: req
          })
          .pipe(
            map((res: GroupAddResponse) => {
              return createGroupSuccess(res);
            }),
            catchError(error => of(createGroupFailure({ error })))
          );
      })
    )
  );

  createGroupAndBuddy$ = createEffect(() =>
    this.actions$.pipe(
      ofType(createGroupAndBuddy),
      withLatestFrom(
        this.store.pipe(
          select(
            (state: any) =>
              state.messenger.sync.buddy2.entities as Dictionary<UserInfo>
          )
        )
      ),
      exhaustMap(([action, buddyList]) => {
        return this.groupProtocolService
          .add({
            groupName: action.groupName
          })
          .pipe(
            map((res: GroupAddResponse) => {
              return createGroupSuccess(res);
            }),
            tap(res => {
              if (!!action.trgtUserSeq && action.trgtUserSeq.length > 0) {
                // 그룹원으로 추가할 대상이 유입.

                // STEP 1 : 그룹원으로 등록될 대상중 Buddy 등록해야 하는 인원 수집.
                const addBuddyList: number[] = [];
                action.trgtUserSeq.forEach(item => {
                  if (!buddyList[item]) {
                    addBuddyList.push(item);
                  }
                });

                if (addBuddyList.length > 0) {
                  this.store.dispatch(addBuddy({ userSeqs: addBuddyList }));
                }

                this.store.dispatch(
                  updateGroup({
                    // 0: 동료그룹SEQ(n)
                    groupSeq: res.groupSeq,
                    groupName: res.groupName,
                    userSeqs: action.trgtUserSeq
                  })
                );
              }
            }),
            catchError(error => of(createGroupFailure({ error })))
          );
      })
    )
  );

  updateGroupMember$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(updateGroupMember),
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.sync.buddy2.entities as Dictionary<UserInfo>
            )
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.sync.group2.entities as Dictionary<
                  GroupDetailData
                >
            )
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.query.myDepartmentUserInfoList as UserInfoSS[]
            )
          ) // 내 부서원 비교.
        ),
        tap(([action, buddyList, groupList, myDeptUserList]) => {
          // Add Buddy
          const addBuddyList: number[] = [];
          action.trgtUserSeq.forEach(item => {
            if (!buddyList[item]) {
              addBuddyList.push(item);
            }
          });

          // Del Buddy
          let delBuddyInGroup: number[] = action.oldGroup.userSeqs.filter(
            v => action.trgtUserSeq.indexOf(v) < 0
          );

          // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
          if (environment.productConfig.CommonSetting.useMyDeptGroup) {
            delBuddyInGroup = delBuddyInGroup.filter(
              delbuddy =>
                myDeptUserList.filter(deptUser => deptUser.seq === delbuddy)
                  .length === 0
            );
          }
          delBuddyInGroup = delBuddyInGroup.filter(delbuddy => {
            let exist = false;
            // tslint:disable-next-line: forin
            for (const key in groupList) {
              const group: GroupDetailData = groupList[key];
              if (
                group.seq !== action.oldGroup.seq &&
                group.userSeqs.filter(v => v === delbuddy).length > 0
              ) {
                exist = true;
                break;
              }
            }
            return !exist;
          });

          if (addBuddyList.length > 0) {
            this.store.dispatch(addBuddy({ userSeqs: addBuddyList }));
          }

          if (delBuddyInGroup.length > 0) {
            // 즐겨찾기 해제.
            delBuddyInGroup.forEach(buddySeq => {
              this.buddyProtocolService
                .update({
                  seq: buddySeq,
                  isFavorit: false
                })
                .pipe(catchError(error => of(delBuddyFailure({ error }))));
            });

            // 동료 삭제
            this.store.dispatch(delBuddy({ userSeqs: delBuddyInGroup }));
          }

          this.logger.debug('group member update', action.trgtUserSeq);
          this.store.dispatch(
            updateGroup({
              groupSeq: action.oldGroup.seq,
              groupName: action.oldGroup.name,
              userSeqs: action.trgtUserSeq
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  moveGroupMember$ = createEffect(() =>
    this.actions$.pipe(
      ofType(moveGroupMember),
      withLatestFrom(
        this.store.pipe(
          select(
            (state: any) =>
              state.messenger.sync.group2.entities as Dictionary<
                GroupDetailData
              >
          )
        ),
        this.store.pipe(
          select((state: any) => state.messenger.sync.group2.syncDate as string)
        )
      ),
      exhaustMap(([action, groupList, syncDate]) => {
        // copy to
        let toTrgtUserSeqs = groupList[action.toGroup.seq].userSeqs;
        action.trgtUserSeq.forEach(trgtSeq => {
          if (toTrgtUserSeqs.indexOf(trgtSeq) > -1) {
            // ignore
          } else {
            toTrgtUserSeqs = toTrgtUserSeqs.concat(trgtSeq);
          }
        });

        return this.groupProtocolService
          .update2({
            groupSeq: action.toGroup.seq,
            groupName: action.toGroup.name,
            userSeqs: toTrgtUserSeqs
          })
          .pipe(
            exhaustMap((resTo: GroupUpdateResponse) => {
              // del from
              const fromTrgtUserSeqs = groupList[action.fromGroup.seq].userSeqs;

              return this.groupProtocolService
                .update2({
                  groupSeq: action.fromGroup.seq,
                  groupName: action.fromGroup.name,
                  userSeqs: fromTrgtUserSeqs.filter(
                    trgtSeq => action.trgtUserSeq.indexOf(trgtSeq) < 0
                  )
                })
                .pipe(
                  map((resFrom: GroupUpdateResponse) => {
                    return group2({
                      syncDate
                    });
                  }),
                  catchError(error => of(moveGroupFromFailure({ error })))
                );
            }),
            catchError(error => of(moveGroupToFailure({ error })))
          );
      })
    )
  );

  delGroup$ = createEffect(() =>
    this.actions$.pipe(
      ofType(delGroup),
      withLatestFrom(
        this.store.pipe(
          select(
            (state: any) =>
              state.messenger.sync.group2.entities as Dictionary<
                GroupDetailData
              >
          )
        ),
        this.store.pipe(
          select(
            (state: any) =>
              state.messenger.query.myDepartmentUserInfoList as UserInfoSS[]
          )
        ) // 내 부서원 비교.
      ),
      exhaustMap(([action, groupList, myDeptUserList]) => {
        // Del Buddy
        const trgtBuddys = action.group.userSeqs;

        let delBuddyList: number[] = [];
        // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
        if (environment.productConfig.CommonSetting.useMyDeptGroup) {
          delBuddyList = trgtBuddys.filter(
            delbuddy =>
              myDeptUserList.filter(deptUser => deptUser.seq === delbuddy)
                .length === 0
          );
        }

        delBuddyList = trgtBuddys.filter(delbuddy => {
          let exist = false;
          // tslint:disable-next-line: forin
          for (const key in groupList) {
            const group: GroupDetailData = groupList[key];
            if (
              group.seq !== action.group.seq &&
              group.userSeqs.filter(v => v === delbuddy).length > 0
            ) {
              exist = true;
              break;
            }
          }
          return !exist;
        });

        if (delBuddyList.length > 0) {
          this.logger.debug('Del Buddy', delBuddyList);
          // 즐겨찾기 해제.
          delBuddyList.forEach(buddySeq => {
            this.buddyProtocolService
              .update({
                seq: buddySeq,
                isFavorit: false
              })
              .pipe(catchError(error => of(delBuddyFailure({ error }))));
          });

          // 동료 삭제
          this.store.dispatch(delBuddy({ userSeqs: delBuddyList }));
        }

        return this.groupProtocolService
          .del({
            groupSeq: action.group.seq
          })
          .pipe(
            map((res: GroupDelResponse) => {
              // this.store.dispatch(delGroupSuccess(res));
              return delGroupSuccess(res);
            }),
            catchError(error => of(delGroupFailure({ error })))
          );
      })
    )
  );

  addBuddy$ = createEffect(() =>
    this.actions$.pipe(
      ofType(addBuddy),
      withLatestFrom(
        this.store.pipe(
          select((state: any) => state.messenger.sync.buddy2.syncDate as string)
        )
      ),
      exhaustMap(([req, syncDate]) =>
        this.buddyProtocolService.add(req).pipe(
          map((res: BuddyAddResponse) => {
            return buddy2({
              syncDate
            });
          }),
          catchError(error => of(addBuddyFailure({ error })))
        )
      )
    )
  );

  delBuddy$ = createEffect(() =>
    this.actions$.pipe(
      ofType(delBuddy),
      withLatestFrom(
        this.store.pipe(
          select((state: any) => state.messenger.sync.buddy2.syncDate as string)
        )
      ),
      exhaustMap(([req, syncDate]) =>
        this.buddyProtocolService.del(req).pipe(
          map((res: BuddyDelResponse) => {
            return delBuddySuccess(res);
          }),
          catchError(error => of(delBuddyFailure({ error })))
        )
      )
    )
  );

  delBuddyAndClear$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(delBuddyAndClear),
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.sync.group2.entities as Dictionary<
                  GroupDetailData
                >
            )
          ),
          this.store.pipe(
            select(
              (state: any) =>
                state.messenger.query.myDepartmentUserInfoList as UserInfoSS[]
            )
          ) // 내 부서원 비교.
        ),
        tap(([req, groupList, myDeptUserList]) => {
          // tslint:disable-next-line: forin
          for (const key in groupList) {
            const group: GroupDetailData = groupList[key];
            if (group.userSeqs.indexOf(req.seq) > -1) {
              // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
              if (
                environment.productConfig.CommonSetting.useMyDeptGroup &&
                environment.productConfig.CommonSetting.fixedGroupSeqs.filter(
                  fixedGroupSeq => fixedGroupSeq === group.seq
                ).length > 0
              ) {
                // skip;;
              } else {
                // Group Clear..
                this.store.dispatch(
                  updateGroup({
                    groupSeq: group.seq,
                    groupName: group.name,
                    userSeqs: group.userSeqs.filter(
                      userSeq => userSeq !== req.seq
                    )
                  })
                );
              }
            }
          }

          // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
          if (
            environment.productConfig.CommonSetting.useMyDeptGroup &&
            myDeptUserList.filter(deptUser => deptUser.seq === req.seq).length >
              0
          ) {
            // skip;;
          } else {
            // Favorit Clear..
            this.store.dispatch(
              updateBuddy({
                seq: req.seq,
                isFavorit: false
              })
            );

            // Buddy Clear..
            this.store.dispatch(delBuddy({ userSeqs: [req.seq] }));
          }
        })
      );
    },
    { dispatch: false }
  );

  updateBuddy$ = createEffect(() =>
    this.actions$.pipe(
      ofType(updateBuddy),
      exhaustMap(req =>
        this.buddyProtocolService.update(req).pipe(
          map((res: BuddyUpdateResponse) => {
            return updateBuddySuccess(res);
          }),
          catchError(error => of(updateBuddyFailure({ error })))
        )
      )
    )
  );

  updateGroup$ = createEffect(() =>
    this.actions$.pipe(
      ofType(updateGroup),
      withLatestFrom(
        this.store.pipe(
          select((state: any) => state.messenger.sync.group2.syncDate as string)
        )
      ),
      concatMap(([req, syncDate]) =>
        this.groupProtocolService.update2(req).pipe(
          map((res: GroupUpdateResponse) => {
            return group2({
              syncDate
            });
          }),
          catchError(error => of(updateGroupFailure({ error })))
        )
      )
    )
  );

  phoneBookReady$ = createEffect(() =>
    this.actions$.pipe(
      ofType(phoneBookReady),
      concatMap(action =>
        this.syncProtocolService.phoneBookReady(action.req).pipe(
          map((res: PhoneBookReadyResponse) => {
            return phoneBookReadySuccess({ res });
            // return phoneBookReqSyncDate({
            //   syncStartDate: new Date().toString()
            // });
          }),
          catchError(error => of(phoneBookReadyFailure({ error })))
        )
      )
    )
  );

  phoneBookReadySuccess$ = createEffect(
    () =>
      this.actions$.pipe(
        ofType(phoneBookReadySuccess),
        tap(() => {
          this.syncProtocolService.notification$
            .pipe(
              take(1),
              timeout(10000),
              tap(noti => {
                switch (noti.SSVC_TYPE) {
                  case SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI:
                    {
                      console.log(
                        'SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI',
                        noti as PhoneBookSndNotification
                      );
                    }
                    break;
                  default:
                    break;
                }
              })
            )
            .subscribe(
              x => this.logger.debug(x),
              err => {
                if (err instanceof TimeoutError) {
                  this.logger.debug('TimeoutError');
                  return;
                }
                this.logger.debug(err);
              }
            );
        })
      ),
    { dispatch: false }
  );

  
  // phoneBookReadySuccess$ = createEffect(
  //   () =>
  //     this.actions$.pipe(
  //       ofType(phoneBookReadySuccess),
  //       tap(() => {
  //         this.syncProtocolService.notification$
  //           .pipe(
  //             take(1),
  //             timeout(10000),
  //             tap(noti => {
  //               switch (noti.SSVC_TYPE) {
  //                 case SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI:
  //                   {
  //                     console.log(
  //                       'SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI',
  //                       noti as PhoneBookSndNotification
  //                     );
  //                   }
  //                   break;
  //                 default:
  //                   break;
  //               }
  //             })
  //           )
  //           .subscribe(
  //             x => this.logger.debug(x),
  //             err => {
  //               if (err instanceof TimeoutError) {
  //                 this.logger.debug('TimeoutError');
  //                 return;
  //               }
  //               this.logger.debug(err);
  //             }
  //           );
  //       })
  //     ),
  //   { dispatch: false }
  // );

  phoneBookReadyOk$ = createEffect(() =>
    this.actions$.pipe(
      ofType(phoneBookReadyOk),
      map(action => action.req),
      exhaustMap(req => {
        return this.syncProtocolService.phoneBookReadyOk(req).pipe(
          map((res: PhoneBookReadyOkResponse) => {
            return phoneBookReadyOkSuccess({ res });
          }),
          catchError(error => of(phoneBookReadyOkFailure({ error })))
        );
      })
    )
  );

  phoneBookSndNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(phoneBookSndNotification),
        // map(action => action.noti),
        map(action => {
          const noti = action.noti as PhoneBookSndNotification;
          const prePhoneBook = this.localStorageService.encGet<
            PhoneBookSndNotification
          >(KEY_PHONE_BOOK, environment.customConfig.appKey);
          let data: PhoneBookSndNotification;

          if (!prePhoneBook) {
            data = noti;
          } else if (noti.pageTotalCount > prePhoneBook.phoneBookInfos.length) {
            prePhoneBook.phoneBookInfos.push(
              ...(noti as PhoneBookSndNotification).phoneBookInfos
            );

            data = prePhoneBook;
          }

          this.localStorageService.encSet<PhoneBookSndNotification>(
            KEY_PHONE_BOOK,
            data,
            environment.customConfig.appKey
          );

          this.store.dispatch(
            phoneBookRcv({
              req: {
                syncSuccessType: PhoneBookSyncType.OK_Y,
                destination: PhoneBookSyncType.TO_MOBILE,
                syncType: PhoneBookSyncType.SYNC_TYPE_WHOLE,
                pageTotalCount: noti.pageTotalCount,
                pageCurrent: noti.pageCurrent,
                pageListCount: noti.pageListCount
              }
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  phoneBookRcv$ = createEffect(() =>
    this.actions$.pipe(
      ofType(phoneBookRcv),
      concatMap(action =>
        this.syncProtocolService.phoneBookRcv(action.req).pipe(
          map((res: PhoneBookRcvResponse) => {
            return phoneBookRcvSuccess(res);
          }),
          catchError(error => of(phoneBookRcvFailure(error)))
        )
      )
    )
  );

  constructor(
    private actions$: Actions,
    private store: Store<any>,
    private syncProtocolService: SyncProtocolService,
    private roomProtocolService: RoomProtocolService,
    private groupProtocolService: GroupProtocolService,
    private buddyProtocolService: BuddyProtocolService,
    private sessionStorageService: SessionStorageService,
    private translateService: TranslateService,
    private dialogService: DialogService,
    private localStorageService: LocalStorageService,
    private logger: NGXLogger
  ) {}
}