<template>
  <div class="flex flex-col w-full lg:max-w-4xl mx-auto">
    <slot />

    <UStepper ref="stepper" v-model="activeStepperItemIndex" :items="stepperItems" class="w-full" />

    <h1 v-if="currentFormElementSection?.title" class="text-xl text-pretty font-bold text-highlighted">
      {{ currentFormElementSection.title }}
    </h1>

    <UCard
      v-for="{ subsection, sectionIndex } in visibleSubsections"
      :key="getSubsectionKey(currentFormElementSection, sectionIndex, subsection)"
      variant="subtle"
      class="mb-6"
    >
      <div class="mb-4">
        <h2 class="text-lg font-semibold text-highlighted">{{ subsection.title }}</h2>
        <p v-if="subsection.subtitle" class="text-sm text-dimmed">{{ subsection.subtitle }}</p>
      </div>
      <FormEngine
        :model-value="subsection.formElements"
        :visibility-map="visibilityMap"
        :application-form-id="applicationFormId"
        :disabled="disabled"
        :all-form-elements="allFormElements"
        @update:model-value="
          (elements) =>
            handleFormElementUpdate(elements, getSubsectionKey(currentFormElementSection, sectionIndex, subsection))
        "
        @add:input-form="
          (position) =>
            handleAddInputForm(position, getSubsectionKey(currentFormElementSection, sectionIndex, subsection))
        "
        @clone:element="
          (element, position) =>
            handleCloneElement(element, position, getSubsectionKey(currentFormElementSection, sectionIndex, subsection))
        "
      />
    </UCard>

    <UCard v-if="visibleSubsections.length === 0" variant="subtle" class="mb-6">
      <div class="text-center py-8 text-dimmed">
        <UIcon name="i-lucide-eye-off" class="w-12 h-12 mx-auto mb-3 opacity-50" />
        <p>{{ $t('applicationForms.noVisibleElements') }}</p>
      </div>
    </UCard>

    <div class="flex gap-2 justify-between">
      <UButton leading-icon="i-lucide-arrow-left" :disabled="!stepper?.hasPrev" @click="handleNavigate('backward')">
        {{ $t('applicationForms.navigation.previous') }}
      </UButton>

      <div class="flex flex-wrap items-center gap-1.5">
        <UButton trailing-icon="i-lucide-save" :disabled="disabled" variant="outline" size="lg" @click="emit('save')">
          {{ $t('applicationForms.navigation.save') }}
        </UButton>
        <UButton
          v-if="stepper?.hasNext"
          trailing-icon="i-lucide-arrow-right"
          size="lg"
          @click="handleNavigate('forward')"
        >
          {{ $t('applicationForms.navigation.next') }}
        </UButton>
        <UButton
          v-if="!stepper?.hasNext"
          trailing-icon="i-lucide-send-horizontal"
          :disabled="disabled"
          size="lg"
          @click="emit('submit')"
        >
          {{ $t('applicationForms.navigation.submit') }}
        </UButton>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import type {
  ApplicationFormDto,
  FormElementSectionDto,
  FormElementDto,
  FormElementSubSectionDto
} from '~~/.api-client'

const props = defineProps<{
  formElementSections: FormElementSectionDto[]
  initialSectionIndex?: number
  applicationFormId?: string
  disabled?: boolean
}>()

const emit = defineEmits<{
  save: []
  submit: []
  'add-input-form': [updatedForm: ApplicationFormDto | undefined]
  'update:formElementSections': [sections: FormElementSectionDto[]]
  navigate: [{ direction: 'forward' | 'backward'; index: number }]
}>()

const { stepper, activeStepperItemIndex, stepperItems, currentFormElementSection, navigateStepper } = useFormStepper(
  computed(() => props.formElementSections)
)

const { evaluateFormElementVisibility } = useFormElementVisibility()
const { clearHiddenFormElementValues } = useFormElementValueClearing()
const { processSpawnTriggers } = useSectionSpawning()
const { cloneElement } = useClonableElements()

const previousVisibilityMap = ref<Map<string, boolean>>(new Map())

const allFormElements = computed(() => {
  return props.formElementSections
    .filter((section) => section.isTemplate !== true)
    .flatMap((section) => section.formElementSubSections?.flatMap((subsection) => subsection.formElements) ?? [])
})

const visibilityMap = computed(() => {
  return evaluateFormElementVisibility(allFormElements.value)
})

