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

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

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

import {
  OpenResponse as CreateResponse,
  Open3Response as CreateTimerResponse,
  ExitResponse as DeleteResponse,
  ExitAllResponse as DeleteMultiResponse,
  UpdateResponse,
  InviteResponse,
  ExitForcingResponse,
  UpdateTimerSetResponse,
  InfoRequest
} from '@ucap/protocol-room';

import { RoomProtocolService } from '@ucap/ng-protocol-room';
import { SyncProtocolService } from '@ucap/ng-protocol-sync';

import { LoginActions, LoginSelector } from '@ucap/ng-store-authentication';
import * as ChattingAction from '../Chatting/actions';
import { RoomSelector, ChattingSelector } from '../state';

import {
  rooms,
  room,
  roomFailure,
  inviteNotification,
  exitNotification,
  excludeUser,
  excludeUserSuccess,
  close,
  delSuccess,
  create,
  createSuccess,
  createFailure,
  createTimer,
  createTimerSuccess,
  createTimerFailure,
  del,
  delFailure,
  update,
  updateSuccess,
  updateFailure,
  open,
  openSuccess,
  closeSuccess,
  invite,
  inviteSuccess,
  inviteFailure,
  expel,
  expelSuccess,
  expelFailure,
  updateTimeRoomInterval,
  updateTimeRoomIntervalSuccess,
  updateTimeRoomIntervalFailure,
  rooms2Success,
  rooms2Failure,
  delMulti,
  delMultiSuccess,
  delMultiFailure,
  selectedRoom,
  room2Success,
  selectedRoomSuccess,
  clearSelectedRoom
} from './actions';
import { LocaleCode } from '@ucap/core';
import { PresenceActions } from '@ucap/ng-store-organization';
import { I18nService } from '@ucap/ng-i18n';

