<script setup lang="ts">
import { PushSubscriptionError } from '~/composables/push-notifications/types'

defineProps<{ show?: boolean }>()

const {
  pushNotificationData,
  saveEnabled,
  undoChanges,
  hiddenNotification,
  isSubscribed,
  isSupported,
  notificationPermission,
  updateSubscription,
  subscribe,
  unsubscribe,
} = usePushManager()
const { t } = useI18n()

const pwaEnabled = useRuntimeConfig().public.pwaEnabled

let busy = $ref<boolean>(false)
let animateSave = $ref<boolean>(false)
let animateSubscription = $ref<boolean>(false)
let animateRemoveSubscription = $ref<boolean>(false)
let subscribeError = $ref<string>('')
let showSubscribeError = $ref<boolean>(false)

const hideNotification = () => {
  const key = currentUser.value?.account?.acct
  if (key)
    hiddenNotification.value[key] = true
}

const showWarning = $computed(() => {
  if (!pwaEnabled)
    return false

  return isSupported
      && (!isSubscribed.value || !notificationPermission.value || notificationPermission.value === 'prompt')
      && !(hiddenNotification.value[currentUser.value?.account?.acct ?? ''] === true)
})

const saveSettings = async () => {
  if (busy)
    return

  busy = true
  await nextTick()
  animateSave = true

  try {
    const subscription = await updateSubscription()
  }
  catch (err) {
    // todo: handle error
    console.error(err)
  }
  finally {
    busy = false
    animateSave = false
  }
}

const doSubscribe = async () => {
  if (busy)
    return

  busy = true
  await nextTick()
  animateSubscription = true

  try {
    const result = await subscribe()
    if (result !== 'subscribed') {
      subscribeError = t(`settings.notifications.push_notifications.subscription_error.${result === 'notification-denied' ? 'permission_denied' : 'request_error'}`)
      showSubscribeError = true
    }
  }
  catch (err) {
    if (err instanceof PushSubscriptionError) {
      subscribeError = t(`settings.notifications.push_notifications.subscription_error.${err.code}`)
    }
    else {
      console.error(err)
      subscribeError = t('settings.notifications.push_notifications.subscription_error.request_error')
    }
    showSubscribeError = true
  }
  finally {
    busy = false
    animateSubscription = false
  }
}
const removeSubscription = async () => {
  if (busy)
    return

  busy = true
  await nextTick()
  animateRemoveSubscription = true
  try {
    await unsubscribe()
  }
  catch (err) {
    console.error(err)
  }
  finally {
    busy = false
    animateRemoveSubscription = false
  }
}
onActivated(() => (busy = false))
</script>

