<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { computed, reactive, ref, watch } from 'vue'
import {
  ChevronDown as ChevronDownIcon,
  ChevronUp as ChevronUpIcon,
} from 'lucide-vue-next'
import { useRoute, useRouter } from 'vue-router'
import ElementParentLink from '@/components/ElementParentLink.vue'
import ElementSiblingNavigation from '@/components/ElementSiblingNavigation.vue'
import RichTextEditor from '@/components/RichTextEditor.vue'
import SiteHeader from '@/components/SiteHeader.vue'
import { useElementsStore } from '@/stores/elements'

type ChildMoveDirection = 'up' | 'down'

interface ElementForm {
  title: string
  description: string
  richText: string
  youtubeUrl: string
}

interface ChildElementForm {
  title: string
}

const route = useRoute()
const router = useRouter()
const elementsStore = useElementsStore()
const {
  element,
  childElements,
  parentElement,
  siblingElements,
  isLoading,
  error,
  isSaving,
  isUploadingIconImage,
  isUploadingShortPdf,
  isUploadingLongPdf,
  isManagingChildren,
  saveError,
  childActionError,
} = storeToRefs(elementsStore)
const savedMessage = ref<string | null>(null)
const childStatus = ref<string | null>(null)
const iconImageStatus = ref<string | null>(null)
const shortPdfStatus = ref<string | null>(null)
const longPdfStatus = ref<string | null>(null)
const iconImageInput = ref<HTMLInputElement | null>(null)
const shortPdfInput = ref<HTMLInputElement | null>(null)
const longPdfInput = ref<HTMLInputElement | null>(null)

const form = reactive<ElementForm>({
  title: '',
  description: '',
  richText: '',
  youtubeUrl: '',
})

const childForm = reactive<ChildElementForm>({
  title: '',
})

const elementId = computed(() => {
  const routeElementId = route.params.id

  if (Array.isArray(routeElementId)) {
    return routeElementId.join('/')
  }

  if (typeof routeElementId === 'string') {
    return routeElementId
  }

  return ''
})

const statusMessage = computed(() => {
  return saveError.value ?? savedMessage.value
})

const childStatusMessage = computed(() => {
  return childActionError.value ?? childStatus.value
})

watch(
  elementId,
  (currentElementId) => {
    if (currentElementId === '') {
      return
    }

    savedMessage.value = null
    childStatus.value = null
    childForm.title = ''
    iconImageStatus.value = null
    shortPdfStatus.value = null
    longPdfStatus.value = null
    void elementsStore.fetchElement(currentElementId)
  },
  { immediate: true },
)

watch(element, (currentElement) => {
  if (currentElement === null) {
    return
  }

  form.title = currentElement.title
  form.description = currentElement.description
  form.richText = currentElement.richText
  form.youtubeUrl = currentElement.youtubeUrl ?? ''
})

async function handleSubmit(): Promise<void> {
  if (elementId.value === '') {
    return
  }

  savedMessage.value = null
  const saved = await elementsStore.updateElement(elementId.value, {
    title: form.title,
    description: form.description,
    richText: form.richText,
    youtubeUrl: form.youtubeUrl,
  })

  if (saved) {
    savedMessage.value = 'Saved'
  }
}

async function handleAddChild(): Promise<void> {
  if (elementId.value === '' || childForm.title === '') {
    return
  }

  childStatus.value = null
  const childElement = await elementsStore.createChildElement(elementId.value, {
    title: childForm.title,
  })

  if (childElement === null) {
    return
  }

  childForm.title = ''
  await router.push({
    name: 'admin-element',
    params: { id: childElement.id.toString() },
  })
}

async function handleRemoveChild(childElementId: number): Promise<void> {
  if (elementId.value === '') {
    return
  }

  const confirmed = window.confirm(
    'Delete this child element and all of its descendants?',
  )
  if (!confirmed) {
    return
  }

  childStatus.value = null
  const deleted = await elementsStore.deleteChildElement(
    elementId.value,
    childElementId,
  )

  if (deleted) {
    childStatus.value = 'Child element removed'
  }
}