const visibleSubsections = computed(() => {
  if (!currentFormElementSection.value?.formElementSubSections) {
    return []
  }

  return currentFormElementSection.value.formElementSubSections
    .map((subsection) => ({ subsection, sectionIndex: currentSectionIndex.value }))
    .filter(({ subsection }) => {
      return subsection.formElements.some((element) => {
        const key = element.id || element.reference
        return key ? visibilityMap.value.get(key) !== false : true
      })
    })
})

const currentSectionIndex = computed(() => {
  if (!currentFormElementSection.value) return -1
  return props.formElementSections.indexOf(currentFormElementSection.value)
})

onMounted(() => {
  if (props.initialSectionIndex !== undefined) {
    activeStepperItemIndex.value = props.initialSectionIndex
  }
  previousVisibilityMap.value = visibilityMap.value
})

async function handleAddInputForm(position: number, subsectionKey: string) {
  const foundSubsection = findSubsectionByKey(subsectionKey)
  if (!foundSubsection) return

  const { addInputFormElement } = useFormElementManagement()
  const updatedElements = addInputFormElement(foundSubsection.formElements, position)

  const updatedSections = updateSubsectionElements(props.formElementSections, subsectionKey, updatedElements)
  emit('update:formElementSections', updatedSections)
}

function findSubsectionByKey(subsectionKey: string): FormElementSubSectionDto | undefined {
  for (let sectionIdx = 0; sectionIdx < props.formElementSections.length; sectionIdx++) {
    const section = props.formElementSections[sectionIdx]
    if (!section) continue

    for (let i = 0; i < section.formElementSubSections.length; i++) {
      const subsection = section.formElementSubSections[i]
      if (subsection && getSubsectionKey(section, sectionIdx, subsection) === subsectionKey) {
        return subsection
      }
    }
  }
  return undefined
}

async function handleNavigate(direction: 'forward' | 'backward') {
  await navigateStepper(direction)
  emit('navigate', { direction, index: activeStepperItemIndex.value })
}

function handleCloneElement(element: FormElementDto, position: number, subsectionKey: string) {
  const clonedElement = cloneElement(element, allFormElements.value)

  const updatedSections = props.formElementSections.map((section, sectionIdx) => ({
    ...section,
    formElementSubSections: section.formElementSubSections.map((subsection) => {
      if (getSubsectionKey(section, sectionIdx, subsection) === subsectionKey) {
        const newFormElements = [...subsection.formElements]
        newFormElements.splice(position + 1, 0, clonedElement as FormElementDto)
        return { ...subsection, formElements: newFormElements }
      }
      return subsection
    })
  }))

  emit('update:formElementSections', updatedSections)
}

function handleFormElementUpdate(updatedFormElements: FormElementDto[], subsectionKey: string) {
  let updatedSections = updateSubsectionElements(props.formElementSections, subsectionKey, updatedFormElements)
  updatedSections = processSpawnTriggers(updatedSections, updatedFormElements)
  updatedSections = clearNewlyHiddenFormElements(updatedSections)
  emit('update:formElementSections', updatedSections)
}

function clearNewlyHiddenFormElements(sections: FormElementSectionDto[]): FormElementSectionDto[] {
  const allElements = sections.flatMap(
    (section) => section.formElementSubSections?.flatMap((subsection) => subsection.formElements) ?? []
  )
  const newVisibilityMap = evaluateFormElementVisibility(allElements)

  const clearedSections = clearHiddenFormElementValues(sections, previousVisibilityMap.value, newVisibilityMap)
  previousVisibilityMap.value = newVisibilityMap

  return clearedSections
}

function getSubsectionKey(
  section: FormElementSectionDto | undefined,
  sectionIndex: number,
  subsection: FormElementSubSectionDto
): string {
  if (subsection.id) {
    return subsection.id
  }

  const spawnedFrom = section?.spawnedFromElementReference ?? 'root'
  const templateRef = section?.templateReference ?? 'no_tmpl'
  const title = subsection.title ?? ''

  return `spawned:${spawnedFrom}|tmpl:${templateRef}|sec:${sectionIndex}|title:${title}`
}

function updateSubsectionElements(
  sections: FormElementSectionDto[],
  subsectionKey: string,
  updatedFormElements: FormElementDto[]
): FormElementSectionDto[] {
  return sections.map((section, sectionIdx) => ({
    ...section,
    formElementSubSections: section.formElementSubSections.map((subsection) => {
      if (getSubsectionKey(section, sectionIdx, subsection) === subsectionKey) {
        return { ...subsection, formElements: updatedFormElements }
      }
      return subsection
    })
  }))
}
</script>