<template>
  <section v-if="pwaEnabled && (showWarning || show)" aria-labelledby="pn-s">
    <Transition name="slide-down">
      <div v-if="show" flex="~ col" border="b base">
        <h3 id="pn-settings" px6 py4 mt2 font-bold text-xl flex="~ gap-1" items-center>
          {{ $t('settings.notifications.push_notifications.label') }}
        </h3>
        <template v-if="isSupported">
          <div v-if="isSubscribed" flex="~ col">
            <form flex="~ col" gap-y-2 px6 pb4 @submit.prevent="saveSettings">
              <p id="pn-instructions" text-sm pb2 aria-hidden="true">
                {{ $t('settings.notifications.push_notifications.instructions') }}
              </p>
              <fieldset flex="~ col" gap-y-1 py-1>
                <legend>{{ $t('settings.notifications.push_notifications.alerts.title') }}</legend>
                <CommonCheckbox v-model="pushNotificationData.follow" hover :label="$t('settings.notifications.push_notifications.alerts.follow')" />
                <CommonCheckbox v-model="pushNotificationData.favourite" hover :label="$t('settings.notifications.push_notifications.alerts.favourite')" />
                <CommonCheckbox v-model="pushNotificationData.reblog" hover :label="$t('settings.notifications.push_notifications.alerts.reblog')" />
                <CommonCheckbox v-model="pushNotificationData.mention" hover :label="$t('settings.notifications.push_notifications.alerts.mention')" />
                <CommonCheckbox v-model="pushNotificationData.poll" hover :label="$t('settings.notifications.push_notifications.alerts.poll')" />
              </fieldset>
              <fieldset flex="~ col" gap-y-1 py-1>
                <legend>{{ $t('settings.notifications.push_notifications.policy.title') }}</legend>
                <CommonRadio v-model="pushNotificationData.policy" hover value="all" :label="$t('settings.notifications.push_notifications.policy.all')" />
                <CommonRadio v-model="pushNotificationData.policy" hover value="followed" :label="$t('settings.notifications.push_notifications.policy.followed')" />
                <CommonRadio v-model="pushNotificationData.policy" hover value="follower" :label="$t('settings.notifications.push_notifications.policy.follower')" />
                <CommonRadio v-model="pushNotificationData.policy" hover value="none" :label="$t('settings.notifications.push_notifications.policy.none')" />
              </fieldset>
              <div flex="~ col" gap-y-4 gap-x-2 py-1 sm="~ justify-between flex-row">
                <button
                  btn-solid font-bold py2 full-w sm-wa flex="~ gap2 center"
                  :class="busy || !saveEnabled ? 'border-transparent' : null"
                  :disabled="busy || !saveEnabled"
                >
                  <span v-if="busy && animateSave" aria-hidden="true" block animate-spin preserve-3d>
                    <span block i-ri:loader-2-fill aria-hidden="true" />
                  </span>
                  <span v-else block aria-hidden="true" i-ri:save-2-fill />
                  {{ $t('settings.notifications.push_notifications.save_settings') }}
                </button>
                <button
                  btn-outline font-bold py2 full-w sm-wa flex="~ gap2 center"
                  type="button"
                  :class="busy || !saveEnabled ? 'border-transparent' : null"
                  :disabled="busy || !saveEnabled"
                  @click="undoChanges"
                >
                  <span aria-hidden="true" class="block i-material-symbols:undo-rounded" />
                  {{ $t('settings.notifications.push_notifications.undo_settings') }}
                </button>
              </div>
            </form>
            <form flex="~ col" mt-4 @submit.prevent="removeSubscription">
              <span border="b base 2px" class="bg-$c-text-secondary" />
              <button
                btn-outline rounded-full font-bold py-4 flex="~ gap2 center" m5
                :class="busy ? 'border-transparent' : null"
                :disabled="busy"
              >
                <span v-if="busy && animateRemoveSubscription" aria-hidden="true" block animate-spin preserve-3d>
                  <span block i-ri:loader-2-fill aria-hidden="true" />
                </span>
                <span v-else block aria-hidden="true" i-material-symbols:cancel-rounded />
                {{ $t('settings.notifications.push_notifications.unsubscribe') }}
              </button>
            </form>
          </div>
          <template v-else>
            <NotificationEnablePushNotification
              :animate="animateSubscription"
              :busy="busy"
              @hide="hideNotification"
              @subscribe="doSubscribe"
            >
              <template #error>
                <Transition name="slide-down">
                  <NotificationSubscribePushNotificationError
                    v-model="showSubscribeError"
                    :message="subscribeError"
                  />
                </transition>
              </template>
            </NotificationEnablePushNotification>
          </template>
        </template>
        <div v-else px6 pb4 role="alert" aria-labelledby="n-unsupported">
          <p id="n-unsupported">
            {{ $t('settings.notifications.push_notifications.unsupported') }}
          </p>
        </div>
      </div>
    </Transition>
    <NotificationEnablePushNotification
      v-if="showWarning && !show"
      closeable-header
      px5
      py4
      :animate="animateSubscription"
      :busy="busy"
      @hide="hideNotification"
      @subscribe="doSubscribe"
    >
      <template #error>
        <Transition name="slide-down">
          <NotificationSubscribePushNotificationError
            v-model="showSubscribeError"
            :message="subscribeError"
          />
        </Transition>
      </template>
    </NotificationEnablePushNotification>
  </section>
</template>