const { get, isNone, run, isEmpty, makeArray } = Ember;
import computed from "ember-addons/ember-computed-decorators";
import UtilsMixin from "select-kit/mixins/utils";
import DomHelpersMixin from "select-kit/mixins/dom-helpers";
import EventsMixin from "select-kit/mixins/events";
import PluginApiMixin from "select-kit/mixins/plugin-api";
import {
  applyContentPluginApiCallbacks,
  applyHeaderContentPluginApiCallbacks,
  applyCollectionHeaderCallbacks
} from "select-kit/mixins/plugin-api";

export default Ember.Component.extend(
  UtilsMixin,
  PluginApiMixin,
  DomHelpersMixin,
  EventsMixin,
  {
    pluginApiIdentifiers: ["select-kit"],
    layoutName: "select-kit/templates/components/select-kit",
    classNames: ["select-kit"],
    classNameBindings: [
      "isLoading",
      "isFocused",
      "isExpanded",
      "isDisabled",
      "isHidden",
      "isAbove",
      "isBelow",
      "isLeftAligned",
      "isRightAligned",
      "hasSelection",
      "hasReachedMaximum",
      "hasReachedMinimum"
    ],
    isDisabled: false,
    isExpanded: false,
    isFocused: false,
    isHidden: false,
    isLoading: false,
    isAsync: false,
    renderedBodyOnce: false,
    renderedFilterOnce: false,
    tabindex: 0,
    none: null,
    highlighted: null,
    valueAttribute: "id",
    nameProperty: "name",
    autoFilterable: false,
    filterable: false,
    filter: "",
    previousFilter: "",
    filterIcon: "search",
    headerIcon: null,
    rowComponent: "select-kit/select-kit-row",
    rowComponentOptions: null,
    noneRowComponent: "select-kit/select-kit-none-row",
    createRowComponent: "select-kit/select-kit-create-row",
    filterComponent: "select-kit/select-kit-filter",
    headerComponent: "select-kit/select-kit-header",
    headerComponentOptions: null,
    headerComputedContent: null,
    collectionHeaderComputedContent: null,
    collectionComponent: "select-kit/select-kit-collection",
    verticalOffset: 0,
    horizontalOffset: 0,
    fullWidthOnMobile: false,
    castInteger: false,
    castBoolean: false,
    allowAny: false,
    allowInitialValueMutation: false,
    content: null,
    computedContent: null,
    limitMatches: null,
    nameChanges: false,
    allowContentReplacement: false,
    collectionHeader: null,
    allowAutoSelectFirst: true,
    highlightedSelection: null,
    maximum: null,
    minimum: null,
    minimumLabel: null,
    maximumLabel: null,

    init() {
      this._super();

      this.noneValue = "__none__";
      this.set("headerComponentOptions", Ember.Object.create());
      this.set("rowComponentOptions", Ember.Object.create());
      this.set("computedContent", []);
      this.set("highlightedSelection", []);

      if (this.get("nameChanges")) {
        this.addObserver(
          `content.@each.${this.get("nameProperty")}`,
          this,
          this._compute
        );
      }

      if (this.get("allowContentReplacement")) {
        this.addObserver(`content.[]`, this, this._compute);
      }

      if (this.get("isAsync")) {
        this.addObserver(`asyncContent.[]`, this, this._compute);
      }
    },

    keyDown(event) {
      if (!isEmpty(this.get("filter"))) return true;

      const keyCode = event.keyCode || event.which;

      if (event.metaKey === true && keyCode === this.keys.A) {
        this.didPressSelectAll();
        return false;
      }

      if (keyCode === this.keys.BACKSPACE) {
        this.didPressBackspace();
        return false;
      }
    },

    willDestroyElement() {
      this.removeObserver(
        `content.@each.${this.get("nameProperty")}`,
        this,
        this._compute
      );
      this.removeObserver(`content.[]`, this, this._compute);
      this.removeObserver(`asyncContent.[]`, this, this._compute);
    },

    willComputeAttributes() {},
    didComputeAttributes() {},

    willComputeContent(content) {
      return content;
    },
    computeContent(content) {
      return content;
    },
    _beforeDidComputeContent(content) {
      content = applyContentPluginApiCallbacks(
        this.get("pluginApiIdentifiers"),
        content,
        this
      );

      let existingCreatedComputedContent = [];
      if (!this.get("allowContentReplacement")) {
        existingCreatedComputedContent = this.get("computedContent").filterBy(
          "created",
          true
        );
      }

      this.setProperties({
        computedContent: content
          .map(c => this.computeContentItem(c))
          .concat(existingCreatedComputedContent)
      });
      return content;
    },
    didComputeContent() {},

    willComputeAsyncContent(content) {
      return content;
    },
    computeAsyncContent(content) {
      return content;
    },
    _beforeDidComputeAsyncContent(content) {
      content = applyContentPluginApiCallbacks(
        this.get("pluginApiIdentifiers"),
        content,
        this
      );
      this.setProperties({
        computedAsyncContent: content.map(c => this.computeAsyncContentItem(c))
      });
      return content;
    },
    didComputeAsyncContent() {},

    computeContentItem(contentItem, options) {
      let originalContent;
      options = options || {};
      const name = options.name;

      if (typeof contentItem === "string" || typeof contentItem === "number") {
        originalContent = {};
        originalContent[this.get("valueAttribute")] = contentItem;
        originalContent[this.get("nameProperty")] = name || contentItem;
      } else {
        originalContent = contentItem;
      }

      let computedContentItem = {
        value: this._cast(this.valueForContentItem(contentItem)),
        name: name || this._nameForContent(contentItem),
        locked: false,
        created: options.created || false,
        __sk_row_type: options.created ? "createRow" : null,
        originalContent
      };

      return computedContentItem;
    },

    computeAsyncContentItem(contentItem, options) {
      return this.computeContentItem(contentItem, options);
    },

    @computed(
      "isAsync",
      "isLoading",
      "filteredAsyncComputedContent.[]",
      "filteredComputedContent.[]"
    )
    collectionComputedContent(
      isAsync,
      isLoading,
      filteredAsyncComputedContent,
      filteredComputedContent
    ) {
      if (isAsync) {
        return isLoading ? [] : filteredAsyncComputedContent;
      } else {
        return filteredComputedContent;
      }
    },

    validateCreate(created) {
      return !this.get("hasReachedMaximum") && created.length > 0;
    },

    validateSelect() {
      return !this.get("hasReachedMaximum");
    },

    @computed("maximum", "selection.[]")
    hasReachedMaximum(maximum, selection) {
      if (!maximum) return false;
      selection = makeArray(selection);
      return selection.length >= maximum;
    },

    @computed("minimum", "selection.[]")
    hasReachedMinimum(minimum, selection) {
      if (!minimum) return true;
      selection = makeArray(selection);
      return selection.length >= minimum;
    },

    @computed("shouldFilter", "allowAny")
    shouldDisplayFilter(shouldFilter, allowAny) {
      if (shouldFilter) return true;
      if (allowAny) return true;
      return false;
    },

    @computed("filter", "collectionComputedContent.[]", "isLoading")
    noContentRow(filter, collectionComputedContent, isLoading) {
      if (
        filter.length > 0 &&
        collectionComputedContent.length === 0 &&
        !isLoading
      ) {
        return I18n.t("select_kit.no_content");
      }
    },

    @computed("hasReachedMaximum", "hasReachedMinimum", "isExpanded")
    validationMessage(hasReachedMaximum, hasReachedMinimum) {
      if (hasReachedMaximum && this.get("maximum")) {
        const key =
          this.get("maximumLabel") || "select_kit.max_content_reached";
        return I18n.t(key, { count: this.get("maximum") });
      }

      if (!hasReachedMinimum && this.get("minimum")) {
        const key =
          this.get("minimumLabel") || "select_kit.min_content_not_reached";
        return I18n.t(key, { count: this.get("minimum") });
      }
    },

    @computed("allowAny")
    filterPlaceholder(allowAny) {
      return allowAny
        ? "select_kit.filter_placeholder_with_any"
        : "select_kit.filter_placeholder";
    },

    @computed("filter", "filterable", "autoFilterable", "renderedFilterOnce")
    shouldFilter(filter, filterable, autoFilterable, renderedFilterOnce) {
      if (renderedFilterOnce && filterable) return true;
      if (filterable) return true;
      if (autoFilterable && filter.length > 0) return true;
      return false;
    },

    @computed(
      "computedValue",
      "filter",
      "collectionComputedContent.[]",
      "hasReachedMaximum",
      "isLoading"
    )
    shouldDisplayCreateRow(
      computedValue,
      filter,
      collectionComputedContent,
      hasReachedMaximum,
      isLoading
    ) {
      if (isLoading || hasReachedMaximum) return false;
      if (collectionComputedContent.map(c => c.value).includes(filter))
        return false;
      if (this.get("allowAny") && this.validateCreate(filter)) return true;
      return false;
    },

    @computed("filter", "shouldDisplayCreateRow")
    createRowComputedContent(filter, shouldDisplayCreateRow) {
      if (shouldDisplayCreateRow) {
        let content = this.createContentFromInput(filter);
        let computedContentItem = this.computeContentItem(content, {
          created: true
        });
        computedContentItem.__sk_row_type = "createRow";
        return computedContentItem;
      }
    },

    @computed
    templateForRow() {
      return () => null;
    },

    @computed
    templateForNoneRow() {
      return () => null;
    },

    @computed("filter")
    templateForCreateRow() {
      return rowComponent => {
        return I18n.t("select_kit.create", {
          content: rowComponent.get("computedContent.name")
        });
      };
    },

    @computed("none")
    noneRowComputedContent(none) {
      if (isNone(none)) return null;

      let noneRowComputedContent;

      switch (typeof none) {
        case "string":
          noneRowComputedContent = this.computeContentItem(this.noneValue, {
            name: (I18n.t(none) || "").htmlSafe()
          });
          break;
        default:
          noneRowComputedContent = this.computeContentItem(none);
      }

      noneRowComputedContent.__sk_row_type = "noneRow";

      return noneRowComputedContent;
    },

    createContentFromInput(input) {
      return input;
    },

    highlightSelection(items) {
      this.propertyWillChange("highlightedSelection");
      this.set("highlightedSelection", makeArray(items));
      this.propertyDidChange("highlightedSelection");
    },

    clearHighlightSelection() {
      this.highlightSelection([]);
    },

    willSelect() {},
    didSelect() {},

    willCreate() {},
    didCreate() {},

    willDeselect() {},
    didDeselect() {},

    clearFilter() {
      this.$filterInput().val("");
      this.setProperties({ filter: "", previousFilter: "" });
    },

    startLoading() {
      this.set("isLoading", true);
      this.set("highlighted", null);
      this._boundaryActionHandler("onStartLoading");
    },

    stopLoading() {
      if (this.site && !this.site.isMobileDevice) {
        this.focusFilterOrHeader();
      }

      this.set("isLoading", false);
      this._boundaryActionHandler("onStopLoading");
    },

    @computed("selection.[]", "isExpanded", "filter", "highlightedSelection.[]")
    collectionHeaderComputedContent() {
      return applyCollectionHeaderCallbacks(
        this.get("pluginApiIdentifiers"),
        this.get("collectionHeader"),
        this
      );
    },

    @computed("selection.[]", "isExpanded", "headerIcon")
    headerComputedContent() {
      return applyHeaderContentPluginApiCallbacks(
        this.get("pluginApiIdentifiers"),
        this.computeHeaderContent(),
        this
      );
    },

    _boundaryActionHandler(actionName, ...params) {
      if (get(this.actions, actionName)) {
        run.next(() => this.send(actionName, ...params));
      } else if (this.get(actionName)) {
        run.next(() => this.get(actionName)(...params));
      }
    },

    highlight(computedContent) {
      this.set("highlighted", computedContent);
      this._boundaryActionHandler("onHighlight", computedContent);
    },

    clearSelection() {
      this.deselect(this.get("selection"));
      this.focusFilterOrHeader();
    },

    actions: {
      onToggle() {
        this.clearHighlightSelection();

        if (this.get("isExpanded")) {
          this.collapse();
        } else {
          this.expand();
        }
      },

      onClickRow(computedContentItem) {
        this.didClickRow(computedContentItem);
      },

      onClickSelectionItem(computedContentItem) {
        this.didClickSelectionItem(computedContentItem);
      },

      onClearSelection() {
        this.clearSelection();
      },

      onMouseoverRow(computedContentItem) {
        this.highlight(computedContentItem);
      },

      onFilterComputedContent(filter) {
        if (filter === this.get("previousFilter")) return;

        this.clearHighlightSelection();

        this.setProperties({
          highlighted: null,
          renderedFilterOnce: true,
          previousFilter: filter,
          filter
        });
        this.autoHighlight();
        this._boundaryActionHandler("onFilter", filter);
      }
    }
  }
);
