<!--**
 * Copyright since 2007 PrestaShop SA and Contributors
 * PrestaShop is an International Registered Trademark & Property of PrestaShop SA
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is bundled with this package in the file LICENSE.md.
 * It is also available through the world-wide-web at this URL:
 * https://opensource.org/licenses/OSL-3.0
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@prestashop.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade PrestaShop to newer
 * versions in the future. If you wish to customize PrestaShop for your
 * needs please refer to https://devdocs.prestashop.com/ for more information.
 *
 * @author    PrestaShop SA and Contributors <contact@prestashop.com>
 * @copyright Since 2007 PrestaShop SA and Contributors
 * @license   https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
 *-->
<template>
  <div id="product-combinations-generate">
    <modal
      v-if="isModalShown"
      :modal-title="$t('modal.title')"
      :confirmation="true"
      @close="closeModal"
    >
      <template #body>
        <attributes-selector
          :attribute-groups="attributeGroups"
          :selected-attribute-groups="selectedAttributeGroups"
          @changeSelected="changeSelected"
          @removeSelected="removeSelected"
          @addSelected="addSelected"
          @toggleAll="toggleAll"
          v-if="attributeGroups"
        />
      </template>

      <template #footer-confirmation>
        <button
          type="button"
          class="btn btn-outline-secondary"
          @click.prevent.stop="closeModal"
          :aria-label="$t('modal.close')"
        >
          {{ $t('modal.close') }}
        </button>

        <button
          type="button"
          class="btn btn-primary"
          @click.prevent.stop="generateCombinations"
          :disabled="!generatedCombinationsNb || loading"
        >
          <span v-if="!loading">
            {{
              $tc('generator.action', generatedCombinationsNb, {
                '%combinationsNb%': generatedCombinationsNb,
              })
            }}
          </span>
          <span
            class="spinner-border spinner-border-sm"
            v-if="loading"
            role="status"
            aria-hidden="true"
          />
        </button>
      </template>
    </modal>
  </div>
</template>

