// SPDX-FileCopyrightText: 2023 g10 code GmbH
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later

/**
 * Download the mail content from the EWS API
 * @returns {Promise<string>}
 */
function downloadViaRest(item) {
  return new Promise((resolve, reject) => {
    const request =
      '<?xml version="1.0" encoding="utf-8"?>' +
      '<soap:Envelope xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"' +
      '               xmlns:xsd="https://www.w3.org/2001/XMLSchema"' +
      '               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"' +
      '               xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">' +
      '  <soap:Header>' +
      '    <RequestServerVersion Version="Exchange2013" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" soap:mustUnderstand="0" />' +
      '  </soap:Header>' +
      '  <soap:Body>' +
      '    <GetItem xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">' +
      '      <ItemShape>' +
      '        <t:BaseShape>Default</t:BaseShape>' +
      '        <t:IncludeMimeContent>true</t:IncludeMimeContent>' +
      '      </ItemShape>' +
      '      <ItemIds><t:ItemId Id="' + item.itemId + '"/></ItemIds>' +
      '    </GetItem>' +
      '  </soap:Body>' +
      '</soap:Envelope>';

    Office.context.mailbox.makeEwsRequestAsync(request, (asyncResult) => {
      const parser = new DOMParser();
      xmlDoc = parser.parseFromString(asyncResult.value, "text/xml");

      const mimeContent = xmlDoc.getElementsByTagName('t:MimeContent')[0].innerHTML;
      resolve(atob(mimeContent));
    });
  })
}

const {createApp} = Vue

function i18n(text) {
  const lang = Office.context.displayLanguage
  if (lang in messages && text in messages[lang]) {
    return messages[lang][text];
  }
  return text;
}

function i18nc(context, text) {
  const lang = Office.context.displayLanguage
  if (lang in messages && text in messages[lang]) {
    return messages[lang][text];
  }
  return text;
}

