<template>
  <div class="itf-filter-panel d-flex flex-column align-items-start" :class="{'gap-3': !filtersOnly, 'd-none': !visible}">
    <div v-if="!filtersOnly" class="d-flex gap-2 justify-content-between w-100">
      <slot name="search">
        <div class="d-flex align-items-center gap-2 flex-grow-1">
          <slot name="before-search"></slot>
          <itf-text-field
            v-if="search"
            style="width: 300px"
            small
            class="h-100"
            :placeholder="searchPlaceholder"
            prepend-icon="search"
            :delay-input="250"
            clearable
            :value="filterValueQuery"
            @input="(e) => onFilterChange({ type: 'text', name: 'query' }, { value: e })"
          />
          <slot name="after-search"></slot>
        </div>
      </slot>
      <div class="d-flex gap-2">
        <!--itf-button v-if="showFilter" default icon class="position-relative" @click="toggleFilters" :class="{'active': showFilters}">
          <itf-icon new name="filter" />
          <span v-if="activeFiltersCount" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-primary">
            {{activeFiltersCount}}
            <span class="visually-hidden">active filters</span>
          </span>
        </itf-button-->
        <slot name="after-filter-btn"></slot>
      </div>
    </div>
    <div class="d-flex align-items-start justify-content-between w-100">
      <div v-show="showFilters && showFilter" class="gap-2 filters-row" ref="container" :class="{'expanded': isFilterExpanded}">
        <filter-badge
          v-for="(facet, n) in visibleFilters"
          :key="n"
          class="itf-filter-panel__badge"
          :ref="'item-' + n"
          v-model="filter[facet.name]"
          :is-default="filter[facet.name].isDefault"
          :text="filter[facet.name].label"
          :type="facet.type"
          :icon="facet.icon"
          :options="facet.options"
          :class="{ hidden: !visibleItems.has(n) && !isFilterExpanded }"
          @change="onFilterChange(facet, $event)"
        />
      </div>
      <div v-if="showFilters && showFilter && (visibleItems.size < visibleFilters.length || (isFilterExpanded && visibleItems.size === visibleFilters.length))">
        <itf-button icon default small class="itf-filter-panel__filters" @click="toggleExpandFilter">
          <itf-icon v-if="isFilterExpanded" name="minus" />
          <itf-icon v-else name="plus" />
        </itf-button>
      </div>
      <slot name="after-filters"></slot>
    </div>
    <div v-if="loading && !visibleFilters.length">
      <span class="itf-spinner"></span>
      {{$t('loading')}}
    </div>
  </div>
</template>
<style lang="scss">
.itf-filter-panel {
  &__badge {
    transition: opacity 0.3s ease-in-out;

    &.hidden {
      opacity: 0;
      pointer-events: none;
      visibility: hidden;
    }
  }
  &__filters {
    outline: 1px solid var(--filter-badge__default-border-color);
  }
  .itf-text-field:not(.is-valid):not(.is-invalid) .itf-icon {
    color: #8E97A5;
  }
  .itf-text-field__input {
    border: 0 none;
    box-shadow: none;
    background-color: #F2F4F7;
    border-radius: .5rem;
    font-family: var(--bs-font-sans-serif);
    .addon-start {
      padding-left: 2rem;
    }
    &::placeholder {
      color: #8E97A5;
    }
  }

  .filters-row {
    display: flex;
    overflow: hidden;
    width: 100%;
    padding: 2px;
    margin: -2px;
    flex-wrap: nowrap;

    &.expanded {
      flex-wrap: wrap;

      .itf-filter-panel__badge.hidden {
        opacity: 1;
        visibility: visible;
        pointer-events: all;
      }
    }
  }
}
</style>
<script>
import { DateTime } from 'luxon';
import { Vue, Watch, Model, Prop, Component } from 'vue-property-decorator';
import tooltip from '../../directives/tooltip';
import itfIcon from '../icon/Icon';
import itfButton from '../button/Button';
import itfDropdown from '../dropdown/Dropdown.vue';
import itfTextField from '../text-field/TextField.vue';
import { formatMoney, formatRangeDates } from '../../helpers/formatters';
import FilterBadge from './FilterBadge.vue';
import sortBy from 'lodash/sortBy';

