import { Observable, of, forkJoin } from 'rxjs';
import { take, concatMap, map, catchError } from 'rxjs/operators';

import { Injectable, Inject, ChangeDetectorRef } from '@angular/core';

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

import { LocaleCode, DeviceType, FileUtil } from '@ucap/core';

import { PasswordUtil } from '@ucap/pi';
import { LoginResponse, SSOMode } from '@ucap/protocol-authentication';
import { NativeService } from '@ucap/native';

import {
  SessionStorageService,
  LocalStorageService
} from '@ucap/ng-web-storage';
import { UCAP_NATIVE_SERVICE } from '@ucap/ng-native';
import { InnerProtocolService } from '@ucap/ng-protocol-inner';
import { AuthenticationProtocolService } from '@ucap/ng-protocol-authentication';
import { LoginActions } from '@ucap/ng-store-authentication';

import { LoginSession } from '@app/models/login-session';
import { UserStore } from '@app/models/user-store';

import { AppKey } from '@app/types/app-key.type';

import { environment } from '@environments';
import {
  RoomInfo,
  RoomType,
  OpenRequest,
  Open3Request
} from '@ucap/protocol-room';
import { Dictionary } from '@ngrx/entity';
import {
  RoomUserMap,
  RoomUserShortMap
} from '@ucap/ng-store-chat/lib/store/room/state';

import {
  TranslatePipe as OrganizationTranslate,
  TranslateService
} from '@ucap/ng-ui-organization';
import { I18nService } from '@ucap/ng-i18n';
import { ChattingActions, RoomActions } from '@ucap/ng-store-chat';
import {
  SendRequest as SendEventRequest,
  EventType
} from '@ucap/protocol-event';
import {
  MassTalkSaveRequest,
  FileTalkSaveResponse,
  FileTalkSaveRequest
} from '@ucap/api-common';
import { CommonApiService } from '@ucap/ng-api-common';
import { StatusCode, FileUploadItem } from '@ucap/api';
import { LogService } from '@ucap/ng-logger';
import { MatDialog } from '@angular/material/dialog';
import {
  AlertDialogComponent,
  AlertDialogData,
  AlertDialogResult
} from '@ucap/ng-ui';
import { StickerFilesInfo, KEY_STICKER_HISTORY } from '@ucap/ng-core';
import {
  CreateDialogComponent,
  CreateDialogData,
  CreateDialogResult
} from '@app/sections/chat/dialogs/create.dialog.component';

@Injectable({
  providedIn: 'root'
})
export class AppChatService {
  defaultProfileImage = 'assets/images/ico/img_nophoto.svg';
  defaultProfileImageMulti = 'assets/images/ico/img_nophoto.svg';

  constructor(
    private i18nService: I18nService,
    private dialog: MatDialog,
    private localStorageService: LocalStorageService,
    private store: Store<any>,
    private commonApiService: CommonApiService,
    private logService: LogService
  ) {
    this.i18nService.setDefaultNamespace('chat');
  }

