<template>
  <v-btn ref="btn" icon size="small" class="ml-2">
    <v-badge
      color="warning"
      offset-x="-4"
      offset-y="17"
      :model-value="unreadNotification.length > 0"
      :content="unreadNotification.length"
    >
      <v-icon class="on-primary"> fa fa-bell </v-icon>
    </v-badge>
  </v-btn>

  <v-tooltip v-if="btn !== null" :activator="btn" location="bottom">
    <span>{{ $t('notification') }}</span>
  </v-tooltip>

  <v-menu
    v-if="btn !== null"
    v-model="isOpen"
    :activator="btn"
    location="bottom left"
  >
    <v-card max-width="400">
      <v-card-title class="bg-neutral text-body-2 font-weight-bold">
        {{ $t('notification') }}
      </v-card-title>

      <v-card-text class="ma-0 pa-0 header-menu">
        <v-list density="compact" :subheader="true" class="pa-0">
          <v-list-item
            v-for="(notification, index) in notifications"
            :key="index"
            :class="
              'list_item py-3' +
              `${notification.notificationUsers.length === 0 ? ' unread' : ''}`
            "
          >
            <span class="">{{ getMessage(notification) }}</span>

            <template #append>
              <v-icon
                v-if="notification.link"
                icon="mdi:mdi-download"
                class="pt-4"
                @click="download(notification.link)"
              />
            </template>
          </v-list-item>

          <v-divider></v-divider>

          <!--suppress VueUnrecognizedDirective -->
          <span v-intersect="onLastNotificationIntersect" />

          <v-row
            v-if="pending"
            class="fill-height mt-3 mb-3"
            align="center"
            justify="center"
          >
            <v-progress-circular indeterminate color="neutral" />
          </v-row>
        </v-list>
      </v-card-text>

      <v-card-actions class="ma-0 pa-0">
        <v-list-item
          :key="$t('all_notification')"
          :href="notificationUrl"
          router
          class="theme-primary"
          style="width: 100%; height: 52px"
        >
          <v-list-item-title class="text-body-2">
            <span v-text="$t('all_notification')" />
          </v-list-item-title>
        </v-list-item>
      </v-card-actions>
    </v-card>
  </v-menu>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue'
import type { ComputedRef, Ref } from 'vue'
import { useRepo } from 'pinia-orm'
import { NOTIFICATION_TYPE } from '~/types/enum/enums'
import Notification from '~/models/Core/Notification'
import NotificationUsers from '~/models/Core/NotificationUsers'
import { useAccessProfileStore } from '~/stores/accessProfile'
import { useEntityFetch } from '~/composables/data/useEntityFetch'
import type { AnyJson, Pagination } from '~/types/data'
import { useEntityManager } from '~/composables/data/useEntityManager'
import UrlUtils from '~/services/utils/urlUtils'
import NotificationRepository from '~/stores/repositories/NotificationRepository'

const accessProfileStore = useAccessProfileStore()

const isOpen: Ref<boolean> = ref(false)
const page: Ref<number> = ref(1)

const i18n = useI18n()
const runtimeConfig = useRuntimeConfig()

const btn = ref(null)

const { em } = useEntityManager()
const { fetchCollection } = useEntityFetch()
const notificationRepo = useRepo(NotificationRepository)

const query: ComputedRef<AnyJson> = computed(() => {
  return { page: page.value }
})

const {
  data: collection,
  pending,
  refresh,
} = await fetchCollection(Notification, null, query)

/**
 * On récupère les Notifications via le store
 */
const notifications: ComputedRef<Array<Notification>> = computed(() => {
  return notificationRepo.getNotifications()
})

/**
 * Retourne les notifications non lues
 */
const unreadNotification: ComputedRef<Array<Notification>> = computed(() => {
  return notificationRepo.getUnreadNotifications()
})

/**
 * Les metadata dépendront de la dernière valeur du GET lancé
 */