export default @Component({
  components: {
    itfIcon,
    itfButton,
    itfDropdown,
    itfTextField,
    FilterBadge
  },
  directives: {
    tooltip
  }
})
class FilterPanel extends Vue {
  @Model('input') value;
  @Prop({ type: Array }) staticFilters;
  @Prop({ type: String }) endpoint;
  @Prop() panel;
  @Prop(String) stateName;
  @Prop(Boolean) search;
  @Prop({ type: Boolean, default: true }) showFilter;
  @Prop({ type: Boolean, default: false }) filtersOnly;
  @Prop(Boolean) mini;
  @Prop({ type: Boolean, default: true }) visible;
  @Prop({ type: String, default: function() { return this.$t('components.filter.search'); } }) searchPlaceholder;

  filter = {};
  filterValue = null;
  filters = [];
  loading = false;
  showFilters = true;
  isFilterExpanded = false;
  observer = null;

  periodFilters = ['period', 'timeframe'];
  visibleItems = new Set();

  toggleExpandFilter() {
    this.isFilterExpanded = !this.isFilterExpanded;
  }

  beforeDestroy() {
    if (this.observer) {
      this.observer.disconnect();
    }
  }

  initObserver() {
    if (this.observer) {
      this.observer.disconnect();
    }
    if (!this.$refs.container) {
      return;
    }
    this.observer = new IntersectionObserver(
      (entries) => {
        entries.forEach(entry => {
          const index = parseInt(entry.target.dataset.index);
          const filter = this.filters[index];
          const value = this.filter[filter.name];
          if (entry.isIntersecting) {
            this.visibleItems.add(index); // Додаємо, якщо елемент у полі зору
          } else {
            this.visibleItems.delete(index); // Видаляємо, якщо вийшов за межі
          }
          this.$forceUpdate(); // Оновлюємо Vue, бо Set не є реактивним
        });
      },
      { root: this.$refs.container, threshold: 1.0 }
    );

    // Спостерігаємо за кожним елементом
    this.$nextTick(() => {
      for (const index in this.visibleFilters) {
        const item = this.$refs[`item-${index}`][0];
        if (item) {
          item.$el.dataset.index = index; // Зберігаємо індекс у dataset
          this.observer.observe(item.$el);
        }
      }
    });
  }

  get filterValueQuery() {
    return this.filterValue?.query ?? '';
  }

  get visibleFilters() {
    if (this.mini) {
      return sortBy(this.filters, (f) => this.filter[f.name].isDefault).filter(f => !f.options?.hidden);
    }
    return this.filters.filter(f => !f.options?.hidden);
  }

  get resetFilters() {
    return this.filters.filter(f => !this.visibleFilters.find(vf => vf.name === f.name));
  }

  get localstorageKey() {
    return `filter-panel-${this.stateName}-filters`;
  }

  @Watch('staticFilters', { deep: true })
  onStaticFiltersUpdate() {
    this.filters = this.staticFilters ?? [];
    this.loadFiltersValue();
  }

  async mounted() {
    if (this.stateName) {
      const item = localStorage.getItem(this.localstorageKey);
      this.showFilters = item ? JSON.parse(item) : this.showFilters;
    }

    this.filters = this.staticFilters ?? [];
    if (this.endpoint) {
      this.loadData();
    } else {
      this.loadFiltersValue();
    }
    if (this.panel) {
      this.panel.on('panels.changed', () => this.loadFiltersValue());
    }
  }

  async loadData() {
    this.loading = true;
    await this.$try(async () => {
      const payload = this.panel ? this.panel.getPayload() : {};
      const {filters, tableSchema} = await this.$axios.$get(this.endpoint, {
        preventRaceCondition: true,
        params: payload
      });
      this.filters = filters;
      this.loading = false;
      this.$emit('set-table-schema', tableSchema);
      this.loadFiltersValue();
    }, () => {
      this.loading = false;
    });
  }

  toggleFilters() {
    this.showFilters = !this.showFilters;
    if (this.stateName) {
      localStorage.setItem(this.localstorageKey, this.showFilters);
    }
  }