async function handleMoveChild(
  childElementId: number,
  direction: ChildMoveDirection,
): Promise<void> {
  if (elementId.value === '') {
    return
  }

  const currentIndex = childElements.value.findIndex((childElement) => {
    return childElement.id === childElementId
  })
  if (currentIndex === -1) {
    return
  }

  const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1
  if (targetIndex < 0 || targetIndex >= childElements.value.length) {
    return
  }

  const reorderedChildElements = [...childElements.value]
  const movingChildElement = reorderedChildElements[currentIndex]
  const targetChildElement = reorderedChildElements[targetIndex]
  if (movingChildElement === undefined || targetChildElement === undefined) {
    return
  }

  reorderedChildElements[currentIndex] = targetChildElement
  reorderedChildElements[targetIndex] = movingChildElement
  const childElementIds = reorderedChildElements.map((childElement) => {
    return childElement.id
  })

  childStatus.value = null
  const saved = await elementsStore.reorderChildElements(
    elementId.value,
    childElementIds,
  )

  if (saved) {
    childStatus.value = 'Child order saved'
  }
}

function chooseIconImage(): void {
  iconImageInput.value?.click()
}

function chooseShortPdf(): void {
  shortPdfInput.value?.click()
}

function chooseLongPdf(): void {
  longPdfInput.value?.click()
}

async function handleIconImageChange(changeEvent: Event): Promise<void> {
  const selectedFile = selectedInputFile(changeEvent)
  if (selectedFile === null || elementId.value === '') {
    return
  }

  savedMessage.value = null
  iconImageStatus.value = null
  const uploaded = await elementsStore.uploadElementIconImage(
    elementId.value,
    selectedFile,
  )
  resetInput(changeEvent)

  if (uploaded) {
    iconImageStatus.value = 'Icon image updated'
  }
}

async function handleShortPdfChange(changeEvent: Event): Promise<void> {
  const selectedFile = selectedInputFile(changeEvent)
  if (selectedFile === null || elementId.value === '') {
    return
  }

  savedMessage.value = null
  shortPdfStatus.value = null
  const uploaded = await elementsStore.uploadElementShortPdf(
    elementId.value,
    selectedFile,
  )
  resetInput(changeEvent)

  if (uploaded) {
    shortPdfStatus.value = 'Short PDF updated'
  }
}

async function handleLongPdfChange(changeEvent: Event): Promise<void> {
  const selectedFile = selectedInputFile(changeEvent)
  if (selectedFile === null || elementId.value === '') {
    return
  }

  savedMessage.value = null
  longPdfStatus.value = null
  const uploaded = await elementsStore.uploadElementLongPdf(
    elementId.value,
    selectedFile,
  )
  resetInput(changeEvent)

  if (uploaded) {
    longPdfStatus.value = 'Long PDF updated'
  }
}

async function handleRemoveIconImage(): Promise<void> {
  if (elementId.value === '') {
    return
  }

  savedMessage.value = null
  iconImageStatus.value = null
  const removed = await elementsStore.clearElementIconImage(elementId.value)
  if (removed) {
    iconImageStatus.value = 'Icon image removed'
  }
}

async function handleRemoveShortPdf(): Promise<void> {
  if (elementId.value === '') {
    return
  }

  savedMessage.value = null
  shortPdfStatus.value = null
  const removed = await elementsStore.clearElementShortPdf(elementId.value)
  if (removed) {
    shortPdfStatus.value = 'Short PDF removed'
  }
}

async function handleRemoveLongPdf(): Promise<void> {
  if (elementId.value === '') {
    return
  }

  savedMessage.value = null
  longPdfStatus.value = null
  const removed = await elementsStore.clearElementLongPdf(elementId.value)
  if (removed) {
    longPdfStatus.value = 'Long PDF removed'
  }
}

function selectedInputFile(changeEvent: Event): File | null {
  const target = changeEvent.target
  if (!(target instanceof HTMLInputElement)) {
    return null
  }

  if (target.files === null || target.files.length === 0) {
    return null
  }

  return target.files.item(0)
}

function resetInput(changeEvent: Event): void {
  const target = changeEvent.target
  if (target instanceof HTMLInputElement) {
    target.value = ''
  }
}
</script>

<template>
  <div class="admin-element-page">
    <SiteHeader />
    <main class="admin-element-page__main" data-cy="admin-element-page">
      <header class="admin-element-page__header">
        <ElementParentLink
          v-if="parentElement !== null"
          :parent-element="parentElement"
          data-cy="admin-parent-link"
          route-name="admin-element"
        />
        <h1 class="admin-element-page__heading">Edit Element</h1>
      </header>

      <p v-if="isLoading" class="admin-element-page__status">
        Loading element...
      </p>
      <p
        v-else-if="error"
        class="admin-element-page__status admin-element-page__status--error"
        data-cy="admin-element-load-error"
      >
        {{ error }}
      </p>
      <form
        v-else-if="element"
        class="admin-element-page__form"
        @submit.prevent="handleSubmit"
      >
        <label class="admin-element-page__field">
          <span class="admin-element-page__label">Title</span>
          <input
            v-model="form.title"
            class="admin-element-page__input"
            data-cy="admin-element-title"
            type="text"
          />
        </label>

        <label class="admin-element-page__field">
          <span class="admin-element-page__label">Description</span>
          <textarea
            v-model="form.description"
            class="admin-element-page__textarea"
            data-cy="admin-element-description"
            rows="4"
          />
        </label>

        <section class="admin-element-page__media-field">
          <span class="admin-element-page__label">Icon Image</span>
          <img
            v-if="element.iconImageUrl !== null"
            :src="element.iconImageUrl"
            :alt="`${element.title} icon`"
            class="admin-element-page__icon-preview"
            data-cy="admin-element-current-icon"
          />
          <p v-else class="admin-element-page__empty-media">No icon image</p>
          <div class="admin-element-page__media-actions">
            <input
              ref="iconImageInput"
              class="admin-element-page__file-input"
              data-cy="admin-element-icon-image-input"
              type="file"
              accept="image/jpeg,image/png,image/webp"
              :disabled="isUploadingIconImage"
              @change="handleIconImageChange"
            />
            <button
              class="admin-element-page__secondary-button"
              type="button"
              :disabled="isUploadingIconImage"
              @click="chooseIconImage"
            >
              {{ isUploadingIconImage ? 'Uploading...' : 'Choose icon image' }}
            </button>
            <button
              v-if="element.iconImageUrl !== null"
              class="admin-element-page__secondary-button"
              type="button"
              :disabled="isSaving || isUploadingIconImage"
              @click="handleRemoveIconImage"
            >
              Remove icon image
            </button>
          </div>
          <p
            v-if="iconImageStatus !== null"
            :class="[
              'admin-element-page__status',
              'admin-element-page__status--inline',
            ]"
            data-cy="admin-element-icon-image-status"
            aria-live="polite"
          >
            {{ iconImageStatus }}
          </p>
        </section>

        <div class="admin-element-page__field">
          <span class="admin-element-page__label">Rich Text</span>
          <RichTextEditor v-model="form.richText" :disabled="isSaving" />
        </div>

        <section class="admin-element-page__media-field">
          <span class="admin-element-page__label">Short PDF</span>
          <a
            v-if="element.shortPdfPath !== null"
            :href="element.shortPdfPath"
            class="admin-element-page__pdf-link"
            data-cy="admin-element-current-short-pdf"
            target="_blank"
            rel="noreferrer"
          >
            View current short PDF
          </a>
          <p v-else class="admin-element-page__empty-media">No short PDF</p>
          <div class="admin-element-page__media-actions">
            <input
              ref="shortPdfInput"
              class="admin-element-page__file-input"
              data-cy="admin-element-short-pdf-input"
              type="file"
              accept="application/pdf"
              :disabled="isUploadingShortPdf"
              @change="handleShortPdfChange"
            />
            <button
              class="admin-element-page__secondary-button"
              type="button"
              :disabled="isUploadingShortPdf"
              @click="chooseShortPdf"
            >
              {{ isUploadingShortPdf ? 'Uploading...' : 'Choose short PDF' }}
            </button>
            <button
              v-if="element.shortPdfPath !== null"
              class="admin-element-page__secondary-button"
              type="button"
              :disabled="isSaving || isUploadingShortPdf"
              @click="handleRemoveShortPdf"
            >
              Remove short PDF
            </button>
          </div>
          <p
            v-if="shortPdfStatus !== null"
            :class="[
              'admin-element-page__status',
              'admin-element-page__status--inline',
            ]"
            data-cy="admin-element-short-pdf-status"
            aria-live="polite"
          >
            {{ shortPdfStatus }}
          </p>
        </section>

        <section class="admin-element-page__media-field">
          <span class="admin-element-page__label">Long PDF</span>
          <a
            v-if="element.longPdfPath !== null"
            :href="element.longPdfPath"
            class="admin-element-page__pdf-link"
            data-cy="admin-element-current-long-pdf"
            target="_blank"
            rel="noreferrer"
          >
            View current long PDF
          </a>
          <p v-else class="admin-element-page__empty-media">No long PDF</p>
          <div class="admin-element-page__media-actions">
            <input
              ref="longPdfInput"
              class="admin-element-page__file-input"
              data-cy="admin-element-long-pdf-input"
              type="file"
              accept="application/pdf"
              :disabled="isUploadingLongPdf"
              @change="handleLongPdfChange"
            />
            <button
              class="admin-element-page__secondary-button"
              type="button"
              :disabled="isUploadingLongPdf"
              @click="chooseLongPdf"
            >
              {{ isUploadingLongPdf ? 'Uploading...' : 'Choose long PDF' }}
            </button>
            <button
              v-if="element.longPdfPath !== null"
              class="admin-element-page__secondary-button"
              type="button"
              :disabled="isSaving || isUploadingLongPdf"
              @click="handleRemoveLongPdf"
            >
              Remove long PDF
            </button>
          </div>
          <p
            v-if="longPdfStatus !== null"
            :class="[
              'admin-element-page__status',
              'admin-element-page__status--inline',
            ]"
            data-cy="admin-element-long-pdf-status"
            aria-live="polite"
          >
            {{ longPdfStatus }}
          </p>
        </section>

        <label class="admin-element-page__field">
          <span class="admin-element-page__label">YouTube URL</span>
          <input
            v-model="form.youtubeUrl"
            class="admin-element-page__input"
            data-cy="admin-element-youtube-url"
            type="text"
          />
        </label>

        <section class="admin-element-page__children">
          <span class="admin-element-page__label">Child Elements</span>
          <ul
            v-if="childElements.length > 0"
            class="admin-element-page__child-list"
            data-cy="admin-child-list"
          >
            <li
              v-for="(childElement, childIndex) in childElements"
              :key="childElement.id"
              class="admin-element-page__child-item"
              data-cy="admin-child-item"
            >
              <div class="admin-element-page__child-body">
                <RouterLink
                  :to="{
                    name: 'admin-element',
                    params: { id: childElement.id },
                  }"
                  class="admin-element-page__child-link"
                  data-cy="admin-child-edit-link"
                >
                  {{ childElement.title }}
                </RouterLink>
                <p
                  v-if="childElement.description !== ''"
                  class="admin-element-page__child-description"
                >
                  {{ childElement.description }}
                </p>
              </div>
              <div class="admin-element-page__child-actions">
                <div class="admin-element-page__child-order-actions">
                  <button
                    class="admin-element-page__icon-button"
                    data-cy="admin-move-child-up"
                    type="button"
                    :disabled="isManagingChildren || childIndex === 0"
                    aria-label="Move child up"
                    title="Move child up"
                    @click="handleMoveChild(childElement.id, 'up')"
                  >
                    <ChevronUpIcon :size="16" aria-hidden="true" />
                  </button>
                  <button
                    class="admin-element-page__icon-button"
                    data-cy="admin-move-child-down"
                    type="button"
                    :disabled="
                      isManagingChildren ||
                      childIndex === childElements.length - 1
                    "
                    aria-label="Move child down"
                    title="Move child down"
                    @click="handleMoveChild(childElement.id, 'down')"
                  >
                    <ChevronDownIcon :size="16" aria-hidden="true" />
                  </button>
                </div>
                <button
                  class="admin-element-page__secondary-button"
                  data-cy="admin-remove-child"
                  type="button"
                  :disabled="isManagingChildren"
                  @click="handleRemoveChild(childElement.id)"
                >
                  Remove
                </button>
              </div>
            </li>
          </ul>
          <p
            v-else
            class="admin-element-page__empty-media"
            data-cy="admin-child-empty"
          >
            No child elements
          </p>
          <div class="admin-element-page__child-add">
            <label class="admin-element-page__child-title-field">
              <span class="admin-element-page__label">New child title</span>
              <input
                v-model="childForm.title"
                class="admin-element-page__input"
                data-cy="admin-child-title"
                type="text"
                :disabled="isManagingChildren"
                @keydown.enter.prevent="handleAddChild"
              />
            </label>
            <button
              class="admin-element-page__secondary-button"
              data-cy="admin-add-child"
              type="button"
              :disabled="isManagingChildren || childForm.title === ''"
              @click="handleAddChild"
            >
              {{ isManagingChildren ? 'Adding...' : 'Add child' }}
            </button>
          </div>
          <p
            v-if="childStatusMessage !== null"
            :class="[
              'admin-element-page__status',
              'admin-element-page__status--inline',
            ]"
            data-cy="admin-child-status"
            aria-live="polite"
          >
            {{ childStatusMessage }}
          </p>
        </section>

        <div class="admin-element-page__actions">
          <button
            class="admin-element-page__save"
            data-cy="admin-element-save"
            type="submit"
            :disabled="isSaving"
          >
            {{ isSaving ? 'Saving...' : 'Save' }}
          </button>
          <p
            v-if="statusMessage !== null"
            :class="[
              'admin-element-page__status',
              'admin-element-page__status--inline',
            ]"
            data-cy="admin-element-status"
            aria-live="polite"
          >
            {{ statusMessage }}
          </p>
        </div>

        <ElementSiblingNavigation
          :current-element-id="element.id"
          :sibling-elements="siblingElements"
          data-cy-prefix="admin"
          route-name="admin-element"
        />
      </form>
    </main>
  </div>
</template>

<style scoped>
.admin-element-page {
  min-height: 100vh;
  background: var(--color-white);
}

.admin-element-page__main {
  min-height: 100vh;
  padding: 4rem 2rem 5rem;
}

.admin-element-page__header,
.admin-element-page__form,
.admin-element-page__status {
  max-width: 48rem;
  margin-right: auto;
  margin-left: auto;
}

.admin-element-page__heading {
  margin: 0.35rem 0 0;
  color: #2c2c2c;
  font-family: var(--font-serif);
  font-size: 2rem;
  font-weight: 400;
  line-height: 1.2;
}

.admin-element-page__form {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  margin-top: 2rem;
}

.admin-element-page__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.admin-element-page__media-field,
.admin-element-page__children {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.7rem;
  padding: 0.9rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
}

.admin-element-page__label {
  color: var(--color-text-muted);
  font-size: 0.9rem;
  font-weight: 500;
}

.admin-element-page__input,
.admin-element-page__textarea {
  width: 100%;
  padding: 0.7rem 0.8rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  color: var(--color-text);
  background: var(--color-white);
  font-family: var(--font-sans);
  font-size: 0.96rem;
  line-height: 1.5;
}

.admin-element-page__input:focus,
.admin-element-page__textarea:focus {
  border-color: var(--color-slate);
  outline: none;
}

.admin-element-page__textarea {
  resize: vertical;
}

.admin-element-page__icon-preview {
  width: 6rem;
  height: 6rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  object-fit: contain;
}

.admin-element-page__empty-media {
  margin: 0;
  color: var(--color-text-muted);
  font-size: 0.95rem;
}

.admin-element-page__media-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.7rem;
}

.admin-element-page__child-list {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  width: 100%;
  padding: 0;
  margin: 0;
  list-style: none;
}

.admin-element-page__child-item {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.7rem;
  padding: 0.75rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
}

.admin-element-page__child-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.admin-element-page__child-link {
  color: var(--color-slate);
  font-size: 0.95rem;
  font-weight: 600;
}

.admin-element-page__child-link:hover,
.admin-element-page__child-link:focus-visible {
  color: var(--color-text);
}

.admin-element-page__child-description {
  margin: 0;
  color: var(--color-text-muted);
  font-size: 0.9rem;
  line-height: 1.45;
}

.admin-element-page__child-actions {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: 0.45rem;
}

.admin-element-page__child-order-actions {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.admin-element-page__icon-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  color: var(--color-text);
  background: var(--color-white);
  cursor: pointer;
}

.admin-element-page__icon-button:disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

.admin-element-page__icon-button:hover:not(:disabled),
.admin-element-page__icon-button:focus-visible {
  border-color: var(--color-slate);
  color: var(--color-slate);
}

.admin-element-page__child-add {
  display: flex;
  align-items: end;
  gap: 0.7rem;
  width: 100%;
}

.admin-element-page__child-title-field {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 0.35rem;
  min-width: 0;
}

.admin-element-page__file-input {
  display: none;
}

.admin-element-page__secondary-button,
.admin-element-page__pdf-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.55rem 0.95rem;
  border: 1px solid var(--color-border);
  border-radius: 4px;
  color: var(--color-text);
  background: var(--color-white);
  font-family: var(--font-sans);
  font-size: 0.9rem;
  font-weight: 600;
}

.admin-element-page__secondary-button {
  cursor: pointer;
}

.admin-element-page__secondary-button:disabled {
  cursor: not-allowed;
  opacity: 0.65;
}

.admin-element-page__secondary-button:hover:not(:disabled),
.admin-element-page__secondary-button:focus-visible,
.admin-element-page__pdf-link:hover,
.admin-element-page__pdf-link:focus-visible {
  border-color: var(--color-slate);
}

.admin-element-page__actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-top: 0.5rem;
}

.admin-element-page__save {
  padding: 0.7rem 1.4rem;
  border: 1px solid var(--color-slate);
  border-radius: 4px;
  color: var(--color-white);
  background: var(--color-slate);
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 600;
  cursor: pointer;
}

.admin-element-page__save:disabled {
  cursor: not-allowed;
  opacity: 0.65;
}

.admin-element-page__status {
  margin-top: 2rem;
  color: var(--color-text-muted);
}

.admin-element-page__status--inline {
  margin: 0;
  color: var(--color-slate);
}

.admin-element-page__status--error {
  color: #9f2f2f;
}

@media (max-width: 768px) {
  .admin-element-page__main {
    padding: 2.5rem 1rem 4rem;
  }

  .admin-element-page__actions {
    align-items: stretch;
    flex-direction: column;
  }

  .admin-element-page__media-actions {
    align-items: stretch;
    flex-direction: column;
    width: 100%;
  }

  .admin-element-page__child-add {
    align-items: stretch;
    flex-direction: column;
  }

  .admin-element-page__child-item {
    flex-direction: column;
  }

  .admin-element-page__child-actions {
    align-self: flex-end;
  }
}
</style>