const pagination: ComputedRef<Pagination> = computed(() => {
  return !pending.value && collection.value !== null
    ? collection.value.pagination
    : {}
})

const notificationUrl = UrlUtils.join(
  runtimeConfig.baseUrlAdminLegacy,
  '#/notifications/list/'
)

/**
 * L'utilisateur a fait défiler le menu jusqu'à la dernière notification affichée
 * @param isIntersecting
 */
const onLastNotificationIntersect = (isIntersecting: boolean) => {
  if (isIntersecting) {
    update()
  }
}

/**
 * Lorsque l'utilisateur scroll on regarde la nextPage à charger et on le fait que si le pending du fetch est false
 * (si on a fini de télécharger les éléments précédents)
 */
const update = async () => {
  if (
    !pending.value &&
    pagination.value &&
    pagination.value.next &&
    pagination.value.next > 0
  ) {
    pending.value = true
    page.value = pagination.value.next

    await refresh()

    // Si des notifications n'avaient pas été marquées comme lues, on le fait immédiatement.
    markNotificationsAsRead()
  }
}

/**
 * On construit le message qui va devoir s'afficher pour une notification
 * @param notification
 */
const getMessage = (notification: Notification) => {
  switch (notification.type) {
    case NOTIFICATION_TYPE.FILE:
      return `${i18n.t('your_file')} ${notification.message?.fileName} ${i18n.t(
        'is_ready_to_be_downloaded'
      )}`

    case NOTIFICATION_TYPE.MESSAGE:
      if (notification.message?.action)
        return `${i18n.t('your_message')} ${
          notification.message?.fileName
        } ${i18n.t('is_ready_to_be')} ${notification.message.action}`

      return `${i18n.t('your_message')} ${
        notification.message?.about ?? ''
      } ${i18n.t('has_been_sent')} `

    case NOTIFICATION_TYPE.SYSTEM:
      if (notification.message?.about)
        return `${i18n.t(notification.message.about)}`
      break

    default:
      return i18n.t(notification.name)
  }
}

/**
 * Dès la fermeture du menu, on indique que les notifications non lues, le sont.
 */
const unwatch = watch(isOpen, (newValue, _) => {
  if (!newValue) {
    markNotificationsAsRead()
  }
})
onUnmounted(() => {
  unwatch()
})

/**
 * Marque une notification comme lue
 */
const markNotificationAsRead = (notification: Notification) => {
  if (accessProfileStore.id === null) {
    throw new Error('Current access id is null')
  }
  const notificationUsers = em.newInstance(NotificationUsers, {
    access: `/api/accesses/${accessProfileStore.currentAccessId}`,
    notification: `/api/notifications/${notification.id}`,
    isRead: true,
  })

  em.persist(NotificationUsers, notificationUsers)
  notification.notificationUsers = ['read']
  em.save(Notification, notification)
}

/**
 * Marque toutes les notifications non lues comme lues
 */
const markNotificationsAsRead = () => {
  unreadNotification.value.map((notification: Notification) => {
    return markNotificationAsRead(notification)
  })
}

/**
 * Download la cible du lien
 * @param link
 */
const download = (link: string) => {
  if (accessProfileStore.id === null) {
    throw new Error('Current access id is null')
  }
  // TODO: passer cette logique dans un service ; tester ; voir si possible de réunir avec composables/utils/useDownloadFile.ts

  const path: string = link.split('/api')[1]

  // En switch : https://api.test5.opentalent.fr/api/{accessId}/{switchId}/files/{fileId}/download
  // Sans switch : https://local.api.opentalent.fr/api/{accessId}/files/{fileId}/download
  const url = UrlUtils.join(
    runtimeConfig.baseUrlLegacy,
    'api',
    String(accessProfileStore.id),
    String(accessProfileStore.switchId || ''),
    path
  )

  window.open(url)
}
</script>

<style scoped lang="scss">
.list_item {
  white-space: normal;
}
.unread {
  background: rgb(var(--v-theme-neutral-soft, white));
}
</style>
