<!--
  Copyright (c) 2023-2024 Rackslab

  This file is part of Slurm-web.

  SPDX-License-Identifier: MIT
-->

<script setup lang="ts">
import { onMounted, watch, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useRuntimeStore } from '@/stores/runtime'
import { useClusterDataPoller } from '@/composables/DataPoller'
import { useJobsPageQuery } from '@/composables/jobs/JobsPageQuery'
import { useJobsListPaging } from '@/composables/jobs/JobsListPaging'
import {
  compareJobs,
  jobNameLabel,
  isJobNameEmpty,
  jobPriorityLabel
} from '@/composables/gateway/slurm/job'
import type { SlurmJob } from '@/composables/gateway/slurm/types'
import ClusterMainLayout from '@/components/ClusterMainLayout.vue'
import JobsSorter from '@/components/jobs/JobsSorter.vue'
import JobStatusBadge from '@/components/job/JobStatusBadge.vue'
import JobsFiltersPanel from '@/components/jobs/JobsFiltersPanel.vue'
import JobsFiltersBar from '@/components/jobs/JobsFiltersBar.vue'
import JobResources from '@/components/jobs/JobResources.vue'
import InfoAlert from '@/components/InfoAlert.vue'
import ErrorAlert from '@/components/ErrorAlert.vue'
import LoadingSpinner from '@/components/LoadingSpinner.vue'
import { ChevronLeftIcon, ChevronRightIcon } from '@heroicons/vue/20/solid'
import { ArchiveBoxIcon, PlusIcon, WindowIcon } from '@heroicons/vue/24/outline'

const { cluster } = defineProps<{ cluster: string }>()

const route = useRoute()
const runtimeStore = useRuntimeStore()
const { updateQueryParameters, setupFilterQuerySync } = useJobsPageQuery('jobs')

const poller = useClusterDataPoller<SlurmJob[]>(cluster, 'jobs', 5000)
const jobsList = computed(() => poller.data.value ?? [])
const { sortedJobs, lastpage, firstjob, lastjob, jobsPages } = useJobsListPaging(jobsList, {
  match: (job) => runtimeStore.jobs.matchesFilters(job),
  compareSort: compareJobs
})

function sortJobs() {
  /*
   * Triggered by sort emit of JobsSorter component to update route and resort
   * the jobs with the new criteria.
   */
  updateQueryParameters()
  console.log(
    `Sorting jobs by ${runtimeStore.jobs.activeSort} ordered ${runtimeStore.jobs.activeOrder}`
  )
}

watch(
  () => cluster,
  (new_cluster) => {
    poller.setCluster(new_cluster)
  }
)

/*
 * Set current page to last page if last page changes to a value lower than
 * current page.
 */
watch(lastpage, (new_last_page) => {
  runtimeStore.jobs.page = Math.min(runtimeStore.jobs.page, new_last_page)
  if (route.query.page && parseInt(route.query.page as string) > new_last_page) {
    updateQueryParameters()
  }
})

onMounted(() => {
  setupFilterQuerySync()
})
</script>