  loadFiltersValue() {
    const payload = this.panel ? this.panel.getPayload() : {};
    const filter = {};
    const filterValue = {};
    if (this.filters) {
      for (const item of this.filters) {
        if (this.periodFilters.includes(item.type)) {
          filter[item.name] = payload.from ? this.formatValue(item, { value: [payload.from, payload.to] }) : { isDefault: true, ...item.options.defaultValue };
          filterValue.from = payload.from;
          filterValue.to = payload.to;
        } else {
          filter[item.name] = payload[item.name] ? this.formatValue(item, { value: payload[item.name] }) : { isDefault: true, ...item.options.defaultValue };
          filterValue[item.name] = payload[item.name];
        }
      }
    }
    if (this.search) {
      filterValue.query = payload.query;
    }
    const prevFilter = JSON.stringify(this.getVisibleFilters(this.filterValue));//.concat(JSON.stringify(this.filterValue));
    const newFilter = JSON.stringify(this.getVisibleFilters(filterValue));//.concat(JSON.stringify(filterValue));
    if (prevFilter !== newFilter || !this.filterValue) {
      this.filter = filter;
      this.filterValue = filterValue;
      this.$emit('input', this.filterValue);
      this.$emit('loaded', this.filterValue);
      this.initObserver();
    }
  }

  getVisibleFilters(filter) {
    const result = [];
    const facets = Object.values(this.filter);
    for (const facet of facets) {
      if (!facet.isDefault && !facet.hidden) {
        result.push(filter[facet.name]);
      }
    }
    return result.filter(Boolean);
  }

  setFilter(field, value) {
    const facet = this.filters.find(facet => facet.name === field);
    if (!facet) {
      throw new Error(`Facet ${field} not found`);
    }
    this.onFilterChange(facet, { value });
  }

  onFilterChange(facet, value) {
    this.filter[facet.name] = this.formatValue(facet, value);
    if (this.periodFilters.includes(facet.type)) {
      this.filterValue.from = this.filter[facet.name].isDefault ? undefined : value.value[0];
      this.filterValue.to = this.filter[facet.name].isDefault ? undefined : value.value[1];
    } else {
      this.filterValue[facet.name] = this.filter[facet.name].isDefault ? undefined : value.value;
    }
    if (this.panel) {
      const payload = this.panel.getPayload();
      this.panel.setPayload({ ...payload, ...this.filterValue });
    }
    this.$emit('input', this.filterValue);
    this.$emit('change', this.filterValue);
  }

  get daysList() {
    return [// { title: 'Today', date: () => [DateTime.local(), DateTime.local()] },
      { title: this.$t('components.thisWeek'), date: () => [DateTime.local().startOf('week'), DateTime.local().endOf('week')] },
      { title: this.$t('components.lastWeek'), date: () => [DateTime.local().minus({ week: 1 }).startOf('week'), DateTime.local().minus({ week: 1 }).endOf('week')] },
      { title: this.$t('components.thisMonth'), date: () => [DateTime.local().startOf('month'), DateTime.local().endOf('month')] },
      { title: this.$t('components.lastMonth'), date: () => [DateTime.local().minus({ months: 1 }).startOf('month'), DateTime.local().minus({ months: 1 }).endOf('month')] },
      { title: this.$t('components.thisQuarter'), date: () => [DateTime.local().startOf('quarter'), DateTime.local().endOf('quarter')] },
      { title: this.$t('components.lastQuarter'), date: () => [DateTime.local().minus({ quarter: 1 }).startOf('quarter'), DateTime.local().minus({ quarter: 1 }).endOf('quarter')] },
      { title: this.$t('components.thisYear'), date: () => [DateTime.local().startOf('year'), DateTime.local().endOf('year')] },
      { title: this.$t('components.lastYear'), date: () => [DateTime.local().minus({ year: 1 }).startOf('year'), DateTime.local().minus({ year: 1 }).endOf('year')] },
    ];
  }

  get activeFiltersCount() {
    return Object.values(this.filter).filter(facet => !facet.isDefault && !facet.hidden).length;
  }

