<template>
  <main class="container is-fluid">
    <div class="loading-content loader is-inline-flex" v-if="loading"></div>
    <template v-else-if="dataset">
      <button
        class="button is-primary is-pulled-right"
        type="button"
        v-on:click="confirmationClone = hasContribAccess"
        :disabled="!hasContribAccess || undefined"
        :title="hasContribAccess ? 'Clone this dataset' : 'You need a contributor access to the project to clone this dataset'"
      >
        Clone
      </button>
      <h1 class="title is-3">Dataset {{ dataset.name }} <StateTag :state="dataset.state" /></h1>
      <p class="subtitle is-5 mb-2"><ItemId :item-id="dataset.id" /></p>
      <p v-if="corpus?.id" class="subtitle is-5">
        In project
        <router-link :to="{ name: 'corpus-update', params: { corpusId } }">
          {{ truncateShort(corpus.name) }}
        </router-link>
      </p>
      <hr />
      <div class="field is-horizontal">
        <label class="label mr-2">State</label>
        <StateTag :state="dataset.state" />
      </div>
      <div class="field is-horizontal">
        <label class="label mr-2">Unique elements</label>
        <span v-if="dataset.unique_elements">✅</span>
        <span v-else>❌</span>
      </div>
      <div class="field">
        <label class="label">Description</label>
        <div class="control">
          <!-- eslint-disable-next-line vue/no-v-html -->
          <p class="content" v-html="md.render(dataset.description)"></p>
        </div>
      </div>
      <div class="mt-4 tabs is-medium is-centered">
        <ul>
          <li
            v-for="set in dataset.sets"
            :key="set.id"
            :class="set.id === selectedSet?.id ? 'is-active' : ''"
          >
            <a v-on:click="selectSet(set)">
              {{ set.name }}
              <span class="tag is-rounded ml-1" :class="{ 'loader': dataset.set_elements === null }">
                {{ dataset.set_elements?.[set.name] ?? '—' }}
              </span>
            </a>
          </li>
        </ul>
      </div>

      <KeepAlive
        v-for="set in dataset.sets"
        :key="set.id"
      >
        <ElementList
          v-if="set.id === selectedSet?.id"
          disabled
          max-size
          :elements="datasetSetElements(set.name)"
          :dataset="dataset"
          :set="set"
        >
          <template v-slot:no-results>
            <div v-if="!elementsLoading[set.name]" class="notification is-warning">
              <span>No elements to display.</span>
            </div>
            <div v-else>
              <!--
                Use a blank div to avoid the "No element to display." default
                notification of the ElementList component, while loading the first page.
              -->
            </div>
          </template>
        </ElementList>
      </KeepAlive>
      <div v-if="selectedSet && elementsLoading[selectedSet.name]" class="loader"></div>
      <button
        v-else-if=" selectedSet && (datasetStats[selectedSet.name] ?? Infinity) > datasetSetElements(selectedSet.name).length"
        class="button"
        v-on:click="next(selectedSet.name)"
      >
        Load more…
      </button>
    </template>
    <div class="notification is-danger" v-else>
      An error occurred loading dataset information.
    </div>

    <Modal v-model="confirmationClone" title="Clone dataset">
      <p>You are about to create a copy of the dataset "{{ dataset?.name }}".</p>
      <p>The copy will have the same elements and attributes, except that its state will be set to <StateTag state="open" />.</p>
      <template v-slot:footer="{ close }">
        <button class="button" v-on:click="close">Cancel</button>
        <button
          class="button is-primary ml-auto"
          v-on:click="clone"
          title="Clone the dataset"
          :class="{ 'is-loading': cloneLoading }"
          :disabled="cloneLoading || undefined"
        >
          Clone
        </button>
      </template>
    </Modal>
  </main>
</template>

