import { ILinkHandler } from './LinkHandler.interface';
import { IJID } from './JID.interface';
import JID from './JID';
import Client from './Client';
import { IContact } from './Contact.interface';
import Contact from './Contact';
import showMultiUserJoinDialog from './ui/dialogs/multiUserJoin';
import Log from '@util/Log';

export default class LinkHandlerXMPP implements ILinkHandler {
   private static instance: LinkHandlerXMPP;

   public static get(): LinkHandlerXMPP {
      if (!LinkHandlerXMPP.instance) {
         LinkHandlerXMPP.instance = new LinkHandlerXMPP();
      }

      return LinkHandlerXMPP.instance;
   }

   private constructor() {}

   public execute(uri: string) {
      if (!uri.startsWith('xmpp:')) {
         throw new Error('Uri has to start with xmpp:');
      }

      let payload = uri.replace(/^xmpp:/, '');
      let { jid, action, params } = this.parsePayload(payload);

      if (typeof this.queryActions[action] !== 'function') {
         throw new Error('Unsupported action');
      }

      return this.queryActions[action](jid, params);
   }

   public detect(element: JQuery = $('body')) {
      let self = this;

      element.find('a[href^="xmpp:"]').each(function () {
         self.processElement($(this));
      });
   }

   private processElement(element: JQuery) {
      let payload = element.attr('href').replace(/^xmpp:/, '');
      let { jid, action, params } = this.parsePayload(payload);

      if (typeof this.queryActions[action] === 'function') {
         element.addClass('jsxc-xmpp-scheme jsxc-xmpp-scheme--' + action);

         element.off('click').click(ev => {
            ev.stopPropagation();
            ev.preventDefault();

            this.queryActions[action](jid, params);
         });

         //@TODO add presence info
      }
   }

   private parsePayload(href: string) {
      let jidString = href.split('?')[0];
      let action: string;
      let params = {};

      if (!/[^"&'\\\/:<>@\s]+@[\w-_.]+/.test(jidString)) {
         return {};
      }

      let jid = new JID(jidString);

      if (href.indexOf('?') < 0) {
         action = 'message';
      } else {
         let parts = href.substring(href.indexOf('?') + 1).split(';');
         action = parts[0];

         for (let i = 1; i < parts.length; i++) {
            let key = parts[i].split('=')[0];
            let value = parts[i].indexOf('=') > 0 ? parts[i].substring(parts[i].indexOf('=') + 1) : null;

            params[decodeURIComponent(key)] = decodeURIComponent(value);
         }
      }

      return {
         jid,
         action,
         params,
      };
   }

   private queryActions = {
      /** xmpp:JID?message[;body=TEXT] */
      message: (jid: IJID, params?: { body: string }) => {
         let contact = this.searchContact(jid);

         if (typeof contact === 'undefined') {
            Log.info('Can not execute query action, because no account is connected');
            return false;
         }

         if (!contact) {
            let account = Client.getAccountManager().getAccount();

            contact = new Contact(account, jid);
            account.getContactManager().addToCache(contact);
         }

         contact.getChatWindowController().openProminently();

         if (params?.body) {
            contact.getChatWindow().appendTextToInput(params?.body);
         }

         return true;
      },

      /** xmpp:JID?join[;password=TEXT] */
      join: (jid: IJID, params?: { password: string }) => {
         let contact = this.searchContact(jid);

         if (typeof contact === 'undefined') {
            Log.info('Can not execute query action, because no account is connected');
            return false;
         }

         if (!contact) {
            showMultiUserJoinDialog(jid.domain, jid.node);
         } else {
            this.queryActions.message(jid);
         }

         return true;
      },
   };

   private searchContact(jid: IJID): IContact {
      let accounts = Client.getAccountManager().getAccounts();

      if (accounts.length === 0) {
         return undefined;
      }

      for (let account of accounts) {
         let contact = account.getContact(jid);

         if (contact) {
            return contact;
         }
      }

      return null;
   }
}