  formatValue(facet, value) {
    if (this.periodFilters.includes(facet.type)) {
      if (value.value) {
        let from = DateTime.fromFormat(value.value[0], 'yyyy-MM-dd');
        let to = DateTime.fromFormat(value.value[1], 'yyyy-MM-dd');
        if (!from.isValid || !to.isValid) {
          from = DateTime.fromFormat(facet.options.defaultValue.value[0], 'yyyy-MM-dd');
          to = DateTime.fromFormat(facet.options.defaultValue.value[1], 'yyyy-MM-dd');
        }
        const namedItem = this.daysList.find(item => {
          const [start, end] = item.date();
          return start.equals(from.startOf('day')) && end.equals(to.endOf('day'));
        });
        if (namedItem) {
          value.label = namedItem.title;
        } else {
          value.label = formatRangeDates(from.toJSDate(), to.toJSDate());
        }
      }
      if (facet.options.defaultValue?.value) {
        value.isDefault = facet.options.defaultValue ? value.value[0] === facet.options.defaultValue.value[0] && value.value[1] === facet.options.defaultValue.value[1] : false;
      } else {
        value.isDefault = !value.value;
      }
    } else if (facet.type === 'date') {
      const date = DateTime.fromISO(value.value);
      value.label = (date.isValid ? date : DateTime.fromISO(facet.options.defaultValue.value)).toFormat('dd MMM yyyy');
      value.isDefault = facet.options.defaultValue ? value.value === facet.options.defaultValue.value : false;
      if (value.isDefault && facet.options.defaultValue.label) {
        value.label = facet.options.defaultValue.label;
      }
    } else if  (facet.type === 'month') {
      const date = DateTime.fromISO(value.value);
      value.label = capitalizeFirstLetter((date.isValid ? date : DateTime.fromISO(facet.options.defaultValue.value)).toFormat('LLLL yyyy'));
      value.isDefault = facet.options.defaultValue ? value.value === facet.options.defaultValue.value : false;
    } else if (facet.type === 'facets-list') {
      const firstItem = facet.options.items.find(item => item.value === (Array.isArray(value.value) ? value.value[0] : value.value));
      value.label = firstItem ? firstItem.label : facet.options.defaultValue.label;
      if (!Array.isArray(value.value)) {
        value.value = [value.value];
      }
      if (value.value && value.value.length > 1) {
        value.label = `${value.label} та ще ${value.value.length - 1}`;
      }
      value.isDefault = facet.options.defaultValue ? JSON.stringify(value.value) === JSON.stringify(facet.options.defaultValue.value) : false;
    } else if (facet.type === 'amount') {
      if (value.value === null || (Array.isArray(value.value) && value.value.every(v => v === null))) {
        value.value = facet.options.defaultValue.value;
      } else if (Array.isArray(value.value)) {
      } else {
        if (Number.isNaN(value.value)) {
          value.value = facet.options.defaultValue.value;
        }
      }
      value.isDefault = facet.options.defaultValue ? JSON.stringify(value.value) === JSON.stringify(facet.options.defaultValue.value) : false;

      if (Array.isArray(value.value)) {
        value.label = value.isDefault ? facet.options.defaultValue.label : `${formatMoney(value.value[0], null, 2)} - ${formatMoney(value.value[1], null, 2)}`;
      } else {
        value.label = value.isDefault ? facet.options.defaultValue.label : formatMoney(value.value, null, 2);
      }
      value.isDefault = facet.options.defaultValue ? JSON.stringify(value.value) === JSON.stringify(facet.options.defaultValue.value) : false;
    } else if (facet.type === 'list') {
      const item = facet.options.items.find(item => item.value === value.value);
      value.isDefault = facet.options.defaultValue ? JSON.stringify(value.value) === JSON.stringify(facet.options.defaultValue.value) : false;
      value.label = item ? item.label : facet.options.defaultValue.label;
    } else if (facet.type === 'text') {
      value.value = value.value.length ? value.value : (facet.options?.defaultValue ?? undefined);
      value.isDefault = !value.value;
    }
    value.hidden = facet.options?.hidden ?? false;
    return value;

    function capitalizeFirstLetter(string) {
      return string.charAt(0).toUpperCase() + string.slice(1);
    }
  }
}
</script>
