<template>
  <div ref="productSelectorContainer">
    <TfPopover
      v-model="isActive"
      v-click-outside="hidePopover"
      :width="popperWidth"
      :visible-arrow="false"
      :border-color="themePrimaryColor || null"
      popper-class="product-select-popover"
      placement="bottom-start"
      trigger="manual"
    >
      <span
        slot="reference"
        :class="[{ 'is-active': isActive }, 'product-selector-input']"
        :style="{ borderColor: inputBorderColor || null }"
        @click="isActive = !isActive"
      >
        <span
          class="product-selector-label"
          data-test="selected product"
          v-text="label.join('; ')"
        />
        <span
          v-if="showSelectedCount"
          class="product-selector-counter"
          v-text="`(${value.length})`"
        />
        <span
          :class="[
            { 'is-fixed': fixedToggle },
            'product-selector-toggle',
          ]"
        >
          <icon
            :class="{ 'is-active': isActive }"
            :style="{ fill: isActive ? themePrimaryColor : null }"
            name="caret-down"
          />
        </span>
      </span>
      <ElCollapse
        v-for="(category, index) in productTypesByCategory"
        :key="`category-${index}`"
        v-model="openCategoryIds"
        @change="toggleCategory(index)"
      >
        <ElCollapseItem
          class="container-category"
          :name="index"
        >
          <span
            v-if="showCategoryTitles"
            slot="title"
            class="title-category"
            :style="{
              color: openCategoryIds.includes(index) && themePrimaryColor
                ? themePrimaryColor
                : null,
              backgroundColor: hoverIndex === index && themeHoverColor
                ? themeHoverColor
                : null,
            }"
            @mouseover="hoverIndex = index"
            @mouseout="hoverIndex = null"
          >
            {{ category.label }}
            <icon
              :style="{
                fill: openCategoryIds.includes(index) && themePrimaryColor
                  ? themePrimaryColor
                  : null,
              }"
              :class="[
                { 'is-active': openCategoryIds.includes(index) },
                'category-toggle',
              ]"
              name="chevron-down"
            />
          </span>
          <ProductType
            v-for="productTypeId in category.productTypeIds"
            :key="`product-type-${productTypeId}`"
            v-model="openProductTypeIds"
            :product-type="productsByTypeTree[productTypeId]"
            @hidePopover="hidePopover"
          >
            <!-- Used to control appended icons in component using <TfProductSelector /> -->
            <template
              slot="productOptionAppend"
              slot-scope="product"
            >
              <slot
                name="productOptionAppend"
                :product="product.product"
              />
            </template>
            <template
              slot="productTypeAppend"
              slot-scope="productTypeData"
            >
              <slot
                name="productTypeAppend"
                :product-type="productTypeData.productType"
              />
            </template>
          </ProductType>
        </ElCollapseItem>
      </ElCollapse>
    </TfPopover>
  </div>
</template>