<script lang="ts">
import MarkdownIt from 'markdown-it'
import Mousetrap from 'mousetrap'
import { corporaMixin, truncateMixin } from '@/mixins.js'
import { mapGetters as mapVuexGetters } from 'vuex'
import { mapState, mapActions } from 'pinia'
import { defineComponent, PropType } from 'vue'
import { UUID_REGEX } from '@/config'
import { UUID } from '@/types'
import Modal from '@/components/Modal.vue'
import { errorParser } from '@/helpers'
import { useDatasetStore, useNotificationStore } from '@/stores'
import ElementList from '@/components/Navigation/ElementList.vue'
import StateTag from '@/components/Corpus/Datasets/StateTag.vue'
import ItemId from '@/components/ItemId.vue'
import { DatasetSet } from '@/types/dataset'

export default defineComponent({
  mixins: [
    corporaMixin,
    truncateMixin
  ],
  components: {
    ElementList,
    StateTag,
    ItemId,
    Modal
  },
  props: {
    datasetId: {
      type: String as PropType<UUID>,
      required: true,
      validator: value => typeof value === 'string' && UUID_REGEX.test(value)
    }
  },
  data: () => ({
    selectedSet: null as (DatasetSet | null),
    md: new MarkdownIt({ breaks: true }),
    confirmationClone: false,
    loading: false,
    // Display a loader for each set
    elementsLoading: {} as Record<string, boolean>,
    cloneLoading: false
  }),
  mounted () {
    Mousetrap.bind('m', () => { if (this.selectedSet) this.next(this.selectedSet.name) })
  },
  beforeUnmount () {
    Mousetrap.unbind('m')
  },
  computed: {
    ...mapVuexGetters('auth', ['isVerified']),
    ...mapState(useDatasetStore, ['datasets', 'datasetElementPagination']),
    dataset () {
      return this.datasets[this.datasetId]
    },
    /**
     * Number of elements in each set.
     * This value is automatically computed and returned when using the API
     * on a single dataset (GET, POST, PATCH). It is set to null otherwise.
     */
    datasetStats (): Record<string, number | null> {
      return this.dataset.set_elements || Object.fromEntries(this.dataset.sets?.map(s => [s, null]) ?? [])
    },
    corpusId () {
      return this.dataset.corpus_id
    },
    hasContribAccess () {
      return this.isVerified && this.corpus && this.canWrite(this.corpus)
    }
  },
  methods: {
    ...mapActions(useDatasetStore, ['cloneDataset', 'retrieveDataset', 'nextDatasetElements']),
    ...mapActions(useNotificationStore, ['notify']),
    async get () {
      this.loading = true
      try {
        await this.retrieveDataset(this.datasetId)
      } catch (err) {
        this.notify({ type: 'error', text: `An error occurred retrieving dataset: ${errorParser(err)}` })
      } finally {
        this.loading = false
      }
    },
    async next (setName: string) {
      if (this.elementsLoading[setName]) return
      this.elementsLoading[setName] = true
      try {
        await this.nextDatasetElements(this.datasetId, setName)
      } catch (err) {
        this.notify({ type: 'error', text: errorParser(err) })
      } finally {
        this.elementsLoading[setName] = false
      }
    },
    selectSet (set: DatasetSet) {
      if (this.selectedSet?.id === set.id) return
      this.selectedSet = set
    },
    async clone () {
      if (!this.dataset?.id || this.cloneLoading) return
      this.cloneLoading = true
      try {
        const data = await this.cloneDataset(this.datasetId)
        this.confirmationClone = false
        this.$router.push({ name: 'dataset-details', params: { datasetId: data.id } })
      } finally {
        this.cloneLoading = false
      }
    },
    // Elements from the current pagination. Defaults to an empty list
    datasetSetElements (setName: string) {
      return (this.datasetElementPagination[this.datasetId]?.[setName]?.datasetElements ?? []).map(({ element }) => element)
    }
  },
  watch: {
    datasetId: {
      handler: 'get',
      immediate: true
    },
    dataset (newValue) {
      this.selectSet(newValue.sets[0])
    },
    selectedSet: {
      immediate: true,
      handler (newValue) {
        if (!newValue || this.datasetElementPagination[this.datasetId]?.[newValue.name]) return
        this.next(newValue.name)
      }
    }
  }
})
</script>