let Widget, Composition, Content, Skin;
import EmbedApi from './api';
import toast from 'pyrsmk-toast';

const send = message => {
  (window.opener || window.parent).postMessage(JSON.stringify(message), '*');
};

const messenger = {
  load: () => send({ t: 'u', d: window.name }),
  apiReady: () => send({ t: 'd', d: window.name }),
  notify: (ev, data) =>
    send({ t: 'ce', id: window.name, d: data, e: ev.type || ev }),
};

const _newComposition = widget => {
  const id = 'c-0';
  return {
    id,
    widget_id: widget.id,
    width: widget.width,
    height: widget.height,
    skin_id: widget.skins[0].id,
    content: widget.content[0].content.map(function(c, i) {
      c.id = i;
      c.composition_id = id;
      c._isDemoContent = true;
      return c;
    }),
    metadata: {
      client: (window.location.search.match(/client_id=([^\&$]+)/) || [])[1],
    },
  };
};

// TODO relay mouse and keyboard events - maybe not?
// TODO editor integration
export default class EmbedController {
  constructor(
    el,
    {
      loader,
      widget,
      user,
      composition,
      skin,
      js,
      css,
      data,
      bootstrapped = false,
    }
  ) {
    this.el = el;

    this.loader = loader;

    this.bootstrapped = bootstrapped;

    if (!widget && !data)
      throw new Error('You must supply either `widget` or `data`');
    this.widget = widget;
    this.dataURL = data;
    this.user = user;
    this.composition = composition;
    this.skin = skin;

    this.js = 'https://widgetic.com' + js;
    this.css = 'https://widgetic.com' + css;

    this.dataLoaded = Widgetic.Aye.defer();
    this.dataLoaded.promise.then(this._showBranding.bind(this));
    this._init();
    this._initEditorApi();
  }

  _init() {
    const wait = () => {};
    if (!this.bootstrapped) {
      toast(
        // load shims, css and libraries in paralel
        'https://gitcdn.xyz/repo/zloirock/core-js/v2.4.1/client/shim.min.js',
        '/bundles/blogvioapp/js/editor/core.vendor.js',
        '/bundles/blogvioapp/js/require.js',
        '/bundles/blogvioapp/css/embed.css',
        this.css,
        wait,
        // load spine next, depends on require.js
        '/bundles/blogvioapp/js/spine.js',
        wait,
        // load widgetic code, depends on spine
        '/bundles/blogvioapp/js/editor/core.js',
        this._loadModels.bind(this),
        this._loadData.bind(this),
        this.js,
        // load the widget, depends on hydrated models
        () => this.dataLoaded.promise.then(this._loadWidget.bind(this))
      );
    } else {
      this._loadModels();
      this._loadData();
      this._loadWidget();
    }
  }

  _loadModels() {
    Widget = window.require('core/models/widget');
    Composition = window.require('core/models/composition');
    Content = window.require('core/models/content');
    Skin = window.require('core/models/skin');
  }

  _loadData() {
    // if the data is local, proceed to hydrating the models
    if (!this.dataURL) return this._hydrateModels();

    Widgetic.uxhr(this.dataURL, null, {
      success: response => {
        let data;
        try {
          data = JSON.parse(response);
        } catch (e) {
          return console.error('Could not parse response', response);
        }
        this.skin = data.skin;
        this.composition = data.composition;
        this.widget = data.widget;
        this.user = data.user;
        this._hydrateModels();
      },
      error: error =>
        console.error('The widget data could not be loaded', error),
    });
  }

  _hydrateModels() {
    this.composition = this.composition || _newComposition(this.widget);
    Widget.refresh(this.widget);
    Composition.refresh(this.composition);
    Skin.refresh(this.skin || this.widget.skins);
    Content.refresh(this.composition.content);
    this.dataLoaded.resolve();

    this._initApi();
  }

  _initApi() {
    const api = new EmbedApi();
    Widgetic.Event.on(window, 'message', api.receiver);

    api.skinSet.listen(this._onSkinSet.bind(this));
    api.contentCleared.listen(this._onContentCleared.bind(this));
    api.contentAdded.listen(this._onContentAdded.bind(this));

    messenger.apiReady();
  }

  _initEditorApi() {
    window.getPreview = () => ({
      widget: () => this.widgetController,
      window,
    });

    window.getData = () => ({
      widget: this.widget,
      composition: $.extend({}, this.composition, {
        widget_id: this.widget.id,
        width: (this.composition || this.widget).width,
        height: (this.composition || this.widget).height,
        content: this.composition.content,
        skin_id: this.composition.skin_id,
      }),
      skin: this.skin || this.widget.skins[0],
      user: this.user,
    });

    // used by weebly editor
    // TODO remove
    // @deprecated
    window.getCurrentData = () => ({ widget: this.widgetController });

    // used by weebly editor
    // TODO remove
    const access_token = (window.location.search.match(
      /access_token=([^\&$]+)/
    ) || [])[1];
    if (access_token) Widgetic.auth.token(access_token);
  }

  _loadWidget() {
    const uri = Widgetic.parse(window.location.href);
    const mode = uri.queryKey.edit_mode !== undefined
      ? Widgetic.UI.Composition.EDIT_MODE
      : Widgetic.UI.Composition.EMBED_MODE;
    const metadata = {
      mode,
      embedOptions: {
        resize: uri.queryKey.resize,
        autoscale: uri.queryKey.autoscale,
      },
      widget: this.widget,
      composition: {
        id: this.composition.id,
      },
      referrer: document.referrer,
    };

    const _widget = window.require(this.widget.module);
    this.widgetController = new _widget(
      { metadata },
      Skin.first(),
      Content.all()
    );

    this.loader && this.loader.parentNode.removeChild(this.loader);

    // TODO replace jquery use with native DOM
    const widgetContainer = $(
      '<div class="widget-container" style="width: 100%; height: 100%">'
    );
    $(this.el).append(widgetContainer);
    widgetContainer.append(this.widgetController.el);
    this.widgetController.added();
    messenger.load();
  }

  _showBranding() {
    if (!this.user.premium || window.location.search.indexOf('branding') >= 0) {
      window.initBranding();
    }
  }

  _onSkinSet(skin) {
    if (!this.widgetController) return;
    this.widgetController.setSkin(skin);
  }

  _onContentCleared() {
    if (!this.widgetController) return;
    this.widgetController.clearContent();
  }

  _onContentAdded(item) {
    if (!this.widgetController) return;
    this.widgetController.addContent(item);
  }
}
