// Licensed to Cloudera, Inc. under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  Cloudera, Inc. licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import $ from 'jquery';
import * as ko from 'knockout';

import componentUtils from './componentUtils';
import dataCatalog from '../../catalog/dataCatalog';
import huePubSub from '../../utils/huePubSub';
import I18n from '../../utils/i18n';

export const NAME = 'nav-tags';

const TEMPLATE = `
  <!-- ko if: loading -->
  <div style="width: 100%; height: 20px; left: 6px; position: relative;" data-bind="hueSpinner: { spin: loading }"></div>
  <!-- /ko -->
  <!-- ko if: !loading() && (!readOnly || readOnly && currentTags().length) -->
  <div class="hue-tags" style="width: 100%" data-bind="ifnot: loading, css: { 'read-only-tags': readOnly }">
   <textarea style="width: 100%" data-bind="tagEditor: {
      emptyPlaceholder: '${I18n('Add tags...')}',
      readOnly: readOnly,
      hasErrors: hasErrors,
      errorMessage: '${I18n('Tags could not be loaded.')}',
      setTags: currentTags,
      overflowEllipsis: overflowEllipsis,
      onSave: saveTags.bind($data),
      validRegExp: '^[a-zA-z0-9_\-]{1,50}$',
      invalidMessage: '${I18n('Tags can only contain 1 to 50 alphanumeric characters, _ or -.')}',
      load: getSelectizeTags
    }"></textarea>
   <div class="selectize-error" style="display: none;"><i class="fa fa-exclamation-triangle"></i> <span class="message"></span></div>
  </div>
  <!-- /ko -->
`;

class NavTags {
  /**
   * @param {object} params
   * @param {DataCatalogEntry} [params.catalogEntry]
   *
   * @constructor
   */
  constructor(params) {
    const self = this;

    self.hasErrors = ko.observable(false);
    self.loading = ko.observable(true);

    self.currentTags = ko.observableArray();
    self.allTags = ko.observableArray();

    self.catalogEntry = params.catalogEntry;
    self.overflowEllipsis = params.overflowEllipsis;
    self.readOnly =
      !window.USER_HAS_METADATA_WRITE_PERM || !!params.readOnly || window.HAS_READ_ONLY_CATALOG;

    self.getSelectizeTags = function (query, callback) {
      callback(
        $.map(self.allTags(), tag => {
          return { value: tag, text: tag };
        })
      );
    };

    self.loadTags();

    self.refreshSub = huePubSub.subscribe('data.catalog.entry.refreshed', details => {
      if (details.entry === self.catalogEntry) {
        self.loadTags();
      }
    });
  }

  dispose() {
    const self = this;
    self.refreshSub.remove();
  }

  loadTags() {
    const self = this;
    self.loading(true);
    self.hasErrors(false);

    const currentTagsPromise = ko
      .unwrap(self.catalogEntry)
      .getNavigatorMeta()
      .then(navigatorMeta => {
        self.currentTags((navigatorMeta && navigatorMeta.tags) || []);
      })
      .catch(() => {
        self.hasErrors(true);
      });

    const allTagsPromise = dataCatalog
      .getAllNavigatorTags({ silenceErrors: true })
      .then(tagList => {
        self.allTags(Object.keys(tagList));
      })
      .catch(() => {
        self.allTags([]);
      });

    Promise.all([currentTagsPromise, allTagsPromise]).finally(() => {
      self.loading(false);
    });
  }

  saveTags(value) {
    const self = this;
    const newTags = value.length > 0 ? value.split(',') : [];
    const tagsToRemove = [];
    const tagsToAdd = [];

    const tagIndex = {};
    self.currentTags().forEach(tag => {
      tagIndex[tag] = false;
    });
    newTags.forEach(newTag => {
      if (typeof tagIndex[newTag] !== 'undefined') {
        tagIndex[newTag] = true;
      } else {
        tagsToAdd.push(newTag);
      }
    });
    Object.keys(tagIndex).forEach(oldTag => {
      if (!tagIndex[oldTag]) {
        tagsToRemove.push(oldTag);
      }
    });

    self.loading(true);
    self.hasErrors(false);

    const addTagsPromise =
      tagsToAdd.length > 0
        ? ko.unwrap(self.catalogEntry).addNavigatorTags(tagsToAdd)
        : Promise.resolve();

    const deleteTagsPromise =
      tagsToRemove.length > 0
        ? ko.unwrap(self.catalogEntry).deleteNavigatorTags(tagsToRemove)
        : Promise.resolve();

    Promise.all([addTagsPromise, deleteTagsPromise])
      .then(() => {
        if (tagsToAdd.length || tagsToRemove.length) {
          dataCatalog.updateAllNavigatorTags(tagsToAdd, tagsToRemove);
          ko.unwrap(self.catalogEntry).save();
        }
        self.loadTags();
      })
      .catch(() => {
        self.hasErrors(true);
      })
      .finally(() => {
        self.loading(false);
      });
  }
}

componentUtils.registerComponent(NAME, NavTags, TEMPLATE);
