import { delGroupSuccess, buddy2 } from './../store/messenger/sync/actions';
import { Injectable, Inject } from '@angular/core';

import { tap, withLatestFrom, take } from 'rxjs/operators';

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

import {
  SSVC_TYPE_LOGOUT_RES,
  SSVC_TYPE_LOGOUT_REMOTE_NOTI,
  AuthenticationProtocolService,
  LogoutResponse,
  LogoutRemoteNotification,
  LogoutNotification,
  LoginResponse
} from '@ucap-webmessenger/protocol-authentication';

import { NGXLogger } from 'ngx-logger';
import {
  EventProtocolService,
  SSVC_TYPE_EVENT_SEND_NOTI,
  SendNotification,
  SSVC_TYPE_EVENT_READ_NOTI,
  SSVC_TYPE_EVENT_CANCEL_NOTI,
  SSVC_TYPE_EVENT_DEL_RES,
  SSVC_TYPE_EVENT_SEND_RES,
  SSVC_TYPE_EVENT_READ_RES
} from '@ucap-webmessenger/protocol-event';
import {
  InfoProtocolService,
  SSVC_TYPE_INFO_USER_NOTI,
  UserNotification
} from '@ucap-webmessenger/protocol-info';
import {
  RoomProtocolService,
  SSVC_TYPE_ROOM_INVITE_NOTI,
  SSVC_TYPE_ROOM_EXIT_NOTI,
  SSVC_TYPE_ROOM_EXIT_FORCING_NOTI,
  SSVC_TYPE_ROOM_FONT_UPD_NOTI,
  InviteNotification,
  UpdateNotification as RoomUpdateNotification,
  SSVC_TYPE_ROOM_UPD_RES,
  SSVC_TYPE_ROOM_EXIT_FORCING_RES,
  SSVC_TYPE_ROOM_EXIT_RES,
  SSVC_TYPE_ROOM_INVITE_RES,
  ExitForcingResponse,
  RoomInfo
} from '@ucap-webmessenger/protocol-room';
import {
  StatusProtocolService,
  SSVC_TYPE_STATUS_NOTI,
  StatusNotification
} from '@ucap-webmessenger/protocol-status';
import {
  ReadNotification,
  CancelNotification,
  DelNotification
} from '@ucap-webmessenger/protocol-event';
import {
  ExitNotification,
  ExitForcingNotification,
  UpdateFontNotification
} from '@ucap-webmessenger/protocol-room';
import {
  GroupProtocolService,
  SSVC_TYPE_GROUP_UPD_RES2,
  UpdateNotification as GroupUpdateNotification,
  SSVC_TYPE_GROUP_ADD_RES,
  AddNotification as GroupAddNotification,
  SSVC_TYPE_GROUP_DEL_RES,
  DelNotification as GroupDelNotification
} from '@ucap-webmessenger/protocol-group';
import {
  BuddyProtocolService,
  SSVC_TYPE_BUDDY_UPD_RES,
  UpdateNotification as BuddyUpdateNotification,
  SSVC_TYPE_BUDDY_ADD_RES,
  AddNotification as BuddyAddNotification,
  SSVC_TYPE_BUDDY_DEL_RES,
  DelNotification as BuddyDelNotification
} from '@ucap-webmessenger/protocol-buddy';

import * as AuthenticationStore from '@app/store/account/authentication';
import * as InfoStore from '@app/store/account/info';
import * as EventStore from '@app/store/messenger/event';
import * as MessageStore from '@app/store/messenger/message';
import * as SyncStore from '@app/store/messenger/sync';
import * as RoomStore from '@app/store/messenger/room';
import * as StatusStore from '@app/store/messenger/status';
import {
  NotificationRequest,
  NativeService,
  UCAP_NATIVE_SERVICE,
  NotificationType
} from '@ucap-webmessenger/native';
import { StringUtil } from '@ucap-webmessenger/ui';
import {
  UmgProtocolService,
  SSVC_TYPE_UMG_NOTI,
  UmgNotiNotification,
  SSVC_TYPE_UMG_DELETE_NOTI,
  UmgDeleteNotiNotification
} from '@ucap-webmessenger/protocol-umg';
import {
  LocalStorageService,
  SessionStorageService
} from '@ucap-webmessenger/web-storage';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';