const vueApp = {
  data() {
    return {
      error: '',
      versionWarning: '',
      content: '',
      hasSelection: true,
      viewerOpen: false,
      status: {
        encrypted: false,
        signed: false,
        drafts: [],
        fetched: false,
      },
      socket: null,
    }
  },
  computed: {
    loaded() {
      return this.content.length > 0 && this.status.fetched;
    },
    statusText() {
      if (!this.loaded) {
        return i18nc("Loading placeholder", "Loading...");
      }
      if (this.status.encrypted) {
        return this.status.signed ? i18n("This mail is encrypted and signed.") : i18n("This mail is encrypted.");
      }
      if (this.status.signed) {
        return i18n("This mail is signed")
      }
      return i18n("This mail is not encrypted nor signed.");
    },
    decryptButtonText() {
      if (!this.loaded) {
        return '';
      }
      if (this.status.encrypted) {
        return this.i18nc("@action:button", "Decrypt")
      }

      return this.i18nc("@action:button", "View email")
    },
  },
  methods: {
    i18n,
    i18nc,
    gpgolLog(message, args) {
      console.log(message, args);
      if (this.socket) {
        this.socket.send(JSON.stringify({
          command: "log",
          arguments: {
            message,
            args: JSON.stringify(args),
          },
        }));
      }
    },
    genericMailAction(command) {
      this.socket.send(JSON.stringify({
        command,
        arguments: {
          body: this.content,
          email: Office.context.mailbox.userProfile.emailAddress,
          displayName: Office.context.mailbox.userProfile.displayName,
        }
      }));
    },
    view() {
      this.genericMailAction('view');
    },
    reply() {
      this.genericMailAction('reply');
    },
    forward() {
      this.genericMailAction('forward');
    },
    newEmail() {
      this.socket.send(JSON.stringify({
        command: 'composer',
        arguments: {
          email: Office.context.mailbox.userProfile.emailAddress,
          displayName: Office.context.mailbox.userProfile.displayName,
        }
      }));
    },
    openDraft(id) {
      this.socket.send(JSON.stringify({
        command: 'open-draft',
        arguments: {
          id: id,
          email: Office.context.mailbox.userProfile.emailAddress,
          displayName: Office.context.mailbox.userProfile.displayName,
        }
      }));
    },
    deleteDraft(id) {
      this.socket.send(JSON.stringify({
        command: 'delete-draft',
        arguments: {
          id: id,
          email: Office.context.mailbox.userProfile.emailAddress,
          displayName: Office.context.mailbox.userProfile.displayName,
        }
      }));
      // TODO this.status.drafts.splice(this.status.drafts.findIndex((draft) => draft.id === id), 1);
    },
    info() {
      this.socket.send(JSON.stringify({
        command: 'info',
        arguments: {
          itemId: Office.context.mailbox.item.itemId,
          email: Office.context.mailbox.userProfile.emailAddress,
          body: this.content,
        }
      }));
    },
    displayDate(timestamp) {
      const date = new Date(timestamp * 1000);
      let todayDate = new Date();
      let lastModification = '';
      if ((new Date(date)).setHours(0, 0, 0, 0) === todayDate.setHours(0, 0, 0, 0)) {
        return date.toLocaleTimeString([], {
          hour: 'numeric',
          minute: 'numeric',
        });
      } else {
        return date.toLocaleDateString();
      }
    },
    async downloadContent() {
      this.content = await downloadViaRest(Office.context.mailbox.item);
      this.hasSelection = true;
      if (this.socket && this.socket.readyState === WebSocket.OPEN) {
        this.info();
      }
    },
    webSocketConnect(vueInstance = null) {
      if (!vueInstance) {
        vueInstance = this;
      }
      console.log("Set socket")
      vueInstance.socket = new WebSocket("wss://localhost:5657");

      // Connection opened
      vueInstance.socket.addEventListener("open", (event) => {
        vueInstance.error = '';
        vueInstance.socket.send(JSON.stringify({
          command: "register",
          arguments: {
            emails: [Office.context.mailbox.userProfile.emailAddress],
            type: 'webclient',
          },
        }));

        vueInstance.socket.send(JSON.stringify({
          command: 'restore-autosave',
          arguments: {
            email: Office.context.mailbox.userProfile.emailAddress,
            displayName: Office.context.mailbox.userProfile.displayName,
          }
        }));

        if (vueInstance.content.length > 0) {
          vueInstance.info()
        }
      });

      vueInstance.socket.addEventListener("close", (event) => {
        vueInstance.error = i18n("Native client was disconnected, reconnecting in 1 second.");
        console.log(event.reason)
        setTimeout(function () {
          vueInstance.webSocketConnect(vueInstance);
        }, 1000);
      });

      vueInstance.socket.addEventListener("error", (event) => {
        vueInstance.error = i18n("Native client received an error");
        vueInstance.socket.close();
      });

      // Listen for messages
      vueInstance.socket.addEventListener("message", function(result) {
        const { data } = result;
        const message = JSON.parse(data);
        vueInstance.gpgolLog("Received message from server", { command: message.command});
        switch (message.command) {
        case 'ews':
          Office.context.mailbox.makeEwsRequestAsync(message.arguments.body, (asyncResult) => {
            if (asyncResult.error) {
              vueInstance.gpgolLog("Error while trying to send email via EWS", {error: asyncResult.error, value: asyncResult.value, });
              return;
            }

            vueInstance.gpgolLog("Email sent", {value: asyncResult.value});
            // let the client known that the email was sent
            vueInstance.socket.send(JSON.stringify({
              command: 'ews-response',
              arguments: {
                requestId: message.arguments.requestId,
                email: Office.context.mailbox.userProfile.emailAddress,
                body: asyncResult.value,
              }
            }));
          });
          break;
        case 'viewer-closed':
        case 'viewer-opened':
          vueInstance.viewerOpen = message.command === 'viewer-opened';
          break;
        case 'disconnection':
          vueInstance.error = i18n("Native client was disconnected")
          break;
        case 'connection':
          vueInstance.error = '';
          break;
        case 'info-fetched':
          const { itemId, encrypted, signed, drafts, viewerOpen, version } = message.arguments;
          vueInstance.status.drafts = drafts;
          if (itemId === Office.context.mailbox.item.itemId) {
            vueInstance.status.fetched = true;
            vueInstance.status.encrypted = encrypted;
            vueInstance.status.signed = signed;
            vueInstance.viewerOpen = viewerOpen;

            if (vueInstance.viewerOpen) {
              vueInstance.view();
            }
            let params = new URLSearchParams(document.location.search);
            let manifestVersion = params.get("version");
            if (version !== manifestVersion) {
                vueInstance.versionWarning = i18nc("@info", "Version mitmatch. Make sure you installed the last manifest.xml.")
            }
          } else {
            vueInstance.status.fetched = false;
          }
        }
      });
    }
  },
  async mounted() {
    await this.downloadContent();

    Office.context.mailbox.addHandlerAsync(Office.EventType.ItemChanged, (eventArgs) => {
        if (Office.context.mailbox.item) {
            this.downloadContent();
        } else {
            this.content = '';
            this.hasSelection = false;
        }
    });

    this.webSocketConnect();
  },
}

Office.onReady().then(() => {
  createApp(vueApp).mount('#app')
  document.getElementById('app').classList.remove('d-none');
});
