import { Injectable } 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
} from 'rxjs/operators';
import {
  RoomInfo,
  UserInfoShort,
  UserInfo,
  RoomProtocolService,
  SSVC_TYPE_ROOM_INFO_ROOM,
  InfoData,
  SSVC_TYPE_ROOM_INFO_USER,
  SSVC_TYPE_ROOM_INFO_USER2,
  SSVC_TYPE_ROOM_INFO_RES,
  UserShortData,
  UserData,
  UpdateResponse,
  OpenResponse,
  ExitResponse,
  Open3Response,
  RoomType,
  InviteResponse,
  UpdateTimerSetResponse,
  ExitForcingResponse
} from '@ucap-webmessenger/protocol-room';

import * as ChatStore from '@app/store/messenger/chat';

import {
  info,
  infoSuccess,
  infoFailure,
  inviteNotification,
  exitNotification,
  updateFontNotification,
  updateOnlyAlarm,
  update,
  updateSuccess,
  updateFailure,
  open,
  openSuccess,
  openFailure,
  exit,
  exitSuccess,
  exitFailure,
  openTimer,
  openTimerSuccess,
  openTimerFailure,
  inviteOrOpen,
  inviteSuccess,
  inviteFailure,
  updateTimeRoomInterval,
  updateTimeRoomIntervalSuccess,
  updateTimeRoomIntervalFailure,
  exitForcing,
  exitForcingFailure,
  exitForcingSuccess,
  exitNotificationOthers,
  clearRoomUser
} from './actions';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { LoginInfo, KEY_LOGIN_INFO } from '@app/types';

@Injectable()
export class Effects {
  selectedRoomForInfo$ = createEffect(() =>
    this.actions$.pipe(
      ofType(ChatStore.selectedRoom),
      map(action => {
        const loginInfo = this.sessionStorageService.get<LoginInfo>(
          KEY_LOGIN_INFO
        );
        return info({
          roomSeq: action.roomSeq,
          isDetail: true,
          localeCode: loginInfo.localeCode
        });
      })
    )
  );

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

      return this.actions$.pipe(
        ofType(info),
        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(
                    infoSuccess({
                      roomInfo,
                      userInfoShortList,
                      userInfoList
                    })
                  );
                  break;
              }
            }),
            catchError(error => of(infoFailure({ error })))
          );
        })
      );
    },
    { dispatch: false }
  );

  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 })))
        );
      })
    )
  );

  updateOnlyAlarm$ = createEffect(() =>
    this.actions$.pipe(
      ofType(updateOnlyAlarm),
      map(action => action.roomInfo),
      map(roomInfo =>
        update({
          req: {
            roomSeq: roomInfo.roomSeq,
            roomName:
              roomInfo.roomName.trim().length === 0
                ? ' '
                : roomInfo.roomName.trim(),
            receiveAlarm: !roomInfo.receiveAlarm,
            syncAll: false
          }
        })
      )
    )
  );

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

  open$ = createEffect(() =>
    this.actions$.pipe(
      ofType(open),
      map(action => action.req),
      exhaustMap(req => {
        return this.roomProtocolService.open(req).pipe(
          map((res: OpenResponse) => {
            return openSuccess({ res });
          }),
          catchError(error => of(openFailure({ error })))
        );
      })
    )
  );
  openSuccess$ = createEffect(() =>
    this.actions$.pipe(
      ofType(openSuccess),
      map(action => {
        return ChatStore.selectedRoom({ roomSeq: action.res.roomSeq });
      })
    )
  );

  openTimer$ = createEffect(() =>
    this.actions$.pipe(
      ofType(openTimer),
      map(action => action.req),
      exhaustMap(req => {
        return this.roomProtocolService.open3(req).pipe(
          map((res: Open3Response) => {
            return openTimerSuccess({ res });
          }),
          catchError(error => of(openTimerFailure({ error })))
        );
      })
    )
  );
  openTimerSuccess$ = createEffect(() =>
    this.actions$.pipe(
      ofType(openTimerSuccess),
      map(action => {
        return ChatStore.selectedRoom({ roomSeq: action.res.roomSeq });
      })
    )
  );

  inviteOrOpen$ = createEffect(() =>
    this.actions$.pipe(
      ofType(inviteOrOpen),
      withLatestFrom(
        this.store.pipe(
          select((state: any) => state.messenger.room.roomInfo as RoomInfo)
        )
      ),
      exhaustMap(([action, roomInfo]) => {
        if (roomInfo.roomType === RoomType.Single) {
          // Re Open
          return this.roomProtocolService.open(action.req).pipe(
            map((res: OpenResponse) => {
              return openSuccess({ res });
            }),
            catchError(error => of(openFailure({ error })))
          );
        } else if (roomInfo.roomType === RoomType.Multi) {
          // Invite
          return this.roomProtocolService
            .invite({
              roomSeq: roomInfo.roomSeq,
              inviteUserSeqs: action.req.userSeqs
            })
            .pipe(
              map((res: InviteResponse) => {
                return inviteSuccess(res);
              }),
              catchError(error => of(inviteFailure({ error })))
            );
        } else {
          return of(inviteFailure({ error: 'room type is error!!' }));
        }
      })
    )
  );

  exit$ = createEffect(() =>
    this.actions$.pipe(
      ofType(exit),
      withLatestFrom(
        this.store.pipe(
          select((state: any) => state.messenger.room.roomInfo as RoomInfo)
        )
      ),
      exhaustMap(([req, roomInfo]) => {
        return this.roomProtocolService.exit(req).pipe(
          map((res: ExitResponse) => {
            if (!!roomInfo && roomInfo.roomSeq === res.roomSeq) {
              this.store.dispatch(ChatStore.clearSelectedRoom());
            }
            return exitSuccess({ res });
          }),
          catchError(error => of(exitFailure({ error })))
        );
      })
    )
  );

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

  inviteNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(inviteNotification),
        map(action => action.noti),
        tap(noti => {})
      );
    },
    { dispatch: false }
  );
  exitNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(exitNotification),
        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());
          }

          this.store.dispatch(
            exitSuccess({
              res: {
                roomSeq: action.noti.roomSeq
              }
            })
          );
        })
      );
    },
    { dispatch: false }
  );
  exitNotificationOthers$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(exitNotificationOthers),
        withLatestFrom(
          this.store.pipe(
            select((state: any) => state.messenger.room.roomInfo as RoomInfo)
          )
        ),
        tap(([action, roomInfo]) => {
          if (
            !!roomInfo &&
            roomInfo.roomSeq === action.noti.roomSeq &&
            !!action.noti &&
            !!action.noti.SENDER_SEQ
          ) {
            this.store.dispatch(
              clearRoomUser({ userSeqs: [action.noti.SENDER_SEQ] })
            );
          }
        })
      );
    },
    { dispatch: false }
  );

  updateFontNotification$ = createEffect(
    () => {
      return this.actions$.pipe(
        ofType(updateFontNotification),
        map(action => action.noti),
        tap(noti => {})
      );
    },
    { dispatch: false }
  );

  constructor(
    private actions$: Actions,
    private store: Store<any>,
    private roomProtocolService: RoomProtocolService,
    private sessionStorageService: SessionStorageService,
    private logger: NGXLogger
  ) {}
}
