import Component from '@ember/component';
import ModalBase from 'shared/mixins/modal-base';
import layout from './template';
import { get, set, computed, setProperties } from '@ember/object';
import { inject as service } from '@ember/service';
import { loadScript, loadStylesheet, proxifyUrl } from 'shared/utils/load-script';
import { empty } from '@ember/object/computed';
import { allSettled, resolve } from 'rsvp';
import Preload from 'ui/mixins/preload';
import C from 'ui/utils/constants';

export default Component.extend(ModalBase, Preload, {
  growl:             service(),
  modalService:      service('modal'),
  globalStore:       service(),
  intl:              service(),

  layout,
  model:             null,
  driver:            null,
  onAdd:             null,

  loading:           true,
  editing:           false,
  cloning:           false,
  allNodeDrivers:    null,

  needReloadSchema:  false,
  reloadingSchema:   false,
  schemaReloaded:    false,
  customDrivers:     null,
  rejectedDriverIds: null,

  showPicker: empty('model.id'),

  didReceiveAttrs() {
    const opts          = get(this, 'modalService.modalOpts') || {};
    const originalModel = get(opts, 'nodeTemplate');
    let { driver }      = this;
    let override        = get(opts, 'driver');
    let newModel        = null;
    let editing         = false;
    let cloning         = false;

    if (get(opts, 'edit')) {
      newModel = originalModel.clone();
      editing  = true;
    } else if (get(opts, 'clone')) {
      newModel = originalModel.cloneForNew();
      editing  = true;
      cloning  = true;
    }


    if ( originalModel ) {
      setProperties(this, {
        model: newModel,
        originalModel,
        editing,
        cloning,
      })
    } else {
      set(this, 'model', get(this, 'globalStore').createRecord({ type: 'nodeTemplate', }));
    }

    if ( override ) {
      driver = override;

      set(this, 'showPicker', false);
    }

    set(this, 'onAdd', get(opts, 'onAdd'));

    // need to reload the schemas to fetch any new custom node template schemas so they are available for the availableDrivers
    return this.loadSchemas('globalStore').then(() => {
      return get(this, 'globalStore').findAll('nodeDriver').then((allNodeDrivers) => {
        set(this, 'allNodeDrivers', allNodeDrivers);

        const active = get(this, 'availableDrivers');

        if ( !driver || !active.findBy('name', driver) ) {
          driver = get(active, 'firstObject.name');
        }

        set(this, 'driver', driver);

        return this.loadCustomUi().then(() => {
          set(this, 'loading', false);
        })
      });
    });
  },

  actions: {
    switchDriver(name) {
      set(this, 'driver', name);
    },

    saved() {
      const fn    = get(this, 'onAdd');
      const model = get(this, 'model');

      if ( fn ) {
        fn(model);
      }
    },

    hidePicker() {
      this.set('showPicker', false);
    },
  },

  availableDrivers: computed('allNodeDrivers.@each.state', 'rejectedDriverIds.[]', function() {
    const out     = [];
    const rejectedDriverIds = get(this, 'rejectedDriverIds') || [];
    const drivers = get(this, 'allNodeDrivers').filter((driver) => {
      if (get(driver, 'state') === 'active' && !rejectedDriverIds.includes(get(driver, 'id'))) {
        return driver;
      }
    }).sortBy('name');

    drivers.forEach((driver) => {
      const configName   = `${ get(driver, 'name') }Config`;
      const driverSchema = get(this, 'globalStore').getById('schema', configName.toLowerCase());

      if ( driverSchema ) {
        out.push(driver);
      }
    });

    return out;
  }),

  availableDriversGroups: computed('availableDrivers.@each.state', function() {
    const { availableDrivers } = this;
    const group                = [];
    let groupIndex             = 0;

    availableDrivers.forEach((item, index) => {
      if (index % 5 === 0) {
        group.push([item]);
        groupIndex++;
      } else {
        group[groupIndex - 1].push(item);
      }
    });

    return group;
  }),

  driverObj: computed('availableDrivers', 'driver', function() {
    return get(this, 'availableDrivers').filterBy('name', get(this, 'driver'))[0];
  }),

  uiFieldHints: computed('driverObj.annotations.[]', function() {
    const uiFieldHints = get(this, 'driverObj.annotations')[C.LABEL.UI_HINTS] || null;

    if (uiFieldHints) {
      return JSON.parse(uiFieldHints);
    } else {
      return {};
    }
  }),

  // Loads the custom UI CSS/JS for drivers that have a uiUrl,
  loadCustomUi() {
    const promises = [];

    let customDrivers = (get(this, 'customDrivers') || []);

    get(this, 'availableDrivers').forEach((driver) => {
      const name = get(driver, 'name');
      const uiUrl = get(driver, 'uiUrl');

      if ( uiUrl ) {
        if (!customDrivers.includes(name)) {
          customDrivers.pushObject(name);
        }
        const jsUrl  = proxifyUrl(driver.uiUrl, this.get('app.proxyEndpoint'));
        const cssUrl = proxifyUrl(driver.uiUrl.replace(/\.js$/, '.css'), this.get('app.proxyEndpoint'));

        promises.push(loadScript(jsUrl, `driver-ui-js-${ name }`));
        promises.push(loadStylesheet(cssUrl, `driver-ui-css-${ name }`));
      }
    });

    set(this, 'customDrivers', customDrivers);

    return allSettled(promises).then((results) => {
      let rejectedResutls = results.filterBy('state', 'rejected');

      if (rejectedResutls.length >= 1) {
        let rejectedIds = rejectedResutls.map((rej) => {
          return ( get(rej, 'reason.srcElement.id') || '').split('-').get('lastObject')
        }).compact().uniq();

        set(this, 'rejectedDriverIds', rejectedIds);

        rejectedIds.forEach((reject) => {
          this.growl.fromError(this.intl.t('nodeDriver.externalError', { driverName: reject }));
        });
      }

      resolve();
    });
  },

});