import { environment } from '../../environments/environment';
import { NotificationMethod } from '@ucap-webmessenger/core';
import { Dictionary } from '@ngrx/entity';
import { MessageType } from '@ucap-webmessenger/api-message';
import { LogoutInfo, KEY_LOGOUT_INFO } from '@app/types';
import { TranslateService } from '@ngx-translate/core';
import { deleteMessageSuccess } from '@app/store/messenger/message';
import { ServerErrorCode } from '@ucap-webmessenger/protocol';

@Injectable()
export class AppNotificationService {
  constructor(
    private authenticationProtocolService: AuthenticationProtocolService,
    private eventProtocolService: EventProtocolService,
    private infoProtocolService: InfoProtocolService,
    private roomProtocolService: RoomProtocolService,
    private groupProtocolService: GroupProtocolService,
    private buddyProtocolService: BuddyProtocolService,
    private statusProtocolService: StatusProtocolService,
    private translateService: TranslateService,
    private umgProtocolService: UmgProtocolService,
    private localStorageService: LocalStorageService,
    private sessionStorageService: SessionStorageService,
    @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
    private store: Store<any>,
    private logger: NGXLogger
  ) {}

  public subscribe(): void {
    this.authenticationProtocolService.logoutNotification$
      .pipe(
        tap(notiOrRes => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_LOGOUT_RES:
              {
                const res = notiOrRes as LogoutNotification;
                this.logger.debug(
                  'Notification::authenticationProtocolService::LogoutResponse',
                  res
                );

                this.sessionStorageService.set<LogoutInfo>(KEY_LOGOUT_INFO, {
                  personLogout: true,
                  reasonCode: res.reasonCode,
                  ip: res.ip,
                  mac: res.mac
                } as LogoutInfo);
              }
              break;
            case SSVC_TYPE_LOGOUT_REMOTE_NOTI:
              {
                const noti = notiOrRes as LogoutRemoteNotification;
                this.logger.debug(
                  'Notification::authenticationProtocolService::LogoutRemoteNotification',
                  noti
                );

                this.sessionStorageService.set<LogoutInfo>(KEY_LOGOUT_INFO, {
                  personLogout: true,
                  reasonCode: ServerErrorCode.ERRCD_FORCE_INIT,
                  forceType: noti.requestDeviceType
                } as LogoutInfo);
              }
              break;
            default:
              break;
          }
          this.store.dispatch(AuthenticationStore.loginRedirect());
        })
      )
      .subscribe();

    this.eventProtocolService.notification$
      .pipe(
        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(([notiOrRes, curRoomInfo, roomList]) => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_EVENT_SEND_RES:
            case SSVC_TYPE_EVENT_SEND_NOTI:
              {
                const noti = notiOrRes as SendNotification;
                this.logger.debug(
                  'Notification::eventProtocolService::SendNotification',
                  noti
                );

                this.store.dispatch(
                  EventStore.sendNotification({
                    noti
                  })
                );

                // notification..
                if (notiOrRes.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI) {
                  let doNoti = true;

                  // 방별 알림이 꺼져 있으면 노티 안함.
                  if (
                    !!roomList[noti.roomSeq] &&
                    !roomList[noti.roomSeq].receiveAlarm
                  ) {
                    doNoti = false;
                  }

                  // 현재 열려 있는 방일경우 노티 안함.
                  if (
                    !!curRoomInfo &&
                    !!curRoomInfo.roomSeq &&
                    curRoomInfo.roomSeq === noti.roomSeq
                  ) {
                    doNoti = false;
                  }

                  if (doNoti) {
                    const appUserInfo = this.localStorageService.encGet<
                      AppUserInfo
                    >(KEY_APP_USER_INFO, environment.customConfig.appKey);

                    if (appUserInfo.settings.notification.use) {
                      if (
                        appUserInfo.settings.notification.method ===
                        NotificationMethod.Sound
                      ) {
                        const audio = new Audio(
                          'assets/sounds/messageAlarm.mp3'
                        );
                        audio.play();
                      } else {
                        const notiReq: NotificationRequest = {
                          type: NotificationType.Event,
                          seq: noti.roomSeq,
                          title: this.translateService.instant(
                            'notification.titleChatEventArrived'
                          ),
                          contents: StringUtil.convertFinalEventMessage(
                            noti.eventType,
                            noti.info.sentMessageJson
                          ),
                          image: '',
                          useSound: [
                            NotificationMethod.Sound,
                            NotificationMethod.SoundAndAlert
                          ].some(
                            n => n === appUserInfo.settings.notification.method
                          )
                            ? true
                            : false,
                          displayTime:
                            appUserInfo.settings.notification
                              .alertExposureTime * 1000
                        };
                        this.nativeService.notify(notiReq);
                      }
                    }
                  }
                }
              }
              break;
            case SSVC_TYPE_EVENT_READ_RES:
            case SSVC_TYPE_EVENT_READ_NOTI:
              {
                // 대화방 unread count 처리.
                const noti = notiOrRes as ReadNotification;
                this.logger.debug(
                  'Notification::eventProtocolService::ReadNotification',
                  noti
                );
                this.store.dispatch(EventStore.readNotification(noti));
              }
              break;
            case SSVC_TYPE_EVENT_CANCEL_NOTI:
              {
                const noti = notiOrRes as CancelNotification;
                this.logger.debug(
                  'Notification::eventProtocolService::CancelNotification',
                  noti
                );
                this.store.dispatch(
                  EventStore.cancelNotification({
                    noti
                  })
                );
              }
              break;
            case SSVC_TYPE_EVENT_DEL_RES:
              {
                const noti = notiOrRes as DelNotification;
                this.logger.debug(
                  'Notification::eventProtocolService::DelNotification',
                  noti
                );
                this.store.dispatch(
                  EventStore.delNotification({
                    noti
                  })
                );
              }
              break;
            default:
              break;
          }
        })
      )
      .subscribe();
    this.infoProtocolService.notification$
      .pipe(
        tap(notiOrRes => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_INFO_USER_NOTI:
              {
                const noti = notiOrRes as UserNotification;
                this.logger.debug(
                  'Notification::infoProtocolService::UserNotification',
                  noti
                );
                this.store.dispatch(
                  InfoStore.userNotification({
                    noti
                  })
                );
              }
              break;
            default:
              break;
          }
        })
      )
      .subscribe();
    this.groupProtocolService.notification$
      .pipe(
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) => state.messenger.sync.group2.syncDate as string
            )
          )
        ),
        tap(([notiOrRes, syncDate]) => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_GROUP_UPD_RES2:
              {
                const noti = notiOrRes as GroupUpdateNotification;
                this.logger.debug(
                  'Notification::groupProtocolService::GroupUpdateNotification',
                  noti
                );
                this.store.dispatch(
                  SyncStore.group2({
                    syncDate
                  })
                );
              }
              break;
            case SSVC_TYPE_GROUP_ADD_RES:
              {
                const noti = notiOrRes as GroupAddNotification;
                this.logger.debug(
                  'Notification::groupProtocolService::GroupAddNotification',
                  noti
                );
                this.store.dispatch(SyncStore.createGroupSuccess(noti));
              }
              break;
            case SSVC_TYPE_GROUP_DEL_RES:
              {
                const noti = notiOrRes as GroupDelNotification;
                this.logger.debug(
                  'Notification::groupProtocolService::GroupDelNotification',
                  noti
                );
                this.store.dispatch(SyncStore.delGroupSuccess(noti));
              }
              break;

            default:
              break;
          }
        })
      )
      .subscribe();
    this.buddyProtocolService.notification$
      .pipe(
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) => state.messenger.sync.buddy2.syncDate as string
            )
          )
        ),
        tap(([notiOrRes, syncDate]) => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_BUDDY_UPD_RES:
              {
                const noti = notiOrRes as BuddyUpdateNotification;
                this.logger.debug(
                  'Notification::groupProtocolService::BuddyUpdateNotification',
                  noti
                );
                this.store.dispatch(SyncStore.updateBuddySuccess(noti));
              }
              break;
            case SSVC_TYPE_BUDDY_ADD_RES:
              {
                const noti = notiOrRes as BuddyAddNotification;
                this.logger.debug(
                  'Notification::groupProtocolService::BuddyAddNotification',
                  noti
                );
                this.store.dispatch(SyncStore.buddy2({ syncDate }));
              }
              break;
            case SSVC_TYPE_BUDDY_DEL_RES:
              {
                const noti = notiOrRes as BuddyDelNotification;
                this.logger.debug(
                  'Notification::groupProtocolService::BuddyDelNotification',
                  noti
                );
                this.store.dispatch(SyncStore.delBuddySuccess(noti));
              }
              break;

            default:
              break;
          }
        })
      )
      .subscribe();
    this.roomProtocolService.notification$
      .pipe(
        withLatestFrom(
          this.store.pipe(
            select(
              (state: any) =>
                state.account.authentication.loginRes as LoginResponse
            )
          )
        ),
        tap(([notiOrRes, loginResInfo]) => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_ROOM_INVITE_RES:
              {
                const noti = notiOrRes as InviteNotification;
                this.logger.debug(
                  'Notification::roomProtocolService::InviteNotification',
                  noti
                );
                this.store.dispatch(
                  RoomStore.inviteSuccess({
                    roomSeq: noti.roomSeq
                  })
                );
              }
              break;
            case SSVC_TYPE_ROOM_UPD_RES:
              {
                const noti = notiOrRes as RoomUpdateNotification;
                this.logger.debug(
                  'Notification::roomProtocolService::RoomUpdateNotification',
                  noti
                );
                this.store.dispatch(
                  RoomStore.updateSuccess({
                    res: {
                      roomSeq: noti.roomSeq,
                      roomName:
                        noti.roomName.trim().length === 0
                          ? ' '
                          : noti.roomName.trim(),
                      receiveAlarm: noti.receiveAlarm,
                      syncAll: false
                    }
                  })
                );
              }
              break;
            case SSVC_TYPE_ROOM_INVITE_NOTI:
              {
                const noti = notiOrRes as InviteNotification;
                this.logger.debug(
                  'Notification::roomProtocolService::InviteNotification',
                  noti
                );
                this.store.dispatch(
                  RoomStore.inviteNotification({
                    noti
                  })
                );
              }
              break;
            case SSVC_TYPE_ROOM_EXIT_RES:
            case SSVC_TYPE_ROOM_EXIT_NOTI:
              {
                const noti = notiOrRes as ExitNotification;
                this.logger.debug(
                  'Notification::roomProtocolService::ExitNotification',
                  noti
                );

                if (noti.SENDER_SEQ === loginResInfo.userSeq) {
                  this.store.dispatch(
                    RoomStore.exitNotification({
                      noti
                    })
                  );
                } else {
                  this.store.dispatch(
                    RoomStore.exitNotificationOthers({
                      noti
                    })
                  );

                  if (!!noti && !!noti.SENDER_SEQ) {
                    this.store.dispatch(
                      SyncStore.clearRoomUsers({
                        roomSeq: noti.roomSeq,
                        userSeqs: [noti.SENDER_SEQ]
                      })
                    );
                  }
                }
              }
              break;
            case SSVC_TYPE_ROOM_EXIT_FORCING_RES:
              {
                // 내가 강퇴 진행.
                const res = notiOrRes as ExitForcingResponse;
                this.logger.debug(
                  'Notification::roomProtocolService::ExitForcingNotification RES',
                  res
                );
                this.store.dispatch(
                  RoomStore.exitForcingSuccess({
                    res
                  })
                );
              }
              break;
            case SSVC_TYPE_ROOM_EXIT_FORCING_NOTI:
              {
                // 내가 강퇴 됨.
                const noti = notiOrRes as ExitForcingNotification;
                this.logger.debug(
                  'Notification::roomProtocolService::ExitForcingNotification NOTI',
                  noti
                );
                this.store.dispatch(
                  RoomStore.exitForcingNotification({
                    noti
                  })
                );
              }
              break;
            case SSVC_TYPE_ROOM_FONT_UPD_NOTI:
              {
                const noti = notiOrRes as UpdateFontNotification;
                this.logger.debug(
                  'Notification::roomProtocolService::UpdateFontNotification',
                  noti
                );
                this.store.dispatch(
                  RoomStore.updateFontNotification({
                    noti
                  })
                );
              }
              break;
            default:
              break;
          }
        })
      )
      .subscribe();
    this.statusProtocolService.notification$
      .pipe(
        tap(notiOrRes => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_STATUS_NOTI:
              {
                const noti = notiOrRes as StatusNotification;
                this.logger.debug(
                  'Notification::statusProtocolService::StatusNotification',
                  noti
                );
                this.store.dispatch(
                  StatusStore.statusNotification({
                    noti
                  })
                );
              }
              break;
            default:
              break;
          }
        })
      )
      .subscribe();
    this.umgProtocolService.notification$
      .pipe(
        tap(notiOrRes => {
          switch (notiOrRes.SSVC_TYPE) {
            case SSVC_TYPE_UMG_NOTI:
              {
                const noti = notiOrRes as UmgNotiNotification;
                this.logger.debug(
                  'Notification::umgProtocolService::UmgNotiNotification',
                  noti
                );

                // unreadCount refresh..
                this.store.dispatch(MessageStore.retrieveUnreadCount({}));

                // Receive Message List refresh..
                this.store.dispatch(
                  MessageStore.retrieveMessage({
                    messageType: MessageType.Receive
                  })
                );

                // notification..
                const appUserInfo = this.localStorageService.encGet<
                  AppUserInfo
                >(KEY_APP_USER_INFO, environment.customConfig.appKey);

                if (appUserInfo.settings.notification.use) {
                  if (
                    appUserInfo.settings.notification.method ===
                    NotificationMethod.Sound
                  ) {
                    const audio = new Audio('assets/sounds/messageAlarm.mp3');
                    audio.play();
                  } else {
                    const notiReq: NotificationRequest = {
                      type: NotificationType.Message,
                      seq: noti.keyId,
                      title: this.translateService.instant(
                        'notification.titleMessageArrived'
                      ),
                      contents: noti.text,
                      image: noti.senderInfo.profileImageFile,
                      useSound: [
                        NotificationMethod.Sound,
                        NotificationMethod.SoundAndAlert
                      ].some(
                        n => n === appUserInfo.settings.notification.method
                      )
                        ? true
                        : false,
                      displayTime:
                        appUserInfo.settings.notification.alertExposureTime *
                        1000
                    };
                    this.nativeService.notify(notiReq);
                  }
                }

                // direct open detail
                if (appUserInfo.settings.notification.receiveForMessage) {
                  this.store.dispatch(
                    MessageStore.detailMessage({
                      messageType: MessageType.Receive,
                      msgId: Number(noti.keyId)
                    })
                  );
                }
              }
              break;
            case SSVC_TYPE_UMG_DELETE_NOTI:
              {
                const noti = notiOrRes as UmgDeleteNotiNotification;
                this.logger.debug(
                  'Notification::umgProtocolService::UmgDeleteNotiNotification',
                  noti
                );

                // Remove one Receive Message
                if (!!noti && !!noti.keyId) {
                  // clear badge in left navi
                  this.store.dispatch(MessageStore.retrieveUnreadCount({}));

                  // delete message in receive message list
                  this.store.dispatch(
                    deleteMessageSuccess({
                      messageType: MessageType.Receive,
                      msgList: [
                        {
                          msgId: Number(noti.keyId)
                        }
                      ]
                    })
                  );
                }
              }
              break;

            default:
              break;
          }
        })
      )
      .subscribe();
  }
}