<script>
  import CombinationsService from '@pages/product/services/combinations-service';
  import AttributesSelector from '@pages/product/components/generator/AttributesSelector';
  import isSelected from '@pages/product/mixins/is-attribute-selected';
  import {getAllAttributeGroups} from '@pages/product/services/attribute-groups';
  import Modal from '@vue/components/Modal';
  import ProductEventMap from '@pages/product/product-event-map';

  const {$} = window;

  const CombinationEvents = ProductEventMap.combinations;

  export default {
    name: 'CombinationGenerator',
    data() {
      return {
        attributeGroups: [],
        selectedAttributeGroups: {},
        combinationsService: new CombinationsService(this.productId),
        isModalShown: false,
        preLoading: true,
        loading: false,
        scrollbar: null,
        hasGeneratedCombinations: false,
      };
    },
    props: {
      productId: {
        type: Number,
        required: true,
      },
      eventEmitter: {
        type: Object,
        required: true,
      },
    },
    mixins: [isSelected],
    components: {
      Modal,
      AttributesSelector,
    },
    computed: {
      generatedCombinationsNb() {
        const groupIds = Object.keys(this.selectedAttributeGroups);
        let combinationsNumber = 0;

        groupIds.forEach((attributeGroupId) => {
          const {attributes} = this.selectedAttributeGroups[attributeGroupId];

          if (!attributes.length) {
            return;
          }

          // Only start counting when at least one attribute is selected
          if (combinationsNumber === 0) {
            combinationsNumber = 1;
          }
          combinationsNumber *= this.selectedAttributeGroups[attributeGroupId]
            .attributes.length;
        });

        return combinationsNumber;
      },
    },
    mounted() {
      this.initAttributeGroups();
      this.eventEmitter.on(CombinationEvents.openCombinationsGenerator, () => this.showModal());
    },
    methods: {
      /**
       * This methods is used to initialize combinations definitions
       */
      async initAttributeGroups() {
        try {
          this.attributeGroups = await getAllAttributeGroups();
          window.prestaShopUiKit.init();
          this.preLoading = false;
          this.eventEmitter.emit(CombinationEvents.combinationGeneratorReady);
        } catch (error) {
          window.$.growl.error({message: error});
        }
      },
      /**
       * Show the modal, and execute PerfectScrollBar and Typehead
       */
      showModal() {
        if (this.preLoading) {
          return;
        }
        document.querySelector('body').classList.add('overflow-hidden');
        this.hasGeneratedCombinations = false;
        this.selectedAttributeGroups = {};
        this.isModalShown = true;
      },
      /**
       * Handle modal closing
       */
      closeModal() {
        this.isModalShown = false;
        document.querySelector('body').classList.remove('overflow-hidden');
        if (this.hasGeneratedCombinations) {
          this.eventEmitter.emit(CombinationEvents.refreshCombinationList);
        }
      },
      /**
       * Used when the user clicks on the Generate button of the modal
       */
      async generateCombinations() {
        this.loading = true;
        const data = {
          attributes: {},
        };
        Object.keys(this.selectedAttributeGroups).forEach((attributeGroupId) => {
          data.attributes[attributeGroupId] = [];
          this.selectedAttributeGroups[attributeGroupId].attributes.forEach(
            (attribute) => {
              data.attributes[attributeGroupId].push(attribute.id);
            },
          );
        });

        try {
          const response = await this.combinationsService.generateCombinations(
            data,
          );
          $.growl({
            message: this.$t('generator.success', {
              '%combinationsNb%': response.combination_ids.length,
            }),
          });
          this.selectedAttributeGroups = {};
          this.hasGeneratedCombinations = true;
        } catch (error) {
          if (error.responseJSON && error.responseJSON.error) {
            $.growl.error({message: error.responseJSON.error});
          } else {
            $.growl.error({message: error});
          }
        }

        this.loading = false;
      },
      /**
       * Remove the attribute if it's selected or add it
       *
       * @param {Object} selectedAttribute
       * @param {{id: int, name: string}} attributeGroup
       */
      changeSelected({selectedAttribute, attributeGroup}) {
        if (
          !this.isSelected(
            selectedAttribute,
            attributeGroup,
            this.selectedAttributeGroups,
          )
        ) {
          this.addSelected({selectedAttribute, attributeGroup});
        } else {
          this.removeSelected({
            selectedAttribute,
            selectedAttributeGroup: attributeGroup,
          });
        }
      },
      /**
       * @param {Object} selectedAttribute
       * @param {{id: int, name: string}} attributeGroup
       */
      addSelected({selectedAttribute, attributeGroup}) {
        // Extra check to avoid adding same attribute twice which would cause a duplicate key error
        if (
          this.isSelected(
            selectedAttribute,
            attributeGroup,
            this.selectedAttributeGroups,
          )
        ) {
          return;
        }

        // Add copy of attribute group in selected groups
        if (!this.selectedAttributeGroups[attributeGroup.id]) {
          const newAttributeGroup = {
            [attributeGroup.id]: {
              id: attributeGroup.id,
              name: attributeGroup.name,
              attributes: [],
            },
          };

          // This is needed to correctly handle observation
          this.selectedAttributeGroups = {
            ...this.selectedAttributeGroups,
            ...newAttributeGroup,
          };
        }

        this.selectedAttributeGroups[attributeGroup.id].attributes.push(
          selectedAttribute,
        );
      },
      /**
       * @param {Object} selectedAttribute
       * @param {Object} selectedAttributeGroup
       */
      removeSelected({selectedAttribute, selectedAttributeGroup}) {
        if (
          !Object.prototype.hasOwnProperty.call(
            this.selectedAttributeGroups,
            selectedAttributeGroup.id,
          )
        ) {
          return;
        }

        const group = this.selectedAttributeGroups[selectedAttributeGroup.id];
        group.attributes = group.attributes.filter(
          (attribute) => attribute.id !== selectedAttribute.id,
        );
      },
      /**
       * Remove the attribute if it's selected or add it
       *
       * @param {Object} selectedAttribute
       * @param {{id: int, name: string}} attributeGroup
       */
      toggleAll({attributeGroup, select}) {
        if (select) {
          attributeGroup.attributes.forEach((attribute) => {
            this.addSelected({selectedAttribute: attribute, attributeGroup});
          });
        } else {
          attributeGroup.attributes.forEach((attribute) => {
            this.removeSelected({
              selectedAttribute: attribute,
              selectedAttributeGroup: attributeGroup,
            });
          });
        }
      },
    },
  };
</script>
