import * as DOMPurify from "dompurify";
import * as log from "loglevel";
import { ChatMessageDTO } from "../../common/dto/ChatMessageDTO";
import { MessageCipherDTO } from "../../common/dto/MessageCipherDTO";
import { ChatModel } from "../model/ChatModel";
import { Observer } from "../observe/Observer";
import { EncryptionService } from "../../common/service/EncryptionService";
import { MarkDownService } from "../../common/service/MarkDownService";
import { JsonAPI } from "../singleton/JsonAPI";
import { ChatMessageViewModel } from "../viewmodel/ChatMessageViewModel";
import { ChatViewDeps } from "./ChatViewDeps";
import { fetchHandler } from "./FetchHandler";
import { ObserverData } from "../observe/ObserverData";
import { NotificationService } from "../../common/service/NotificationService";
import { UserModel } from "../model/UserModel";

export class ChatView implements Observer<ChatMessageViewModel> {
  private readonly _chatModel: ChatModel;
  private readonly _messageContainer: HTMLElement;
  private readonly _messageSendTemplate: Handlebars.TemplateDelegate<
    ChatMessageViewModel
  >;
  private readonly _messageReceiveTemplate: Handlebars.TemplateDelegate<
    ChatMessageViewModel
  >;
  private readonly _markdownService: MarkDownService;
  private readonly _encryptionService: EncryptionService;
  private readonly _notificationService: NotificationService;
  private readonly _userModel: UserModel;

  constructor(deps: ChatViewDeps) {
    this._messageContainer = deps.messageContainer;
    this._chatModel = deps.chatModel;
    this._messageSendTemplate = deps.messageSendTemplate;
    this._messageReceiveTemplate = deps.messageReceiveTemplate;
    this._markdownService = deps.markdownService;
    this._encryptionService = deps.encryptionService;
    this._notificationService = deps.notificationService;
    this._userModel = deps.userModel;
    this._initEventListeners();

    $(document).ready(function () {
      $("#action_menu_btn").click(function () {
        $(".action_menu").toggle();
      });
    });

    this._chatMessagePageLoadAjax();
  }

  update(cd: ObserverData<ChatMessageViewModel>): void {
    log.info("ChatView: updating view");

    switch (cd.op) {
      case "clear":
        {
          $(this._messageContainer).html("");
        }
        break;
      case "new":
        {
          cd.data.forEach((vm: ChatMessageViewModel) => {
            let rendered = this.renderMessage(vm);
            $(this._messageContainer).append(rendered);
          });
          $(this._messageContainer)
            .stop()
            .animate(
              {
                scrollTop: $(this._messageContainer)[0].scrollHeight,
              },
              1500
            );
        }
        break;
      case "update":
        {
          cd.data.forEach((vm: ChatMessageViewModel) => {
            let rendered = this.renderMessage(vm);
            $(this._messageContainer).append(rendered);
          });
          // if (cd.data.length > 0) {
          //   this._userModel.notify();
          // }
          $(this._messageContainer)
            .stop()
            .animate(
              {
                scrollTop: $(this._messageContainer)[0].scrollHeight,
              },
              1500
            );
        }
        break;
      case "page":
        {
          {
            const rev: ChatMessageViewModel[] = Object.create(cd.data);
            rev.reverse();
            rev.forEach((vm: ChatMessageViewModel) => {
              let rendered = this.renderMessage(vm);
              $(this._messageContainer).prepend(rendered);
            });
          }
        }
        break;
      default:
        new Error("Invalid option");
    }
  }

  private renderMessage(vm: ChatMessageViewModel): string {
    const vmTemp: ChatMessageViewModel = { ...vm };
    vmTemp.message = this._markdownService.render(vm.message);
    switch (vmTemp.fromUser) {
      case JsonAPI.principleName:
        return DOMPurify.sanitize(this._messageSendTemplate(vmTemp));
      default:
        return DOMPurify.sanitize(this._messageReceiveTemplate(vmTemp));
    }
  }

  private _initEventListeners(): void {
    this._addChatFormEL();
  }

  private _addChatFormEL() {
    const chatForm = document.getElementById(
      "chatMessageForm"
    ) as HTMLSelectElement;

    if (chatForm == null) {
      log.error("Chat form is null");
    } else {
      chatForm.addEventListener("submit", (e) =>
        this._createChatMessageDTO(e, chatForm)
      );
    }
  }

  private _createChatMessageDTO(e: Event, chatForm: HTMLSelectElement): void {
    e.preventDefault();

    let contactName = JsonAPI.contactName;

    if (!chatForm.checkValidity()) {
      log.error("Form is not valid");
      chatForm.classList.add("was-validated");
      return;
    }
    chatForm.classList.add("was-validated");

    const chatInput = document.getElementById("chatInput") as HTMLInputElement;
    const vm = this._userModel.activeUsersList.find(
      (u) => u.userName == JsonAPI.contactName
    );
    // new Date().
    vm!.lastMessageTime = new Date();

    const passphrase = vm?.passphrase;

    if (chatInput.value == "") {
      this._notificationService.error("Please enter a message");
      return;
    }

    // if (passphraseInput.value == '' || passphraseInput.value == null) {
    //     this._notificationService.error("Please enter a passphrase");
    //     log.error("Passphrase is null.");
    //     return;
    // }

    const messageContent = chatInput.value;
    vm!.lastMessageText = messageContent.slice(0, 15) + "...";
    const msgTime = new Date();
    const context: ChatMessageViewModel = {
      fromUser: JsonAPI.principleName,
      toUser: contactName,
      message: messageContent,
      messageTime: msgTime,
    };

    this.update({ data: new Array(context), op: "new" });
    this._userModel.updateLastActive(contactName, msgTime)
    this._userModel.notify();

    let messageCipher: MessageCipherDTO = this._encryptionService.encrypt(
      passphrase!,
      messageContent
    );
    let chatMessageDTO = {
      fromUser: JsonAPI.principleName,
      toUser: contactName,
      messageCipher: messageCipher,
      messageTime: msgTime.toISOString(),
    };
    this._sendMessageAJAX(chatMessageDTO);
  }

  private _sendMessageAJAX(chatMessageDTO: any): void {
    let headers = new Headers();
    //	console.log("Token = " + btoa("hmm" + ":" + "hmm"))

    // headers.append('Accept','application/json')
    headers.append("Content-Type", "application/json");
    // headers.append('Authorization', basicAuthToken);
    // @ts-ignore
    headers.append("X-AUTH-TOKEN", JsonAPI.authToken);
    fetch(JsonAPI.MESSAGE_POST, {
      method: "POST",
      headers: headers,
      body: JSON.stringify(chatMessageDTO),
    })
      .then((response) => {
        log.debug(response);
        return response.clone();
      })
      .then((response) => fetchHandler(response, this._notificationService));
  }

  private _chatMessagePageLoadAjax() {
    this._messageContainer.addEventListener("scroll", (e) => {
      if (
        $(this._messageContainer).scrollTop() == 0 &&
        $(this._messageContainer).html() != ""
      ) {
        let currentMsg = $(".msg:first");
        log.debug("Reached top");
        const vm = this._userModel.activeUsersList.find(
          (u) => u.userName == JsonAPI.contactName
        );
        this._chatModel.getMessages(vm!, "page").then(() => {
          if (currentMsg != null) {
            // log.debug(currentMsg.offset()!.top)
            $(this._messageContainer).scrollTop(
              currentMsg.position().top - $(".msg").position()!.top
            );
          }
        });
      }
    });
  }
}
