<script>
import {
  GlAlert,
  GlFormGroup,
  GlDropdown,
  GlDropdownItem,
  GlDropdownSectionHeader,
  GlDropdownDivider,
  GlSearchBoxByType,
  GlIntersectionObserver,
  GlLoadingIcon,
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { s__, __ } from '~/locale';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { DEBOUNCE_DELAY } from '~/vue_shared/components/filtered_search_bar/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import currentUserNamespace from '~/projects/settings/graphql/queries/current_user_namespace.query.graphql';

export const i18n = {
  SELECT_A_NAMESPACE: __('Select a new namespace'),
  GROUPS: __('Groups'),
  USERS: __('Users'),
  ERROR_MESSAGE: s__(
    'ProjectTransfer|An error occurred fetching the transfer locations, please refresh the page and try again.',
  ),
  ALERT_DISMISS_LABEL: __('Dismiss'),
  NO_RESULTS_TEXT: __('No results found.'),
};

export default {
  name: 'TransferLocations',
  components: {
    GlAlert,
    GlFormGroup,
    GlDropdown,
    GlDropdownItem,
    GlDropdownSectionHeader,
    GlDropdownDivider,
    GlSearchBoxByType,
    GlIntersectionObserver,
    GlLoadingIcon,
  },
  inject: ['resourceId'],
  props: {
    value: {
      type: Object,
      required: false,
      default: null,
    },
    groupTransferLocationsApiMethod: {
      type: Function,
      required: true,
    },
    showUserTransferLocations: {
      type: Boolean,
      required: false,
      default: true,
    },
    additionalDropdownItems: {
      type: Array,
      required: false,
      default() {
        return [];
      },
    },
    label: {
      type: String,
      required: false,
      default: i18n.SELECT_A_NAMESPACE,
    },
  },
  initialTransferLocationsLoaded: false,
  data() {
    return {
      searchTerm: '',
      userTransferLocations: [],
      groupTransferLocations: [],
      filteredAdditionalDropdownItems: this.additionalDropdownItems,
      isLoading: false,
      isSearchLoading: false,
      hasError: false,
      page: 1,
      totalPages: 1,
    };
  },
  computed: {
    hasUserTransferLocations() {
      return this.userTransferLocations.length;
    },
    hasGroupTransferLocations() {
      return this.groupTransferLocations.length;
    },
    hasAdditionalDropdownItems() {
      return this.filteredAdditionalDropdownItems.length;
    },
    selectedText() {
      return this.value?.humanName || this.label;
    },
    hasNextPageOfGroups() {
      return this.page < this.totalPages;
    },
    showAdditionalDropdownItems() {
      return !this.isLoading && this.filteredAdditionalDropdownItems.length;
    },
    hasNoResults() {
      if (this.isLoading || this.isSearchLoading) {
        return false;
      }

      return (
        !this.hasAdditionalDropdownItems &&
        !this.hasUserTransferLocations &&
        !this.hasGroupTransferLocations
      );
    },
  },
  watch: {
    searchTerm() {
      this.page = 1;

      this.debouncedSearch();
    },
  },
  methods: {
    handleSelect(item) {
      this.searchTerm = '';
      this.$emit('input', item);
    },
    async handleShow() {
      if (this.$options.initialTransferLocationsLoaded) {
        return;
      }

      this.isLoading = true;

      [this.groupTransferLocations, this.userTransferLocations] = await Promise.all([
        this.getGroupTransferLocations(),
        this.getUserTransferLocations(),
      ]);

      this.isLoading = false;
      this.$options.initialTransferLocationsLoaded = true;
    },
    async getGroupTransferLocations() {
      try {
        const {
          data: groupTransferLocations,
          headers,
        } = await this.groupTransferLocationsApiMethod(this.resourceId, {
          page: this.page,
          search: this.searchTerm,
        });

        const { totalPages } = parseIntPagination(normalizeHeaders(headers));
        this.totalPages = totalPages;

        return groupTransferLocations.map(({ id, full_name: humanName }) => ({
          id,
          humanName,
        }));
      } catch {
        this.handleError();

        return [];
      }
    },
    async getUserTransferLocations() {
      if (!this.showUserTransferLocations) {
        return [];
      }

      try {
        const {
          data: {
            currentUser: { namespace },
          },
        } = await this.$apollo.query({
          query: currentUserNamespace,
        });

        if (!namespace) {
          return [];
        }

        return [
          {
            id: getIdFromGraphQLId(namespace.id),
            humanName: namespace.fullName,
          },
        ];
      } catch {
        this.handleError();

        return [];
      }
    },
    async handleLoadMoreGroups() {
      this.isLoading = true;
      this.page += 1;

      const groupTransferLocations = await this.getGroupTransferLocations();
      this.groupTransferLocations.push(...groupTransferLocations);

      this.isLoading = false;
    },
    debouncedSearch: debounce(async function debouncedSearch() {
      this.isSearchLoading = true;

      this.groupTransferLocations = await this.getGroupTransferLocations();

      this.filteredAdditionalDropdownItems = this.additionalDropdownItems.filter((dropdownItem) =>
        dropdownItem.humanName.toLowerCase().includes(this.searchTerm.toLowerCase()),
      );

      this.isSearchLoading = false;
    }, DEBOUNCE_DELAY),
    handleError() {
      this.hasError = true;
    },
    handleAlertDismiss() {
      this.hasError = false;
    },
  },
  i18n,
};
</script>
<template>
  <div>
    <gl-alert
      v-if="hasError"
      variant="danger"
      :dismiss-label="$options.i18n.ALERT_DISMISS_LABEL"
      @dismiss="handleAlertDismiss"
      >{{ $options.i18n.ERROR_MESSAGE }}</gl-alert
    >
    <gl-form-group :label="label">
      <gl-dropdown
        :text="selectedText"
        data-qa-selector="namespaces_list"
        data-testid="transfer-locations-dropdown"
        block
        toggle-class="gl-mb-0"
        @show="handleShow"
      >
        <template #header>
          <gl-search-box-by-type
            v-model.trim="searchTerm"
            :is-loading="isSearchLoading"
            data-qa-selector="namespaces_list_search"
          />
        </template>
        <template v-if="showAdditionalDropdownItems">
          <gl-dropdown-item
            v-for="item in filteredAdditionalDropdownItems"
            :key="item.id"
            @click="handleSelect(item)"
            >{{ item.humanName }}</gl-dropdown-item
          >
          <gl-dropdown-divider />
        </template>
        <div
          v-if="hasUserTransferLocations"
          data-qa-selector="namespaces_list_users"
          data-testid="user-transfer-locations"
        >
          <gl-dropdown-section-header>{{ $options.i18n.USERS }}</gl-dropdown-section-header>
          <gl-dropdown-item
            v-for="item in userTransferLocations"
            :key="item.id"
            data-qa-selector="namespaces_list_item"
            @click="handleSelect(item)"
            >{{ item.humanName }}</gl-dropdown-item
          >
        </div>
        <div
          v-if="hasGroupTransferLocations"
          data-qa-selector="namespaces_list_groups"
          data-testid="group-transfer-locations"
        >
          <gl-dropdown-section-header v-if="showUserTransferLocations">{{
            $options.i18n.GROUPS
          }}</gl-dropdown-section-header>
          <gl-dropdown-item
            v-for="item in groupTransferLocations"
            :key="item.id"
            data-qa-selector="namespaces_list_item"
            @click="handleSelect(item)"
            >{{ item.humanName }}</gl-dropdown-item
          >
        </div>
        <gl-dropdown-item v-if="hasNoResults" button-class="gl-text-gray-900!" disabled>{{
          $options.i18n.NO_RESULTS_TEXT
        }}</gl-dropdown-item>
        <gl-loading-icon v-if="isLoading" class="gl-mb-3" size="sm" />
        <gl-intersection-observer v-if="hasNextPageOfGroups" @appear="handleLoadMoreGroups" />
      </gl-dropdown>
    </gl-form-group>
  </div>
</template>