<template>
  <ClusterMainLayout
    menu-entry="jobs"
    :cluster="cluster"
    :breadcrumb="[{ title: 'Jobs', routeName: 'jobs' }, { title: 'Active' }]"
  >
    <div>
      <JobsFiltersPanel :cluster="cluster" :nb-jobs="sortedJobs.length" />

      <div class="mx-auto flex items-center justify-between">
        <div class="px-4 py-16 sm:px-6 lg:px-8">
          <h1 class="text-3xl font-bold tracking-tight text-gray-900 dark:text-gray-100">
            Jobs Active
          </h1>
          <p class="mt-4 max-w-xl text-sm font-light text-gray-600 dark:text-gray-300">
            Active jobs in cluster queue
          </p>
        </div>

        <div v-if="poller.loaded.value" class="mt-4 text-right text-gray-600 dark:text-gray-300">
          <div class="text-5xl font-bold">{{ sortedJobs.length }}</div>
          <div class="text-sm font-light">job{{ sortedJobs.length > 1 ? 's' : '' }} found</div>
        </div>
        <div v-else class="flex animate-pulse space-x-4">
          <div class="h-14 w-14 rounded-2xl bg-slate-200 dark:bg-slate-800"></div>
        </div>
      </div>

      <section aria-labelledby="filter-heading" class="-mx-4 -my-2 sm:-mx-6 lg:-mx-8">
        <h2 id="filter-heading" class="sr-only">Filters</h2>

        <div class="border-gray-200 pb-4">
          <div class="mx-auto flex items-center justify-between px-4 sm:px-6 lg:px-8">
            <JobsSorter @sort="sortJobs" />

            <div class="flex flex-wrap items-center gap-2">
              <RouterLink
                v-if="runtimeStore.hasAnyPermission(['jobs-view-past', 'jobs-view-past-own'])"
                data-testid="jobs-scope-toggle"
                :to="{ name: 'jobs-past', params: { cluster } }"
                class="inline-flex items-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-medium text-gray-700 ring-1 ring-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:ring-gray-600 dark:hover:bg-gray-700"
              >
                <ArchiveBoxIcon class="-ml-0.5 h-5 w-5 shrink-0" aria-hidden="true" />
                Terminated
              </RouterLink>
              <button
                type="button"
                class="bg-slurmweb dark:bg-slurmweb-verydark hover:bg-slurmweb-darker focus-visible:outline-slurmweb inline-flex items-center gap-x-1.5 rounded-md px-3 py-2 text-sm font-semibold text-white shadow-xs focus-visible:outline-2 focus-visible:outline-offset-2"
                @click="runtimeStore.jobs.openFiltersPanel = true"
              >
                <PlusIcon class="-ml-0.5 h-5 w-5" aria-hidden="true" />
                Add filters
              </button>
            </div>
          </div>
        </div>
        <JobsFiltersBar />
      </section>

      <div class="mt-8 flow-root min-w-0">
        <ErrorAlert v-if="poller.unable.value"
          >Unable to retrieve jobs from cluster
          <span class="font-medium">{{ cluster }}</span></ErrorAlert
        >
        <div v-else-if="!poller.loaded.value" class="text-gray-400 sm:pl-6 lg:pl-8">
          <LoadingSpinner :size="5" />
          Loading jobs…
        </div>
        <InfoAlert v-else-if="sortedJobs.length == 0"
          >No jobs found on cluster <span class="font-medium">{{ cluster }}</span></InfoAlert
        >
        <div v-else class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8 xl:overflow-x-clip">
          <div class="inline-block min-w-full py-2 align-middle">
            <table class="w-full min-w-full divide-y divide-gray-300 dark:divide-gray-500">
              <thead>
                <tr class="text-sm font-semibold text-gray-900 dark:text-gray-200">
                  <th
                    scope="col"
                    class="w-0 py-3.5 pr-3 whitespace-nowrap sm:pl-6 lg:pr-5 lg:pl-8 xl:pl-10"
                  >
                    #ID
                  </th>
                  <th
                    scope="col"
                    class="w-0 px-3 py-3.5 text-left whitespace-nowrap lg:px-5 xl:px-6"
                  >
                    State
                  </th>
                  <th
                    scope="col"
                    class="hidden w-full max-w-0 overflow-hidden px-3 py-3.5 text-left xl:table-cell xl:px-5 2xl:px-6"
                  >
                    Name
                  </th>
                  <th
                    scope="col"
                    class="w-0 max-w-xs px-3 py-3.5 text-left lg:max-w-sm lg:px-5 xl:max-w-md xl:px-6"
                  >
                    User (account)
                  </th>
                  <th
                    scope="col"
                    class="hidden w-0 px-3 py-3.5 text-left whitespace-nowrap sm:table-cell lg:px-5 xl:px-6"
                  >
                    Resources
                  </th>
                  <th
                    scope="col"
                    class="hidden w-0 px-3 py-3.5 text-left whitespace-nowrap lg:px-5 xl:table-cell xl:px-6"
                  >
                    Partition
                  </th>
                  <th
                    scope="col"
                    class="hidden w-0 px-3 py-3.5 text-left whitespace-nowrap lg:px-5 xl:table-cell xl:px-6"
                  >
                    QOS
                  </th>
                  <th
                    scope="col"
                    class="hidden w-0 px-3 py-3.5 text-center whitespace-nowrap sm:table-cell lg:px-5 xl:px-6"
                  >
                    Priority
                  </th>
                  <th
                    scope="col"
                    class="hidden w-0 max-w-md overflow-hidden px-3 py-3.5 text-left 2xl:table-cell 2xl:max-w-lg 2xl:px-6"
                  >
                    Reason
                  </th>
                  <th
                    scope="col"
                    class="w-0 py-3.5 pr-4 pl-3 whitespace-nowrap sm:pr-6 lg:pr-8 lg:pl-5 xl:pr-10"
                  >
                    <span class="sr-only">View</span>
                  </th>
                </tr>
              </thead>
              <tbody
                class="divide-y divide-gray-200 text-sm text-gray-500 dark:divide-gray-700 dark:text-gray-300"
              >
                <tr v-for="job in sortedJobs.slice(firstjob, lastjob)" :key="job.job_id">
                  <td
                    class="w-0 py-4 pr-3 font-medium whitespace-nowrap text-gray-900 sm:pl-6 lg:pr-5 lg:pl-8 xl:pl-10 dark:text-gray-100"
                  >
                    {{ job.job_id }}
                  </td>
                  <td class="w-0 px-3 py-4 whitespace-nowrap lg:px-5 xl:px-6">
                    <JobStatusBadge :status="job.job_state" />
                  </td>
                  <td
                    class="hidden w-full max-w-0 overflow-hidden px-3 py-4 xl:table-cell xl:px-5 2xl:px-6"
                  >
                    <span
                      :class="[
                        isJobNameEmpty(job.name) ? 'text-gray-400 dark:text-gray-600' : '',
                        'block truncate'
                      ]"
                      :title="isJobNameEmpty(job.name) ? undefined : job.name"
                    >
                      {{ jobNameLabel(job.name) }}
                    </span>
                  </td>
                  <td
                    class="w-0 max-w-xs overflow-hidden px-3 py-4 lg:max-w-sm lg:px-5 xl:max-w-md xl:px-6"
                  >
                    <span class="block truncate" :title="`${job.user_name} (${job.account})`">
                      {{ job.user_name }} ({{ job.account }})
                    </span>
                  </td>
                  <td class="hidden w-0 px-3 py-4 whitespace-nowrap sm:table-cell lg:px-5 xl:px-6">
                    <JobResources :job="job" />
                  </td>
                  <td class="hidden w-0 px-3 py-4 whitespace-nowrap lg:px-5 xl:table-cell xl:px-6">
                    {{ job.partition }}
                  </td>
                  <td class="hidden w-0 px-3 py-4 whitespace-nowrap lg:px-5 xl:table-cell xl:px-6">
                    {{ job.qos }}
                  </td>
                  <td
                    class="hidden w-0 px-3 py-4 text-center whitespace-nowrap sm:table-cell lg:px-5 xl:px-6"
                  >
                    {{ jobPriorityLabel(job) }}
                  </td>
                  <td
                    class="hidden w-0 max-w-md overflow-hidden px-3 py-4 2xl:table-cell 2xl:max-w-lg 2xl:px-6"
                  >
                    <span
                      v-if="job.state_reason != 'None'"
                      class="block truncate"
                      :title="job.state_reason"
                    >
                      {{ job.state_reason }}
                    </span>
                  </td>
                  <td class="h-full text-right font-medium">
                    <RouterLink
                      :to="{ name: 'job', params: { cluster: cluster, id: job.job_id } }"
                      class="hover:text-slurmweb-dark hover:dark:text-slurmweb"
                    >
                      <WindowIcon class="mr-4 inline-block h-5 w-5 lg:mr-6" aria-hidden="true" />
                      <span class="sr-only">View {{ job.job_id }}</span>
                    </RouterLink>
                  </td>
                </tr>
              </tbody>
            </table>
            <div
              class="flex items-center justify-between border-t border-gray-200 px-4 py-3 sm:px-6 dark:border-gray-700"
            >
              <div class="flex flex-1 justify-between sm:hidden">
                <a
                  href="#"
                  class="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 hover:dark:bg-gray-600"
                  >Previous</a
                >
                <a
                  href="#"
                  class="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 hover:dark:bg-gray-600"
                  >Next</a
                >
              </div>
              <div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
                <div>
                  <p class="text-sm text-gray-700 dark:text-gray-300">
                    Showing
                    {{ ' ' }}
                    <span class="font-medium">{{ firstjob }}</span>
                    {{ ' ' }}
                    to
                    {{ ' ' }}
                    <span class="font-medium">{{ lastjob }}</span>
                    {{ ' ' }}
                    of
                    {{ ' ' }}
                    <span class="font-medium">{{ sortedJobs.length }}</span>
                    {{ ' ' }} jobs
                  </p>
                </div>
                <div>
                  <nav
                    v-if="lastpage > 1"
                    class="isolate inline-flex -space-x-px rounded-md shadow-xs"
                    aria-label="Pagination"
                  >
                    <button
                      :class="[
                        runtimeStore.jobs.page == 1
                          ? 'cursor-default bg-gray-100 text-gray-100 dark:bg-gray-900 dark:text-gray-900'
                          : 'text-gray-400 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 hover:dark:bg-gray-700',
                        'relative inline-flex items-center rounded-l-md px-2 py-2 ring-1 ring-gray-300 ring-inset focus:z-20 focus:outline-offset-0 dark:ring-gray-700'
                      ]"
                      @click="runtimeStore.jobs.page > 1 && (runtimeStore.jobs.page -= 1)"
                    >
                      <span class="sr-only">Previous</span>
                      <ChevronLeftIcon class="h-5 w-5" aria-hidden="true" />
                    </button>
                    <template v-for="page in jobsPages()" :key="page.id">
                      <button
                        v-if="page.ellipsis"
                        aria-current="page"
                        class="relative z-10 inline-flex items-center bg-white px-4 py-2 text-xs font-semibold text-gray-600 ring-1 ring-gray-300 ring-inset focus:z-20 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 dark:bg-gray-800 dark:ring-gray-700"
                      >
                        …
                      </button>
                      <button
                        v-else
                        aria-current="page"
                        :class="[
                          page.id == runtimeStore.jobs.page
                            ? 'bg-slurmweb dark:bg-slurmweb-dark text-white'
                            : 'bg-white text-black ring-1 ring-gray-300 ring-inset hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 dark:ring-gray-700 hover:dark:bg-gray-700',
                          'relative z-10 inline-flex items-center px-4 py-2 text-sm font-semibold focus:z-20 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600'
                        ]"
                        @click="runtimeStore.jobs.page = page.id"
                      >
                        {{ page.id }}
                      </button>
                    </template>
                    <button
                      :class="[
                        runtimeStore.jobs.page == lastpage
                          ? 'cursor-default bg-gray-100 text-gray-100 dark:bg-gray-900 dark:text-gray-900'
                          : 'text-gray-400 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-300 hover:dark:bg-gray-700',
                        'relative inline-flex items-center rounded-r-md px-2 py-2 ring-1 ring-gray-300 ring-inset focus:z-20 focus:outline-offset-0 dark:ring-gray-700'
                      ]"
                      @click="runtimeStore.jobs.page < lastpage && (runtimeStore.jobs.page += 1)"
                    >
                      <span class="sr-only">Next</span>
                      <ChevronRightIcon class="h-5 w-5" aria-hidden="true" />
                    </button>
                  </nav>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </ClusterMainLayout>
</template>