<script>
  import ProductType from './ProductType';
  /**
   * The Project Product Product selector for passed products.
   *
   * @vuedoc
   * @exports ProductSelector/index
   * @category Components
   */
  export default {
    name: 'TfProductSelector',
    components: { ProductType },
    provide() {
      return {
        TfProductSelector: this,
      };
    },
    props: {
      allowMultiple: {
        type: Boolean,
        default: false,
      },
      categories: {
        type: Array,
        default: () => ([]),
      },
      collapsedCategories: {
        type: Array,
        default: () => ([]),
      },
      collapsedProductTypes: {
        type: Array,
        default: () => ([]),
      },
      disabledProducts: {
        type: Array,
        default: () => ([]),
      },
      fixedToggle: {
        type: Boolean,
        default: true,
      },
      inputBorderColor: {
        type: String,
        default: null,
      },
      keepOpen: {
        type: Boolean,
        default: false,
      },
      products: {
        type: Array,
        required: true,
        default: () => ([]),
      },
      showCategoryTitles: {
        type: Boolean,
        default: true,
      },
      showSelectedCount: {
        type: Boolean,
        default: false,
      },
      themePrimaryColor: {
        type: String,
        default: null,
      },
      themeHoverColor: {
        type: String,
        default: null,
      },
      value: {
        type: Array,
        required: true,
        default: () => ([]),
      },
      width: {
        type: Number,
        default: null,
      },
    },
    data: () => ({
      hoverIndex: null,
      isActive: false,
      openCategoryIds: [],
      openProductTypeIds: [],
      popperWidth: null,
    }),
    computed: {
      /**
       * Creates an array of all the product labels or "placeholder" if nothing is selected.
       *
       * @returns {Array}
       */
      label() {
        return this.value.length && this.products.length
          ? this.value.map((productId) => {
            const selectedProduct = this.products.find((product) => product.id === productId);

            if (!selectedProduct) {
              return ['No product found'];
            }

            const typeIsNotLabel = selectedProduct.product_type_name !== selectedProduct.label;

            return selectedProduct.alternative || typeIsNotLabel
              ? `${selectedProduct.product_type_name}, ${selectedProduct.label}`
              : selectedProduct.label;
          })
          : ['Select a Product'];
      },
      /**
       * Creates an object of all the products types with their products as children.
       *
       * @returns {object}
       */
      productsByTypeTree() {
        const productTypes = {};
        const productTypeId = 'product_type_id';

        this.products.forEach((product) => {
          if (!productTypes[product[productTypeId]]) {
            productTypes[product[productTypeId]] = {
              id: product[productTypeId],
              name: product.product_type_name,
              products: [],
            };
          }

          productTypes[product[productTypeId]].products.push(product);
        });

        return productTypes;
      },
      /**
       * Creates an array of categories if no category is provided.
       *
       * @returns {Array}
       */
      productTypesByCategory() {
        return this.categories.length
          ? this.categories
          : [{
            id: 1,
            label: 'All Products',
            productTypeIds: [...new Set(this.products
              .map((product) => product.product_type_id)
              .reduce((array, value) => array.concat(value), []))],
          }];
      },
    },
    watch: {
      /**
       * Watch changes on productsByTypeTree and call expandAll
       */
      productsByTypeTree() {
        this.expandAll();
      },
    },
    mounted() {
      this.popperWidth = (this.width || this.$refs.productSelectorContainer.clientWidth) - 2;

      this.expandAll();
    },
    methods: {
      /**
       * Go through all of the product types by category and the child product type IDs.
       * Push to the respective "openCategoryIds" and "openProductTypeIds" arrays
       * depending if the are included in
       * the "collapsedCategories" and "collapsedProductTypes" props .
       */
      expandAll() {
        this.productTypesByCategory.forEach((category, index) => {
          if (!this.collapsedCategories.includes(index)) {
            this.openCategoryIds.push(index);
          }

          category.productTypeIds.forEach((productTypeId) => {
            if (!this.collapsedProductTypes.includes(productTypeId)) {
              this.openProductTypeIds.push(productTypeId);
            }
          });
        });
      },
      /**
       * Hiding the popover when clicking outside <TfPopover>
       */
      hidePopover() {
        if (this.keepOpen) {
          return;
        }

        this.isActive = false;
      },
      /**
       * Emits the category ID and the openCategoryIds array.
       *
       * @param {number} categoryIndex
       */
      toggleCategory(categoryIndex) {
        this.$emit('toggleCategory', {
          action: this.openCategoryIds.includes(categoryIndex)
            ? 'expand'
            : 'collapse',
          categoryIndex,
          openCategoryIds: this.openCategoryIds,
        });
      },
    },
  };
</script>

<style lang="scss" scoped>
  @import 'src/assets/styles/variables';

  .category-toggle {
    height: 18px;
    width: 16px;
    margin-top: 3px;
    fill: $tf-dark-gray;
    transition: transform .3s;
    transform: rotateZ(0);

    &.is-active {
      fill: $tf-blue;
      transform: rotateZ(180deg);
    }
  }

  .el-collapse-item__header.is-active {
    .title-category {
      color: $tf-blue;
    }
  }

  .title-category {
    display: flex;
    justify-content: space-between;
    padding: 9px 15px;
    border-bottom: 1px solid $tf-extra-light-gray-1;

    font: {
      size: 16px;
      weight: 700;
    }

    cursor: pointer;

    svg {
      .container-category.is-active & {
        transition: transform .3s;
        transform: rotateZ(180deg);
      }

      transform: rotateZ(0);
    }
  }

  .product-selector {
    &-counter {
      margin: 0 5px;
    }

    &-toggle {
      display: flex;
      flex: {
        basis: 18px;
        shrink: 0;
      }
      justify-content: flex-end;

      svg {
        fill: $tf-medium-gray;
        transition: transform .3s;
        transform: rotateZ(0);
        height: 20px;
        width: 12px;

        &.is-active {
          fill: $tf-blue;
          transform: rotateZ(180deg);
        }
      }

      &.is-fixed {
        margin-left: auto;
      }
    }

    &-input {
      position: relative;
      display: flex;
      align-items: center;
      width: 100%;
      padding: 5px 10px;
      font-size: 14px;
      border: 1px solid $tf-light-gray;
      border-radius: 4px;
      box-sizing: border-box;
      cursor: pointer;

      &.is-active {
        border-color: $tf-blue;
      }

      &:focus {
        outline: none;
      }
    }

    &-label {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }

  .el-collapse-item__wrap {
    padding-left: 15px;
  }
</style>
