import utils from './utils';
import axios from 'axios';
import Mercury from '@postlight/mercury-parser';

global.browser = require('webextension-polyfill');

const app = {
  hosts: {},
  actions: {},
  scripts: {},
  categories: {},
  separator: '//',

  categoriesByHost(host) {
    return Object.entries({ ...(this.actions || {}), ...(this.scripts || {}) }).reduce((obj, [actionName, action]) => {
      if (action.hosts.indexOf(host) < 0) {
        return obj;
      }

      const appendAction = category => {
        if (!(category in obj)) {
          obj[category] = {};
        }

        obj[category][actionName] = action;
      };

      if (!(action.categories && action.categories.length)) {
        appendAction('');
      } else {
        action.categories.forEach(category => appendAction(category));
      }

      return obj;
    }, {});
  },

  async refresh() {
    this.hosts = await utils.methods.getHosts();
    this.actions = await utils.methods.getActions();
    this.scripts = await utils.methods.getScripts();
    await browser.contextMenus.removeAll();

    for (const [host] of Object.entries(this.hosts)) {
      const hostId = this.separator + host;
      browser.contextMenus.create({
        id: hostId,
        title: host,
      });

      const categories = this.categoriesByHost(host);
      for (const [categoryName, category] of Object.entries(categories)) {
        const categoryId = hostId + this.separator + (categoryName.length ? categoryName : '[NONE]');
        browser.contextMenus.create({
          id: categoryId,
          parentId: hostId,
          title: categoryName.length ? categoryName : '[No Category]',
        });

        for (const [action] of Object.entries(category)) {
          const actionId = categoryId + this.separator + action;
          browser.contextMenus.create({
            id: actionId,
            parentId: categoryId,
            title: action,
          });
        }
      }
    }

    browser.contextMenus.onClicked.addListener(async (info, tab) => {
      const [host, , action] = info.menuItemId.split(this.separator).slice(1);
      const target = await utils.methods.getTargetElement();

      if (action in this.actions) {
        await utils.methods.run(this.actions[action], this.hosts[host]);
      } else {
        await utils.methods.runScript(this.scripts[action].script, this.hosts[host], tab, target);
      }
    });

    browser.runtime.onConnect.addListener(port => {
      switch (port.name) {
        case 'action':
          port.onMessage.addListener(async message => {
            let ret = null;
            switch (message.type) {
              case 'run':
                ret = await utils.methods.run(message.action, message.host);
                port.postMessage(ret);
                break;
            }
          });

          break;

        case 'url':
          port.onMessage.addListener(async message => {
            const tab = await utils.methods.getCurrentTab();
            switch (message.type) {
              case 'get':
                port.postMessage(tab.url);
                break;

              case 'set':
                await browser.tabs.sendMessage(tab.id, { type: 'setURL', url: message.url }, {});
                break;

              case 'open':
                await browser.tabs.create({
                  url: message.url,
                });
                break;
            }
          });

          break;

        case 'dom':
          port.onMessage.addListener(async message => {
            const tab = await utils.methods.getCurrentTab();
            let dom = null;

            switch (message.type) {
              case 'get':
                dom = await browser.tabs.sendMessage(tab.id, { type: 'getDOM' }, {});
                port.postMessage(dom);
                break;

              case 'set':
                await browser.tabs.sendMessage(tab.id, { type: 'setDOM', html: message.html }, {});
                break;
            }
          });

          break;

        case 'notify':
          port.onMessage.addListener(async message => {
            switch (message.type) {
              case 'run':
                utils.methods.notify(message.message, message.title, message.error);
                break;
            }
          });

          break;

        case 'axios':
          port.onMessage.addListener(async message => {
            const method = axios[message.type.toLowerCase()];
            const response = await method(message.url, ...message.args);
            port.postMessage({
              config: {
                data: response.config.data,
                headers: response.config.headers,
                maxContentLength: response.config.maxContentLength,
                method: response.config.method,
                timeout: response.config.timeout,
                url: response.config.url,
                xsrfCookieName: response.config.xsrfCookieName,
                xsrfHeaderName: response.config.xsrfHeaderName,
              },
              headers: response.headers,
              data: response.data,
              status: response.status,
              statusText: response.statusText,
            });
          });

          break;

        case 'mercury':
          port.onMessage.addListener(async message => {
            let response = null;
            switch (message.type) {
              case 'parse':
                response = await Mercury.parse(message.url, {
                  contentType: 'html',
                  html: message.html,
                });

                port.postMessage(response);
                break;
            }
          });

          break;
      }
    });
  },

  async create() {
    await this.refresh();
  },
};

const onCreate = () => {
  app.create().then(() => {
    console.debug('Extension context created');
  });
};

onCreate();

// vim:sw=2:ts=2:et: