<script setup lang="ts">
import type { ContentCategory, Channel, BackendError } from '~/types'
import type { paths } from '~/generated/types'

import { slugify } from 'transliteration'
import { reactive, computed, ref, watchEffect, watch } from 'vue'
import { useI18n } from 'vue-i18n'

import axios from 'axios'
import AttachmentInput from '~/components/common/AttachmentInput.vue'

import Layout from '~/components/ui/Layout.vue'
import Alert from '~/components/ui/Alert.vue'
import Input from '~/components/ui/Input.vue'
import Textarea from '~/components/ui/Textarea.vue'
import Pills from '~/components/ui/Pills.vue'


interface Props {
  object?: Channel | null
  step?: number
}

const emit = defineEmits<{
  category: [contentCategory: ContentCategory]
  submittable: [value: boolean]
  loading: [value: boolean]
  errored: [errors: string[]]
  created: [channel: Channel]
  updated: [channel: Channel]
}>()
const props = withDefaults(defineProps<Props>(), {
  object: null,
  step: 1
})

const { t } = useI18n()

const newValues = reactive({
  name: props.object?.artist?.name ?? '',
  username: props.object?.actor.preferred_username ?? '',
  tags: props.object?.artist?.tags ?? [] as string[],
  description: props.object?.artist?.description?.text ?? '',
  cover: props.object?.artist?.cover?.uuid ?? null,
  content_category: props.object?.artist?.content_category ?? 'podcast',
  metadata: { ...(props.object?.metadata ?? {}) } as Channel['metadata']
})

// If props has an object, then this form edits, else it creates
// TODO: rename to `process : 'creating' | 'editing'`
const creating = computed(() => props.object === null)
const categoryChoices = computed(() => [
  {
    value: 'podcast',
    label: t('components.audio.ChannelForm.label.podcast'),
    helpText: t('components.audio.ChannelForm.help.podcast')
  },
  {
    value: 'music',
    label: t('components.audio.ChannelForm.label.discography'),
    helpText: t('components.audio.ChannelForm.help.discography')
  }
])

interface ITunesCategory {
  value: string
  label: string
  children: []
}

interface MetadataChoices {
  itunes_category?: ITunesCategory[] | null
  language: {
    value: string
    label: string
  }[]
}

const metadataChoices = ref({ itunes_category: null } as MetadataChoices)
const itunesSubcategories = computed(() => {
  for (const element of metadataChoices.value.itunes_category ?? []) {
    // TODO: Backend: Define schema for `metadata` field
    // @ts-expect-error No types defined by backend schema for `metadata` field
    if (element.value === newValues.metadata.itunes_category) {
      return element.children ?? []
    }
  }

  return []
})

const labels = computed(() => ({
  namePlaceholder: t('components.audio.ChannelForm.placeholder.name'),
  usernamePlaceholder: t('components.audio.ChannelForm.placeholder.username')
}))

const submittable = computed(() => !!(
  newValues.content_category === 'podcast'
    // @ts-expect-error No types defined by backend schema for `metadata` field
    ? newValues.name && newValues.username && newValues.metadata.itunes_category && newValues.metadata.language
    : newValues.name && newValues.username
))

watch(() => newValues.name, (name) => {
  if (creating.value) {
    newValues.username = slugify(name)
  }
})

// @ts-expect-error No types defined by backend schema for `metadata` field
watch(() => newValues.metadata.itunes_category, () => {
  // @ts-expect-error No types defined by backend schema for `metadata` field
  newValues.metadata.itunes_subcategory = null
})

const isLoading = ref(false)
const errors = ref([] as string[])

// @ts-expect-error Re-check emits
watchEffect(() => emit('category', newValues.content_category))
watchEffect(() => emit('loading', isLoading.value))
watchEffect(() => emit('submittable', submittable.value))

// TODO (wvffle): Add loader / Use Suspense
const fetchMetadataChoices = async () => {
  try {
    const response = await axios.get<paths['/api/v2/channels/metadata-choices/']['get']['responses']['200']['content']['application/json']>('channels/metadata-choices/')
    // TODO: Fix schema generation so we don't need to typecast here!
    metadataChoices.value = response.data as unknown as MetadataChoices
  } catch (error) {
    errors.value = (error as BackendError).backendErrors
  }
}

fetchMetadataChoices()

const submit = async () => {
  isLoading.value = true

  const payload = {
    ...newValues,
    description: newValues.description
      ? {
          content_type: 'text/markdown',
          text: newValues.description
        }
      : null
  }

  try {
    const request = () => creating.value
      ? axios.post('channels/', payload)
      : axios.patch(`channels/${props.object?.uuid}`, payload)

    const response = await request()
    if (creating.value) emit('created', response.data)
    else emit('updated', response.data)
  } catch (error) {
    errors.value = (error as BackendError).backendErrors
    emit('errored', errors.value)
  }

  isLoading.value = false
}

defineExpose({
  submit
})
</script>

<template>
  <Layout
    form
    class="ui form"
    @submit.prevent.stop="submit"
  >
    <Alert
      v-if="errors.length > 0"
      red
    >
      <h4 class="header">
        {{ t('components.audio.ChannelForm.header.error') }}
      </h4>
      <ul class="list">
        <li
          v-for="(error, key) in errors"
          :key="key"
        >
          {{ error }}
        </li>
      </ul>
    </Alert>
    <template v-if="metadataChoices">
      <fieldset
        v-if="creating && step === 1"
        class="ui grouped channel-type required field"
      >
        <legend>
          {{ t('components.audio.ChannelForm.legend.purpose') }}
        </legend>
        <div class="ui hidden divider" />
        <div class="field">
          <div
            v-for="(choice, key) in categoryChoices"
            :key="key"
            :class="['ui', 'radio', 'checkbox', {selected: choice.value == newValues.content_category}]"
          >
            <input
              :id="`category-${choice.value}`"
              v-model="newValues.content_category"
              type="radio"
              name="channel-category"
              :value="choice.value"
            >
            <label :for="`category-${choice.value}`">
              <span :class="['right floated', 'placeholder', 'image', 'shifted', {circular: choice.value === 'music'}]" />
              <strong>{{ choice.label }}</strong>
              <div class="ui small hidden divider" />
              {{ choice.helpText }}
            </label>
          </div>
        </div>
      </fieldset>

      <template v-if="!creating || step === 2">
        <div class="ui required field">
          <Input
            v-model="newValues.name"
            type="text"
            required
            :placeholder="labels.namePlaceholder"
            :label="t('components.audio.ChannelForm.label.name')"
          />
        </div>
        <div class="ui required field">
          <Input
            v-model="newValues.username"
            type="text"
            :required="creating"
            :disabled="!creating"
            :placeholder="labels.usernamePlaceholder"
            :label="t('components.audio.ChannelForm.label.username')"
          />
          <template v-if="creating">
            <div class="ui small hidden divider" />
            <p>
              {{ t('components.audio.ChannelForm.help.username') }}
            </p>
          </template>
        </div>
        <div class="six wide column">
          <attachment-input
            v-model="newValues.cover"
            :image-class="newValues.content_category === 'podcast' ? '' : 'circular'"
            @delete="newValues.cover = null"
          >
            {{ t('components.audio.ChannelForm.label.image') }}
          </attachment-input>
        </div>
        <Pills
          :get="model => { newValues.tags = model.currents.map(({ label }) => label) }"
          :set="model => ({
            currents: newValues.tags.map(tag => ({ type: 'custom' as const, label: tag })),
            others: [].map(tag => ({ type: 'custom' as const, label: tag }))
          })"
          :label="t('components.audio.ChannelForm.label.tags')"
        />
        <div
          v-if="newValues.content_category === 'podcast'"
        >
          <label for="channel-language">
            {{ t('components.audio.ChannelForm.label.language') }}
          </label>

          <!-- @vue-ignore -->
          <select
            id="channel-language"
            v-model="newValues.metadata.language"
            name="channel-language"
            required
            class="ui search selection dropdown"
          >
            <option
              v-for="(v, key) in metadataChoices.language"
              :key="key"
              :value="v.value"
            >
              {{ v.label }}
            </option>
          </select>
        </div>
        <div class="ui field">
          <Textarea
            v-model="newValues.description"
            :label="t('components.audio.ChannelForm.label.description')"
            initial-lines="3"
          />
        </div>
        <template
          v-if="newValues.content_category === 'podcast'"
        >
          <div class="ui required field">
            <label for="channel-itunes-category">
              {{ t('components.audio.ChannelForm.label.category') }}
            </label>

            <!-- @vue-ignore -->
            <select
              id="itunes-category"
              v-model="newValues.metadata.itunes_category"
              name="itunes-category"
              required
              class="ui dropdown"
            >
              <option
                v-for="(v, key) in metadataChoices.itunes_category"
                :key="key"
                :value="v.value"
              >
                {{ v.label }}
              </option>
            </select>
          </div>
          <div class="ui field">
            <label for="channel-itunes-category">
              {{ t('components.audio.ChannelForm.label.subcategory') }}
            </label>

            <!-- @vue-ignore -->
            <select
              id="itunes-category"
              v-model="newValues.metadata.itunes_subcategory"
              name="itunes-category"
              :disabled="!newValues.metadata.itunes_category"
              class="ui dropdown"
            >
              <option
                v-for="(v, key) in itunesSubcategories"
                :key="key"
                :value="v"
              >
                {{ v }}
              </option>
            </select>
          </div>
        </template>
        <template
          v-if="newValues.content_category === 'podcast'"
        >
          <Alert blue>
            <span>
              <i class="bi bi-info-circle-fill" />
              {{ t('components.audio.ChannelForm.help.podcastFields') }}
            </span>
          </Alert>
          <div class="ui field">
            <!-- @vue-ignore -->
            <Input
              id="channel-itunes-email"
              v-model="newValues.metadata.owner_email"
              name="channel-itunes-email"
              type="email"
              :label="t('components.audio.ChannelForm.label.email')"
            />
          </div>
          <div class="ui field">
            <!-- @vue-ignore -->
            <Input
              id="channel-itunes-name"
              v-model="newValues.metadata.owner_name"
              name="channel-itunes-name"
              maxlength="255"
              :label="t('components.audio.ChannelForm.label.owner')"
            />
          </div>
        </template>
      </template>
    </template>
    <div
      v-else
      class="ui active inverted dimmer"
    >
      <div class="ui text loader">
        {{ t('components.audio.ChannelForm.loader.loading') }}
      </div>
    </div>
  </Layout>
</template>
