<template>
  <n-tabs v-model="selectedTapIndex" variant="primary">
    <n-tab
      v-for="(item, index) in list"
      :key="item.id + index"
      @click="navigateToSection(item.id)"
    >
      {{ item.title }}
    </n-tab>
  </n-tabs>
</template>

<script>
import { defineComponent } from "vue";
import NTabs from "../Tabs/NTabs/n-tabs.vue";
import NTab from "../Tabs/NTab/n-tab.vue";

export default defineComponent({
  name: "NScrollAwareNavigation",
  components: {
    NTabs,
    NTab,
  },
  props: {
    // [id, title]
    list: {
      type: Array,
      default: () => [],
    },
    wrapperClass: {
      type: String,
      default: "",
    },
    mandatory: {
      type: Boolean,
      default: false,
    },
    threeshold: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      activeTabId: "",
      selectedTapIndex: "",
    };
  },
  computed: {
    sortedNodes() {
      const { list, threeshold } = this;

      if (this.list.length) {
        const sections = list
          .map(({ id }) => {
            const node = document.querySelector(`#${id}`);
            const sectionTop = node.offsetTop;
            const acualScrollableDistance = sectionTop - threeshold;

            return {
              id,
              acualScrollableDistance,
            };
          })
          .sort((a, b) =>
            a.acualScrollableDistance > b.acualScrollableDistance ? 1 : -1
          );

        return sections;
      }

      return [];
    },
    sanitizedHash() {
      const { $route, removeHashFromString } = this;

      return removeHashFromString($route.hash);
    },
  },
  created() {
    window.addEventListener("scroll", this.onScroll);
  },
  mounted() {
    const {
      mandatory,
      activeTabId,
      isActiveHashedSection,
      sanitizedHash,
    } = this;

    if (mandatory) {
      this.setDefaultActive();
    }

    // ? Trigger scrolling based on hashing
    if (sanitizedHash && !isActiveHashedSection(activeTabId)) {
      this.navigateToSection(sanitizedHash);
    }
  },
  destroyed() {
    window.removeEventListener("scroll", this.onScroll);
  },
  methods: {
    setDefaultActive() {
      const { list } = this;

      if (list.length) {
        const defaultId = list[0].id;

        this.navigateToSection(defaultId);
        this.activeTabId = defaultId;
      }
    },
    isActiveHashedSection(id) {
      const { hash } = this.$route;

      return this.removeHashFromString(hash) === id;
    },
    removeHashFromString(hashed) {
      return hashed.split("#")[1];
    },
    onScroll() {
      const { sortedNodes, activeTabId, sanitizedHash } = this;

      if (sortedNodes.length) {
        let activeId = activeTabId;

        sortedNodes.forEach((node) => {
          if (window.scrollY >= node.acualScrollableDistance) {
            const listItemId = node.id;

            activeId = listItemId;
          }
        });

        const useHash = sanitizedHash && sanitizedHash !== activeId;
        const enhancedId = useHash ? sanitizedHash : activeId;

        this.updateIds(enhancedId);
      }
    },
    navigateToSection(id) {
      if (id !== this.activeTabId) {
        const section = document.getElementById(id);

        if (section) {
          const sectionTop = section.offsetTop;
          const acualScrollableDistance = sectionTop - this.threeshold;

          // ! To avoid redundant navigation
          if (id !== this.sanitizedHash) {
            this.updateIds(id);
            this.$router.replace(`${this.$route.path}#${id}`);
          }

          const noAvailableScroll = this.isScrollBottomReached();

          if (noAvailableScroll) {
            this.updateIds(id);
          }

          window.scroll({
            top: acualScrollableDistance,
            behavior: "smooth",
          });
        }
      }
    },
    getIndexById(id) {
      const { list } = this;

      return list.findIndex((item) => item.id === id);
    },
    updateIds(id) {
      this.selectedTapIndex = this.getIndexById(id);
      this.activeTabId = id;
    },
    isScrollBottomReached() {
      const scrollTop = window.scrollY;

      // Get the height of the document.
      const documentHeight = document.documentElement.scrollHeight;

      // If the scroll position is equal to the document height,
      // then the user has reached the bottom of the page.
      return scrollTop + window.innerHeight === documentHeight;
    },
  },
});
</script>

<style scoped>
.pkg-base-n-divider {
  height: 1px;
  opacity: 0.2;
}

.pkg-base-n-divider.n-divider--vertical {
  width: 1px;
}
</style>