  /**
   * 방이름 생성.
   * cf) 방이름이 지정되어 있다면 방이름 리턴, 지정되어 있지 않으면 방참여인원의 이름 조합.
   */
  getRoomName(
    organizationTranslate: OrganizationTranslate,
    loginRes: LoginResponse,
    roomInfo: RoomInfo,
    roomUsersDictionary?: Dictionary<RoomUserMap>,
    roomUsersShortDictionary?: Dictionary<RoomUserShortMap>
  ): string {
    if (!roomInfo) {
      return '';
    }

    let roomName = '...';

    switch (roomInfo.roomType) {
      case RoomType.Mytalk:
        roomName = 'MyTalk';
        break;
      default:
        {
          const roomId = roomInfo.roomId;
          const roomUsers = !!roomUsersDictionary
            ? roomUsersDictionary[roomId]
            : undefined;
          const roomUsersShort = !!roomUsersShortDictionary
            ? roomUsersShortDictionary[roomId]
            : undefined;

          let users = [];
          let existUsers = false;
          if (!!roomUsers && roomUsers.userInfos.length > 0) {
            existUsers = true;
            users = roomUsers.userInfos.filter(
              (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
            );
          } else if (!!roomUsersShort && roomUsersShort.userInfos.length > 0) {
            existUsers = true;
            users = roomUsersShort.userInfos.filter(
              (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
            );
          }

          const curRoomName = roomInfo.roomName;
          if (!!curRoomName && curRoomName.trim().length > 0) {
            // Exist RoomName.
            roomName = curRoomName;
          } else {
            if (users.length > 0) {
              roomName = organizationTranslate.transform(users, 'name', ',');
            } else {
              if (existUsers) {
                roomName = this.i18nService.t('room.noRoomUser');
              }
            }
          }
        }
        break;
    }

    return roomName;
  }

  /**
   * 방 프로필 이미지 생성.
   * cf) 방 참여인원의 프로필을 리턴.
   * 없으면, defaultProfileImage, defaultProfileImageMulti,
   * 멀티룸은 기본 defaultProfileImageMulti
   */
  getRoomProfileImage(
    roomInfo: RoomInfo,
    loginRes: LoginResponse,
    roomUsersDictionary?: Dictionary<RoomUserMap>,
    roomUsersShortDictionary?: Dictionary<RoomUserShortMap>,
    defaultProfileImage?: string,
    defaultProfileImageMulti?: string
  ): string {
    defaultProfileImage = defaultProfileImage || this.defaultProfileImage;
    defaultProfileImageMulti =
      defaultProfileImageMulti || this.defaultProfileImageMulti;

    let roomImage = '';

    if (!!roomInfo) {
      switch (roomInfo.roomType) {
        case RoomType.Mytalk:
          {
            if (!!loginRes && !!loginRes.userInfo) {
              roomImage = loginRes.userInfo.profileImageFile;
            }
          }
          break;
        case RoomType.Multi:
          {
            roomImage = defaultProfileImageMulti;
          }
          break;
        default:
          {
            const roomUsers = this.getRoomUserList(
              loginRes,
              roomInfo.roomId,
              roomUsersDictionary,
              roomUsersShortDictionary
            );
            if (
              !!roomUsers &&
              !!roomUsers.existUsers &&
              roomUsers.users.length > 0
            ) {
              roomImage = roomUsers.users[0].profileImageFile;
            }
          }
          break;
      }
    }

    if (roomImage.trim().length === 0) {
      roomImage = defaultProfileImage;
    }

    return roomImage;
  }

  /**
   * 방 인원 정보 수집.
   * cf) roomUserShort, roomUser 데이터가 쌍을 이루는데 수집방법에 따라 short 만 존재할 수 있어, 수집할 수 있는 방인원을 리턴.
   * roomUser 가 detail 정보라 우선함.
   */
  getRoomUserList(
    loginRes: LoginResponse,
    roomId: string,
    roomUsersDictionary?: Dictionary<RoomUserMap>,
    roomUsersShortDictionary?: Dictionary<RoomUserShortMap>
  ) {
    let users = [];
    let existUsers = false;

    const roomUsers = !!roomUsersDictionary
      ? roomUsersDictionary[roomId]
      : undefined;
    const roomUsersShort = !!roomUsersShortDictionary
      ? roomUsersShortDictionary[roomId]
      : undefined;

    if (!!roomUsers && roomUsers.userInfos.length > 0) {
      existUsers = true;
      users = roomUsers.userInfos.filter(
        (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
      );
    } else if (!!roomUsersShort && roomUsersShort.userInfos.length > 0) {
      existUsers = true;
      users = roomUsersShort.userInfos.filter(
        (userInfo) => userInfo.seq !== Number(loginRes.userSeq)
      );
    }

    return {
      existUsers,
      users
    };
  }

  /**
   * 스티커 히스토리 관리 in localstorage.
   * cf) 스티커 전송 성공분만 처리.
   */
  setStickerHistory(sticker: StickerFilesInfo) {
    const history = this.localStorageService.get<string[]>(KEY_STICKER_HISTORY);

    if (!!history && history.length > 0) {
      const stickers: string[] = [];
      [sticker.index, ...history.filter((hist) => hist !== sticker.index)].map(
        (s, i) => {
          if (i < 10) {
            stickers.push(s);
          }
        }
      );

      this.localStorageService.set<string[]>(KEY_STICKER_HISTORY, stickers);
    } else {
      this.localStorageService.set<string[]>(KEY_STICKER_HISTORY, [
        sticker.index
      ]);
    }
  }

  /**
   * Event Send Protocol Service.
   */
  protected sendEvent(
    senderSeq: string,
    roomId: string,
    eventType: EventType,
    sentMessage: string
  ) {
    this.store.dispatch(
      ChattingActions.send({
        senderSeq,
        req: {
          roomId,
          eventType,
          sentMessage
        } as SendEventRequest
      })
    );
  }

  /** Send Normal message */
  sendMessageOfNormal(senderSeq: string, roomId: string, sentMessage: string) {
    this.sendEvent(senderSeq, roomId, EventType.Character, sentMessage);
  }

  /** Send Masstext message */
  sendMessageOfMassText(
    loginRes: LoginResponse,
    deviceType: DeviceType,
    roomId: string,
    sentMessage: string
  ) {
    const req: MassTalkSaveRequest = {
      userSeq: loginRes.userSeq,
      deviceType,
      token: loginRes.tokenString,
      content: sentMessage.replace(/"/g, '\\"'),
      roomId
    };

    this.commonApiService
      .massTalkSave(req)
      .pipe(
        take(1),
        map((res) => {
          if (res.statusCode === StatusCode.Success) {
            this.sendEvent(
              loginRes.userSeq,
              roomId,
              EventType.MassText,
              res.returnJson
            );
          } else {
            this.logService.error(
              `commonApiService] massTalkSave ${res?.errorMessage}`
            );
          }
        }),
        catchError((error) => of({ error }))
      )
      .subscribe();
  }

  async sendMessageOfSticker(
    senderSeq: string,
    roomId: string,
    selectedSticker: StickerFilesInfo,
    sentMessage: string
  ) {
    // Validation
    if (
      !!sentMessage &&
      sentMessage.trim().length > environment.productConfig.chat.masstextLength
    ) {
      const result = await this.dialog.open<
        AlertDialogComponent,
        AlertDialogData,
        AlertDialogResult
      >(AlertDialogComponent, {
        panelClass: 'miniSize-dialog',
        data: {
          title: this.i18nService.t('errors.label'),
          message: this.i18nService.t('errors.maxLengthOfMassText', {
            maxLength: environment.productConfig.chat.masstextLength
          })
        }
      });
      return;
    }

    // Send
    this.sendEvent(
      senderSeq,
      roomId,
      EventType.Sticker,
      JSON.stringify({
        name: '스티커',
        file: selectedSticker.index,
        chat: !!sentMessage ? sentMessage.trim() : ''
      })
    );

    // set sticker's history in localstorage
    this.setStickerHistory(selectedSticker);
  }

  /** Send Translation message */
  sendMessageOfTranslate(
    loginRes: LoginResponse,
    deviceType: DeviceType,
    destLocale: string,
    roomId: string,
    sentMessage: string,
    selectedSticker?: StickerFilesInfo
  ) {
    // const destLocale = this.destLocale;
    // const original = message;
    // const roomSeq = this.roomInfoSubject.value.roomSeq;
    // if (!!this.isTranslationProcess) {
    //   return;
    // }
    // this.isTranslationProcess = true;
    // this.commonApiService
    //   .translationSave({
    //     userSeq: this.loginResSubject.value.userSeq,
    //     deviceType: this.environmentsInfo.deviceType,
    //     token: this.loginResSubject.value.tokenString,
    //     roomSeq,
    //     original,
    //     srcLocale: '',
    //     destLocale
    //   } as TranslationSaveRequest)
    //   .pipe(
    //     take(1),
    //     map((res) => {
    //       if (res.statusCode === StatusCode.Success) {
    //         let sentMessage = '';
    //         let eventType = EventType.Translation;
    //         let previewObject: TranslationEventJson | MassTranslationEventJson;
    //         if (res.translationSeq > 0) {
    //           // Mass Text Translation
    //           previewObject = res;
    //           sentMessage = res.returnJson;
    //           eventType = EventType.MassTranslation;
    //         } else {
    //           // Normal Text Translation
    //           previewObject = {
    //             locale: destLocale,
    //             original,
    //             translation: res.translation,
    //             stickername: '',
    //             stickerfile: !!this.selectedSticker
    //               ? this.selectedSticker.index
    //               : ''
    //           };
    //           sentMessage = JSON.stringify(previewObject);
    //           eventType = EventType.Translation;
    //         }
    //         if (!!this.translationPreview) {
    //           // preview
    //           this.translationPreviewInfo = {
    //             previewInfo: res,
    //             translationType: eventType
    //           };
    //           this.changeDetectorRef.detectChanges();
    //         } else {
    //           // direct send
    //           this.store.dispatch(
    //             EventStore.send({
    //               senderSeq: this.loginResSubject.value.userSeq,
    //               req: {
    //                 roomSeq,
    //                 eventType,
    //                 sentMessage
    //               }
    //             })
    //           );
    //           if (!!this.translationPreviewInfo) {
    //             this.translationPreviewInfo = null;
    //           }
    //         }
    //         if (!!this.selectedSticker) {
    //           this.isShowStickerSelector = false;
    //           this.setStickerHistory(this.selectedSticker);
    //           this.selectedSticker = null;
    //         }
    //       } else {
    //         this.isTranslationProcess = false;
    //         this.dialogService.open<
    //           AlertDialogComponent,
    //           AlertDialogData,
    //           AlertDialogResult
    //         >(AlertDialogComponent, {
    //           panelClass: 'miniSize-dialog',
    //           data: {
    //             title: '',
    //             message: this.translateService.instant(
    //               'chat.error.translateServerError'
    //             )
    //           }
    //         });
    //         this.logger.error('res', res);
    //       }
    //     }),
    //     catchError((error) => {
    //       this.isTranslationProcess = false;
    //       this.dialogService.open<
    //         AlertDialogComponent,
    //         AlertDialogData,
    //         AlertDialogResult
    //       >(AlertDialogComponent, {
    //         panelClass: 'miniSize-dialog',
    //         data: {
    //           title: '',
    //           message: this.translateService.instant(
    //             'chat.error.translateServerError'
    //           )
    //         }
    //       });
    //       return of(this.logger.error('error', error));
    //     })
    //   )
    //   .subscribe(() => {
    //     this.isTranslationProcess = false;
    //   });
  }

  /** Send AttachFile message */
  sendMessageOfAttachFile(
    loginRes: LoginResponse,
    deviceType: DeviceType,
    roomId: string,
    fileUploadItems: FileUploadItem[]
  ): Promise<boolean> {
    const executor = async (
      resolve: (value?: boolean | PromiseLike<boolean>) => void,
      reject: (reason?: any) => void
    ) => {
      const allObservables: Observable<FileTalkSaveResponse>[] = [];

      for (const fileUploadItem of fileUploadItems) {
        let thumbnail: File;
        if (
          -1 !==
          [
            '3gp',
            'avi',
            'm4v',
            'mkv',
            'mov',
            'mp4',
            'mpeg',
            'mpg',
            'rv',
            'ts',
            'webm',
            'wmv'
          ].indexOf(FileUtil.getExtension(fileUploadItem.file.name))
        ) {
          try {
            thumbnail = await FileUtil.thumbnail(fileUploadItem.file);
          } catch (err) {
            this.logService.error('video thumbnail error.', err);
          }
          this.logService.debug('thumbnail', thumbnail);
        }

        const req: FileTalkSaveRequest = {
          userSeq: loginRes.userSeq,
          deviceType,
          token: loginRes.tokenString,
          roomId,
          file: fileUploadItem.file,
          fileName: fileUploadItem.file.name,
          thumb: thumbnail,
          fileUploadItem
        };

        allObservables.push(
          this.commonApiService.fileTalkSave(req).pipe(
            map((res) => {
              if (!res) {
                return;
              }
              if (StatusCode.Success === res.statusCode) {
                return res;
              } else {
                throw res;
              }
            })
          )
        );
      }

      forkJoin(allObservables)
        .pipe(take(1))
        .subscribe(
          (resList) => {
            for (const res of resList) {
              this.store.dispatch(
                ChattingActions.send({
                  senderSeq: loginRes.userSeq,
                  req: {
                    roomId,
                    eventType: EventType.File,
                    sentMessage: JSON.stringify(res.returnJson)
                  } as SendEventRequest
                })
              );
            }
          },
          (error) => {
            this.logService.debug('onFileSelected error', error);
            const msg = this.i18nService.t('common.file.errors.failToUpload');
            alert(msg);

            reject(msg);
          },
          () => {
            resolve(true);
          }
        );
    };

    return new Promise<boolean>(executor);
  }

  /**
   * Open Dialog for 'New Room Open'.
   */
  newOpenRoomDialog(): void {
    const dialogRef = this.dialog.open<
      CreateDialogComponent,
      CreateDialogData,
      CreateDialogResult
    >(CreateDialogComponent, {
      width: '100%',
      height: '100%',
      data: {}
    });

    dialogRef
      .afterClosed()
      .pipe(
        take(1),
        map((result) => {
          if (!!result && !!result.userSeqs && result.userSeqs.length > 0) {
            this.newOpenRoom(result.userSeqs, result.isTimer);
          }
        }),
        catchError((err) => {
          return of(err);
        })
      )
      .subscribe();
  }

  newOpenRoom(
    userSeqs: string[],
    isTimerRoom: boolean,
    loginRes?: LoginResponse
  ) {
    if (!userSeqs || userSeqs.length === 0) {
      return;
    }
    isTimerRoom = isTimerRoom || false;

    if (!!isTimerRoom) {
      /** Timer Room Open. */
      const req: Open3Request = {
        divCd: 'OPROOMT',
        roomName: '',
        isTimerRoom,
        timerRoomInterval:
          environment.productConfig.chat.timerRoomDefaultInterval,
        userSeqs
      };
      this.store.dispatch(RoomActions.createTimer({ req }));
    } else {
      /** Normal Room Open */
      let req: OpenRequest;
      if (
        userSeqs.length === 1 &&
        !!loginRes &&
        userSeqs[0] === loginRes.userSeq
      ) {
        // MyTalk Open.
        req = {
          divCd: 'OPMYTALK',
          userSeqs: [loginRes.talkWithMeBotSeq + '']
        };
      } else {
        req = {
          divCd: 'OPROOM',
          userSeqs
        };
      }
      this.store.dispatch(RoomActions.create({ req }));
    }
  }
}
