import * as log from "loglevel";
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
import { EncryptionService } from "../../common/service/EncryptionService";
import { NotificationService } from "../../common/service/NotificationService";
import { JsonAPI } from "../singleton/JsonAPI";
import { Sprintf } from "../../common/global/Sprintf";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { fetchErrorHandler } from "./FetchErrorHandler";

export class ChatModelHelper {
  private readonly _encryptionService: EncryptionService;
  private readonly _notificationService: NotificationService;

  constructor(
    encryptionService: EncryptionService,
    notificationService: NotificationService
  ) {
    this._encryptionService = encryptionService;
    this._notificationService = notificationService;
  }

  public async getMessages(
    userName: string,
    passphrase: string,
    page: number | null,
    lastMessageTime: Date | null,
    op: string
  ): Promise<ChatMessageViewModel[]> {
    switch (op) {
      case "page": {
        const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(
          userName,
          page!
        );
        const cVMs = Promise.all(
          data.map((vm) => this._toChatMessageVMAsync(vm, passphrase)).reverse()
        );
        return cVMs;
      }
      case "new": {
        const data: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(
          userName,
          page!
        );
        const cVMs = Promise.all(
          data.map((vm) => this._toChatMessageVMAsync(vm, passphrase)).reverse()
        );
        return cVMs;
      }
      case "update": {
        const data: ChatMessageDTO[] = await this._getNewMessagesAjax(
          userName,
          lastMessageTime!
        );
        const cVMs = Promise.all(
          data.map((vm) => this._toChatMessageVMAsync(vm, passphrase)).reverse()
        );
        return cVMs;
      }
      default: {
        log.error("Invalid operation");
        return Array();
      }
    }
  }

  public async isPassphraseValid(
    passphrase: string,
    userName: string
  ): Promise<boolean> {
    const messages: ChatMessageDTO[] = await this._getPaginatedMessagesAjax(
      userName,
      0
    );
    if (messages.length === 0) return true;
    try {
      this._encryptionService.decrypt(passphrase, messages[0].messageCipher);
    } catch (error) {
      return false;
    }
    return true;
  }

  private async _toChatMessageVMAsync(
    chatMessageDTO: ChatMessageDTO,
    passphrase: string
  ): Promise<ChatMessageViewModel> {
    const vm = new ChatMessageViewModel();
    vm.fromUser = chatMessageDTO.fromUser;
    vm.toUser = chatMessageDTO.toUser;
    vm.messageTime = chatMessageDTO.messageTime;
    vm.message = await this._encryptionService.decryptAsPromise(
      passphrase,
      chatMessageDTO.messageCipher
    );
    return vm;
  }

  private _toChatMessageVM(
    chatMessageDTO: ChatMessageDTO,
    passphrase: string
  ): ChatMessageViewModel {
    const vm = new ChatMessageViewModel();
    vm.fromUser = chatMessageDTO.fromUser;
    vm.toUser = chatMessageDTO.toUser;
    // vm.messageTime = chatMessageDTO.messageTime;
    chatMessageDTO.messageTime == null
      ? log.error("Message time somehow null")
      : (vm.messageTime = chatMessageDTO.messageTime);
    vm.message = this._encryptionService.decrypt(
      passphrase,
      chatMessageDTO.messageCipher
    ) as string;
    return vm;
  }

  private async _getAllMessagesAjax(toUser: string): Promise<any> {
    const headers = new Headers();
    if (JsonAPI.authToken == null) {
      log.error("authToken null");
      return;
    }
    headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
    const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser);
    log.debug(url);
    const response = await fetch(url, {
      method: "GET",
      headers: headers,
    });
    log.debug(response.clone());
    if (fetchErrorHandler(response.clone(), this._notificationService)) {
      return null;
    }
    const data: Promise<any> = await response.json();
    return data;
  }

  private async _getPaginatedMessagesAjax(
    toUser: string,
    page: number
  ): Promise<any> {
    const headers = new Headers();
    if (JsonAPI.authToken == null) {
      log.error("authToken null");
      return;
    }
    headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
    const url = Sprintf(
      JsonAPI.CHAT_MESSAGE_PAGE_GET,
      toUser,
      page,
      JsonAPI.CHAT_PAGE_SIZE
    );
    log.debug(url);
    const response = await fetch(url, {
      method: "GET",
      headers: headers,
    });
    log.debug(response.clone());
    if (fetchErrorHandler(response.clone(), this._notificationService)) {
      return null;
    }
    const data: Promise<any> = await response.json();
    function func(data: any) {
      const d1 = data.map((d: any) => {
        if (d.messageTime == null) return null;

        d.messageTime = new Date(d.messageTime);
        return d;
      });
      return d1;
    }
    const data2 = func(data);
    return data2;
  }

  private async _getMessagesAjax(
    toUser: string,
    lastMessageTimeStamp: Date
  ): Promise<any> {
    const headers = new Headers();
    if (JsonAPI.authToken == null) {
      log.error("authToken null");
      return;
    }
    headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
    // const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
    // log.debug(url)
    const response = await fetch(`/api/chat/get/messages/${toUser}`, {
      method: "GET",
      headers: headers,
    });
    log.debug(response.clone());
    if (fetchErrorHandler(response.clone(), this._notificationService)) {
      return null;
    }
    const data: Promise<any> = await response.json();
    function func(data: any) {
      const d1 = data.map((d: any) => {
        if (d.messageTime == null) return null;

        d.messageTime = new Date(d.messageTime);
        return d;
      });
      return d1;
    }
    const data2 = func(data);
    return data2;
  }

  private async _getNewMessagesAjax(
    toUser: string,
    lastMessageTimeStamp: Date
  ): Promise<any> {
    const headers = new Headers();
    headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
    // const url = Sprintf(JsonAPI.CHAT_MESSAGES_GET, toUser, page, 5);
    // log.debug(url)
    // log.debug(lastMessageTimeStamp);
    // log.debug(lastMessageTimeStamp.toISOString())
    const response = await fetch(
      `/api/chat/get/messages/${toUser}/${lastMessageTimeStamp.toISOString()}`,
      {
        method: "GET",
        headers: headers,
      }
    );
    log.debug(response.clone());
    if (!response.ok) location.reload();
    const data: Promise<any> = await response.json();
    function func(data: any) {
      const d1 = data.map((d: any) => {
        if (d.messageTime == null) return null;

        d.messageTime = new Date(d.messageTime);
        return d;
      });
      return d1;
    }
    const data2 = func(data);
    return data2;
  }
}