@Injectable()
export class Effects {
  sessionCreatedForRooms$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(LoginActions.sessionCreated),
      map((action) => rooms({ localeCode: action.loginSession.localeCode }))
    );
  });

  selectedRoom$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(selectedRoom),
        debounceTime(300),
        tap((action) => {
          const req: InfoRequest = {
            ...action,
            isDetail: false
          };

          // retrieve room info
          this.roomProtocolService
            .info2(req)
            .pipe(
              map((res) => {
                let isJoinRoom = true;
                if (!res.roomInfo || !res.roomInfo.isJoinRoom) {
                  isJoinRoom = false;
                }

                if (!!isJoinRoom) {
                  this.store.dispatch(
                    selectedRoomSuccess({
                      roomId: req.roomId,
                      roomInfo2Res: res
                    })
                  );

                  // Buddy Presence
                  const targetUserInfos = req.isDetail
                    ? res.roomUserInfo.userInfoList.map(
                        (userInfo) => userInfo.seq + ''
                      )
                    : res.roomUserInfo.userInfoShortList.map(
                        (userInfo) => userInfo.seq + ''
                      );
                  if (!!targetUserInfos && targetUserInfos.length > 0) {
                    this.store.dispatch(
                      PresenceActions.bulkInfo({
                        divCd: 'roomBulk',
                        userSeqs: targetUserInfos
                      })
                    );
                  }
                } else {
                  // is not join room. so, redirect chat main.
                  this.store.dispatch(
                    clearSelectedRoom({ roomId: req.roomId })
                  );
                }
              }),
              catchError((error) => of(roomFailure({ error })))
            )
            .subscribe();

          // retrieve event info >> chatting.effect.selectedRoom$
        })
      );
    },
    { dispatch: false }
  );

  selectedRoomSuccess$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(selectedRoomSuccess),
        tap((action) => {
          // room info success reduce.
          this.store.dispatch(
            room2Success({
              roomInfo: action.roomInfo2Res.roomInfo,
              roomUserInfo: action.roomInfo2Res.roomUserInfo
            })
          );
        })
      );
    },
    { dispatch: false }
  );

  rooms$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(rooms),
      withLatestFrom(this.store.pipe(select(RoomSelector.roomsSyncDate))),
      switchMap(([action, syncDate]) => {
        // // CASE :: RoomUser Data 중 Detail data 만 수집.
        // return this.syncProtocolService
        //   .room({ syncDate, localeCode: action.localeCode })
        //   .pipe(
        //     map((res) => {
        //       return roomsSuccess({
        //         roomList: res.roomList,
        //         roomUserInfoMap: res.roomUserInfoMap,
        //         syncDate: res.res.syncDate
        //       });
        //     }),
        //     catchError((error) => of(roomsFailure({ error })))
        //   );

        // CASE :: RoomUser Data 중 Detail data, Short data 수집.
        return this.syncProtocolService
          .room2({ syncDate, localeCode: action.localeCode })
          .pipe(
            map((res) => {
              return rooms2Success({
                roomList: res.roomList,
                roomUserInfoMap: res.roomUserInfoMap,
                syncDate: res.res.syncDate
              });
            }),
            catchError((error) => of(rooms2Failure({ error })))
          );
      })
    );
  });

  room$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(room),
      switchMap((action) => {
        const req = action.req;
        // // CASE :: RoomUser Data 중 Detail data 만 수집.
        // return this.roomProtocolService.info(req).pipe(
        //   map((res) =>
        //     roomSuccess({
        //       roomInfo: res.roomInfo,
        //       userInfoList: res.userInfoList
        //     })
        //   ),
        //   catchError((error) => of(roomFailure({ error })))
        // );

        // CASE :: RoomUser Data 중 Detail data, Short data 수집.
        return this.roomProtocolService.info2(req).pipe(
          map((res) =>
            room2Success({
              roomInfo: res.roomInfo,
              roomUserInfo: res.roomUserInfo
            })
          ),
          catchError((error) => of(roomFailure({ error })))
        );
      })
    );
  });

  create$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(create),
        map((action) => action.req),
        exhaustMap((req) => {
          return this.roomProtocolService.open(req).pipe(
            map((res: CreateResponse) => {
              this.store.dispatch(createSuccess({ res }));

              // this.router.navigate(
              //   [
              //     'chat',
              //     {
              //       outlets: { content: 'chatroom' }
              //     }
              //   ],
              //   {
              //     queryParams: { roomId: res.roomId }
              //   }
              // );

              // if (!res.newRoom) {
              // } else {
              // }
            }),
            catchError((error) => of(createFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  createTimer$ = createEffect(() =>
    this.actions$.pipe(
      ofType(createTimer),
      map((action) => action.req),
      exhaustMap((req) => {
        return this.roomProtocolService.open3(req).pipe(
          map((res: CreateTimerResponse) => {
            return createTimerSuccess({ res });
          }),
          catchError((error) => of(createTimerFailure({ error })))
        );
      })
    )
  );

  del$ = createEffect(() =>
    this.actions$.pipe(
      ofType(del),
      map((action) => action.req),
      exhaustMap((req) => {
        return this.roomProtocolService.exit(req).pipe(
          switchMap((res: DeleteResponse) => [
            // clear activeRoomId
            clearSelectedRoom({ roomId: res.roomId }),
            // close room, clear chatting
            close({ roomIds: [res.roomId] }),
            // clear room in rooms.
            delSuccess({ res })
          ]),
          catchError((error) => of(delFailure({ error })))
        );
      })
    )
  );

  delMulti$ = createEffect(() =>
    this.actions$.pipe(
      ofType(delMulti),
      map((action) => action.req),
      withLatestFrom(this.store.pipe(select(ChattingSelector.activeRoomId))),
      exhaustMap(([req, activeRoomId]) => {
        const existActiveRoomId = req.roomIds.find(
          (roomId) => roomId === activeRoomId
        );

        return this.roomProtocolService.exitAll(req).pipe(
          switchMap((res: DeleteMultiResponse) => {
            if (!!existActiveRoomId) {
              return [
                // clear selected room
                clearSelectedRoom({ roomId: existActiveRoomId }),
                // close room, clear chatting
                close({ roomIds: res.roomIds }),
                // clear room in rooms.
                delMultiSuccess({ res })
              ];
            } else {
              return [
                // close room, clear chatting
                close({ roomIds: res.roomIds }),
                // clear room in rooms.
                delMultiSuccess({ res })
              ];
            }
          }),
          catchError((error) => of(delMultiFailure({ error })))
        );
      })
    )
  );

  update$ = createEffect(() =>
    this.actions$.pipe(
      ofType(update),
      map((action) => action.req),
      exhaustMap((req) => {
        return this.roomProtocolService.update(req).pipe(
          map((res: UpdateResponse) => {
            return updateSuccess({ res });
          }),
          catchError((error) => of(updateFailure({ error })))
        );
      })
    )
  );

  excludeUser$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(excludeUser),
      map((action) =>
        excludeUserSuccess({
          roomId: action.roomId,
          userSeqs: action.userSeqs
        })
      )
    );
  });

  open$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(open),
      map((action) => openSuccess({ roomIds: [...action.roomIds] }))
    );
  });

  close$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(close),
      map((action) => closeSuccess({ roomIds: [...action.roomIds] }))
    );
  });

  invite$ = createEffect(() =>
    this.actions$.pipe(
      ofType(invite),
      exhaustMap((action) => {
        const req = action.req;
        const localeCode = action.localeCode;

        return this.roomProtocolService.invite(req).pipe(
          switchMap((res: InviteResponse) => {
            return [
              inviteSuccess({ res }),
              room({
                req: {
                  roomId: req.roomId,
                  isDetail: true,
                  localeCode
                }
              })
            ];
          }),
          catchError((error) => of(inviteFailure({ error })))
        );
      })
    )
  );

  expel$ = createEffect(() =>
    this.actions$.pipe(
      ofType(expel),
      map((action) => action.req),
      exhaustMap((req) => {
        return this.roomProtocolService.exitForcing(req).pipe(
          map((res: ExitForcingResponse) => {
            return expelSuccess({ res });
          }),
          catchError((error) => of(expelFailure({ error })))
        );
      })
    )
  );

  updateTimeRoomInterval$ = createEffect(() =>
    this.actions$.pipe(
      ofType(updateTimeRoomInterval),
      map((action) => action.req),
      exhaustMap((req) => {
        return this.roomProtocolService.updateTimerSet(req).pipe(
          map((res: UpdateTimerSetResponse) => {
            return updateTimeRoomIntervalSuccess({ res });
          }),
          catchError((error) => of(updateTimeRoomIntervalFailure({ error })))
        );
      })
    )
  );

  /**
   * @discription Call by notifications case in SSVC_TYPE_ROOM_INVITE_RES, SSVC_TYPE_ROOM_INVITE_NOTI
   * 1. roomlist 를 체크하여 없을경우 내가 초대된 경우라 간주하고 방 조회하여 갱신하지 않도록 한다.(첫 대화가 들어오면 그때 조회.)
   * 2. roomlist 를 체크하여 있을 경우 기존방에 다른 인원이 추가되었을 경우이므로 방 조회하여 갱신한다.
   */
  inviteNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(inviteNotification),
        withLatestFrom(this.store.pipe(select(RoomSelector.rooms))),
        map(([action, roomList]) => {
          const roomId = action.noti.roomId;
          if (!!roomList && roomList.length > 0) {
            if (roomList.some((roomInfo) => roomId === roomInfo.roomId)) {
              this.store.dispatch(
                room({
                  req: {
                    roomId,
                    isDetail: true,
                    localeCode: action.localeCode
                  }
                })
              );
            }
          }
        })
      );
    },
    {
      dispatch: false
    }
  );

  exitNotification$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(exitNotification),
      withLatestFrom(this.store.pipe(select(LoginSelector.loginRes))),
      switchMap(([action, loginRes]) => {
        if (loginRes.userSeq + '' === action.senderSeq + '') {
          return [
            close({ roomIds: [action.roomId] }),
            clearSelectedRoom({ roomId: action.roomId }),
            delSuccess({
              res: { roomId: action.roomId }
            })
          ];
        } else {
          return [
            excludeUser({
              roomId: action.roomId,
              userSeqs: [action.senderSeq]
            })
          ];
        }
      })
    );
  });

  /*******************************************************************
   * [Chatting Action watching.]
   *******************************************************************/
  addEventSuccess$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(ChattingAction.addEventSuccess),
        withLatestFrom(this.store.pipe(select(RoomSelector.rooms))),
        map(([action, roomList]) => {
          const roomId = action.roomId;

          if (!roomList.find((roomInfo) => roomInfo.roomId === roomId)) {
            this.store.dispatch(
              rooms({
                localeCode: this.i18nService.currentLng.toUpperCase() as LocaleCode
              })
            );
          }
        })
      );
    },
    { dispatch: false }
  );

  constructor(
    private actions$: Actions,
    private store: Store<any>,
    private syncProtocolService: SyncProtocolService,
    private roomProtocolService: RoomProtocolService,
    private i18nService: I18nService
  ) {}
}