<template>
  <div class="page-container card">
    <div class="card-body">
      <div class="list-group list-unstyled page-table-heading">
        <div class="page-table-heading-item">
          <button class="btn p-1 m-n1" @click="toggleAll()">
            <chevron-up-icon v-if="allPagesOpen" />
            <chevron-down-icon v-else />
          </button>
          <div class="page-title">
            Pagina
          </div>
        </div>
        <slot v-if="$slots.header" name="header"></slot>
      </div>
      <ul class="list-group list-unstyled" v-if="pageTree.length > 0">
        <component
          :is="itemComponent"
          ref="nestedPageTableItem"
          v-for="page in pageTree"
          :key="page.id"
          :page="page"
          :show-page="showPage"
          defaultChildrenOpened
          @toggle="onNestedPageTableItemToggle"
        />
      </ul>
      <p class="p-2" v-if="!isLoadingPages && !hasResults">
        Er zijn geen pagina's gevonden.
      </p>
      <spinner v-if="isLoadingPages" />
    </div>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Watch, Ref, Prop } from 'vue-property-decorator';
import { namespace } from 'vuex-class';

import NestedPageTableItem from './nested-page-table-item.vue';
import PageOverviewItem from './page-overview-item.vue';
import PageTranslationsOverviewItem from '../shared/language/page-translations-overview-item.vue';

import { Page } from '../types/api';
import Spinner from '../shared/spinner.vue';
import RouteUtils from '../tools/route-utils';

const PageStore = namespace('pages');

@Component({
  name: 'PageOverview',
  components: {
    NestedPageTableItem,
    PageOverviewItem,
    PageTranslationsOverviewItem,
    Spinner
  }
})
export default class NestedPageTable extends Vue {
  @Prop({ type: String, required: true }) readonly itemComponent!: string;
  @Prop({ type: String, default: '' }) readonly typeFilter!: string;
  @Prop({ type: String, default: '' }) readonly search!: string;

  @Ref() readonly nestedPageTableItem!: NestedPageTableItem[] | NestedPageTableItem;

  @PageStore.State('initialized') readonly initialized!: boolean;
  @PageStore.State('isLoadingPages') readonly isLoadingPages!: Page[];
  @PageStore.Getter('pageTree') readonly pageTree!: Page[];

  filteredPages: Page[] = [];
  allPagesOpen = false;
  nestedPageTableItems: NestedPageTableItem[] = [];

  showPage(page: Page) {
    const children = page.children.filter(subPage => {
      if (subPage.children && subPage.children.length) {
        return this.showPage(subPage);
      }
      return this.matchesFilters(subPage);
    });

    if (children.length) {
      // Has children, but not when filtered.
      return true;
    }

    return this.matchesFilters(page);
  }

  matchesFilters(page: Page) {
    const matchesPageType = this.typeFilter ? page.itemName === this.typeFilter : true;
    const matchesSearch = page.name.includes(this.search);

    return matchesPageType && matchesSearch;
  }

  toggleAll(open = !this.allPagesOpen) {
    this.setNestedPageTableItems();
    this.nestedPageTableItems.forEach(item => item.toggleSelfAndChildren(open));
    this.allPagesOpen = open;
  }

  onNestedPageTableItemToggle() {
    this.setNestedPageTableItems();
    // Check if all overview page items are opened
    this.allPagesOpen = this.nestedPageTableItems.every(item => this.pageItemAndChildrenAreOpen(item));
  }

  pageItemAndChildrenAreOpen(item: NestedPageTableItem): boolean {
    // If the item is undefined or has no children we consider it open
    if (!item || item.page?.children?.length === 0) {
      return true;
    }

    // Check if the current item with children is open and continue the recursive check by calling this function once more for every child overview item
    return item.childrenOpened && (Array.isArray(item.nestedPageTableItem)
      ? item.nestedPageTableItem.every(i => this.pageItemAndChildrenAreOpen(i))
      : this.pageItemAndChildrenAreOpen(item.nestedPageTableItem as NestedPageTableItem));
  }

  setNestedPageTableItems() {
    // This is useful to always treat the page overview item(s) ref as an array instead of a conditional array or object
    this.nestedPageTableItems = Array.isArray(this.nestedPageTableItem) ? this.nestedPageTableItem : [this.nestedPageTableItem];
  }

  get filters() {
    return {
      search: this.search,
      typeFilter: this.typeFilter
    };
  }

  get hasResults() {
    return this.pageTree.filter(page => this.showPage(page)).length > 0;
  }

  @Watch('filters')
  async onFiltersChange() {
    await RouteUtils.persistToQuery(this.$router, this.$route, {
      search: this.search || undefined,
      typeFilter: this.typeFilter || undefined
    });

    if (this.initialized) {
      this.toggleAll(true);
    }
  }

  @Watch('initialized')
  async onInitializedChange() {
    if (this.search || this.typeFilter) {
      this.toggleAll(true);
    }
  }
}
</script>

<style lang="scss" scoped>
@import '../variables';

.page-table-heading {
  display: flex;
  flex-direction: row;
  border-bottom: 2px solid $body-color;
  border-radius: 0;

  &-item {
    padding: .5rem .5rem .5rem 0;
    font-weight: bold;
    display: flex;
    flex: 0 0 50%;

    .page-title {
      padding: 0;
    }
  }
}
</style>
