<script setup lang="ts">
import Draggable from 'vuedraggable'
import { RelationTypes, UITypes, getSystemColumns, isVirtualCol } from 'nocodb-sdk'
import { useToast } from 'vue-toastification'
import type { Permission } from '~/composables/useUIPermission/rolePermissions'
import { computed, inject, useDebounceFn } from '#imports'
import { ActiveViewInj, FieldsInj, IsFormInj, MetaInj } from '~/context'
import { extractSdkResponseErrorMsg } from '~/utils'
import MdiPlusIcon from '~icons/mdi/plus'
import MdiDragIcon from '~icons/mdi/drag-vertical'
import MdiHideIcon from '~icons/mdi/eye-off-outline'

provide(IsFormInj, true)

// todo: generate hideCols based on default values
const hiddenCols = ['created_at', 'updated_at']

const toast = useToast()

const state = useGlobal()

const { $api, $e } = useNuxtApp()

const { isUIAllowed } = useUIPermission()

const formState = reactive({
  heading: '',
  subheading: '',
  submit_another_form: false,
  show_blank_form: false,
  email: false,
  success_msg: '',
  fields: {},
})

const isEditable = isUIAllowed('editFormView' as Permission)

const meta = inject(MetaInj)

const view = inject(ActiveViewInj)

if (meta) useProvideColumnCreateStore(meta)

const {
  loadData,
  paginationData,
  formattedData: data,
  loadFormView,
  insertRow,
  formColumnData,
  formViewData,
  changePage,
  updateRowProperty,
} = useViewData(meta, view as any)

const { showAll, hideAll, saveOrUpdate } = useViewColumns(view, meta as any, false, () => {
  loadFormView()
  setFormData()
})

const columns = computed(() => meta?.value?.columns || [])

const localColumns = ref<Record<string, any>>([])

const hiddenColumns = ref<Record<string, any>>([])

const availableColumns = inject(FieldsInj, ref([]))

const systemFieldsIds = ref<Record<string, any>>([])

const showColumnDropdown = ref(false)

const drag = ref(false)

const activeRow = ref('')

const formView = ref({})

function updateView() {}

function submitForm() {
  // TODO: validation
  insertRow(formState)
}

function isDbRequired(column: Record<string, any>) {
  if (hiddenCols.includes(column.fk_column_id)) {
    return false
  }

  let isRequired =
    // confirm column is not virtual
    (!isVirtualCol(column) &&
      // column required / not null
      column.rqd &&
      // column default value
      !column.cdf &&
      // confirm it's not foreign key
      !columns.value.some(
        (c: Record<string, any>) =>
          c.uidt === UITypes.LinkToAnotherRecord &&
          c?.colOptions?.type === RelationTypes.BELONGS_TO &&
          column.fk_column_id === c.colOptions.fk_child_column_id,
      )) ||
    // primary column
    (column.pk && !column.ai && !column.cdf)
  if (column.uidt === UITypes.LinkToAnotherRecord && column.colOptions.type === RelationTypes.BELONGS_TO) {
    const col = columns.value.find((c: Record<string, any>) => c.id === column.colOptions.fk_child_column_id) as Record<
      string,
      any
    >
    if ((col.rqd && !col.default) || (formState as Record<string, any>)?.fields[column.title]?.required) {
      isRequired = true
    }
  }

  return isRequired
}

function onMove(event: any) {
  const { newIndex, element, oldIndex } = event.added || event.moved || event.removed

  if (event.added) {
    element.show = true
  }

  if (event.removed) {
    element.show = false
    saveOrUpdate(element, oldIndex)
  } else {
    if (!localColumns.value.length || localColumns.value.length === 1) {
      element.order = 1
    } else if (localColumns.value.length - 1 === newIndex) {
      element.order = (localColumns.value[newIndex - 1]?.order || 0) + 1
    } else if (newIndex === 0) {
      element.order = (localColumns.value[1]?.order || 0) / 2
    } else {
      element.order = (localColumns.value[newIndex - 1]?.order || 0) + (localColumns.value[newIndex + 1].order || 0) / 2
    }
    saveOrUpdate(element, newIndex)
  }

  $e('a:form-view:reorder')
}

function hideColumn(idx: number) {
  if (isDbRequired(localColumns.value[idx]) || localColumns.value[idx].required) {
    toast.info("Required field can't be removed")
    return
  }

  saveOrUpdate(
    {
      ...localColumns.value[idx],
      show: false,
    },
    idx,
  )
  ;(localColumns.value[idx] as any).show = false

  $e('a:form-view:hide-columns')
}

async function addAllColumns() {
  for (const col of (formColumnData as Record<string, any>)?.value) {
    if (!systemFieldsIds.value.includes(col.fk_column_id)) {
      console.log(col)
      col.show = true
    }
  }
  await showAll(systemFieldsIds.value)
  $e('a:form-view:add-all')
}

async function removeAllColumns() {
  for (const col of (formColumnData as Record<string, any>)?.value) {
    if (isDbRequired(col) || !!col.required) {
      continue
    }
    col.show = false
  }
  await hideAll(
    (localColumns as Record<string, any>)?.value
      .filter((f: Record<string, any>) => isDbRequired(f) || !!f.required)
      .map((f: Record<string, any>) => f.fk_column_id),
  )
  $e('a:form-view:remove-all')
}

async function checkSMTPStatus() {}

function setFormData() {
  Object.assign(formState, {
    heading: formViewData.value?.heading,
    subheading: formViewData.value?.subheading,
    submit_another_form: !!formViewData.value?.submit_another_form,
    show_blank_form: !!formViewData.value?.email,
    email: formViewData.value?.submit_another_form,
    success_msg: formViewData.value?.success_msg,
    fields: (formColumnData.value as Record<string, any>[])?.map((c) => ({
      [c.title]: {
        required: false,
      },
    })),
  })

  const col = (formColumnData as Record<string, any>)?.value

  localColumns.value = col
    .filter((f: Record<string, any>) => f.show && f.uidt !== UITypes.Rollup && f.uidt !== UITypes.Lookup)
    .sort((a: Record<string, any>, b: Record<string, any>) => a.order - b.order)
    .map((c: Record<string, any>) => ({ ...c, required: !!(c.required || 0) }))

  systemFieldsIds.value = getSystemColumns(col).map((c: Record<string, any>) => c.fk_column_id)

  hiddenColumns.value = col.filter((f: Record<string, any>) => !f.show && !systemFieldsIds.value.includes(f.fk_column_id))
}

function isRequired(_columnObj: Record<string, any>, required = false) {
  let columnObj = _columnObj
  if (
    columnObj.uidt === UITypes.LinkToAnotherRecord &&
    columnObj.colOptions &&
    columnObj.colOptions.type === RelationTypes.BELONGS_TO
  ) {
    columnObj = columns.value.find((c: Record<string, any>) => c.id === columnObj.colOptions.fk_child_column_id) as Record<
      string,
      any
    >
  }

  return required || (columnObj && columnObj.rqd && !columnObj.cdf)
}

const updateColMeta = useDebounceFn(async (col: Record<string, any>) => {
  if (col.id) {
    try {
      $api.dbView.formColumnUpdate(col.id, col)
    } catch (e: any) {
      toast.error(await extractSdkResponseErrorMsg(e))
    }
  }
}, 250)

onMounted(async () => {
  await loadFormView()
  setFormData()
})

// TODO: check if it's required
watch(
  () => formColumnData || formViewData,
  (v) => {
    setFormData()
  },
)
</script>

<template>
  <a-row class="h-full flex overflow-auto scrollbar-thin-primary">
    <a-col v-if="isEditable" :span="8" class="bg-[#f7f7f7] shadow-md pa-5">
      <div class="flex">
        <div class="flex flex-row flex-1 text-lg">
          <span>
            <!-- Fields -->
            {{ $t('objects.fields') }}
          </span>
        </div>
        <div class="flex flex-row">
          <div class="cursor-pointer mr-2">
            <span
              v-if="hiddenColumns.length"
              class="mr-2"
              style="border-bottom: 2px solid rgb(218, 218, 218)"
              @click="addAllColumns"
            >
              <!-- Add all -->
              {{ $t('general.addAll') }}
            </span>
            <span
              v-if="localColumns.length"
              class="ml-2"
              style="border-bottom: 2px solid rgb(218, 218, 218)"
              @click="removeAllColumns"
            >
              <!-- Remove all -->
              {{ $t('general.removeAll') }}
            </span>
          </div>
        </div>
      </div>
      <draggable :list="hiddenColumns" draggable=".item" group="a-card" @start="drag = true" @end="drag = false">
        <template #item="{ element }">
          <a-card size="small" class="ma-0 pa-0 cursor-pointer item mb-2">
            <div class="flex">
              <div class="flex flex-row flex-1">
                {{ element.required }}
                <SmartsheetHeaderVirtualCell
                  v-if="isVirtualCol(element)"
                  :column="{ ...element, title: element.label || element.title }"
                  :required="isRequired(element, element.required)"
                />
                <SmartsheetHeaderCell
                  v-else
                  class="w-full"
                  :column="{ ...element, title: element.label || element.title }"
                  :required="isRequired(element, element.required)"
                />
              </div>
              <div class="flex flex-row">
                <MdiDragIcon class="flex flex-1" />
              </div>
            </div>
          </a-card>
        </template>
        <template #footer>
          <div class="mt-4 border-dashed border-2 border-gray-400 py-3 text-gray-400 text-center">
            <!-- Drag and drop fields here to hide -->
            {{ $t('msg.info.dragDropHide') }}
          </div>
          <a-button type="link" class="w-full caption mt-2" size="large" @click="showColumnDropdown = true">
            <div class="flex items-center prose-sm justify-center text-gray-400">
              <MdiPlusIcon />
              <!-- Add new field to this table -->
              {{ $t('activity.addField') }}
            </div>
          </a-button>
          <!-- TODO #1: set showColumnDropdown to false after saving  -->
          <!-- TODO #2: make the component out of sidebar  -->
          <!-- TODO #3: reload view  -->
          <SmartsheetColumnEditOrAdd v-if="showColumnDropdown" @click.stop @cancel="showColumnDropdown = false" />
        </template>
      </draggable>
    </a-col>
    <a-col :span="isEditable ? 16 : 24">
      <a-card class="h-full">
        <a-form :model="formState" class="bg-[#fefefe]">
          <!-- Header -->
          <div class="pb-2">
            <a-form-item class="ma-0 gap-0 pa-0">
              <a-input
                v-model:value="formState.heading"
                class="w-full text-bold text-h3"
                size="large"
                hide-details
                placeholder="Form Title"
                :bordered="false"
                @blur="updateView()"
                @keydown.enter="updateView()"
              />
            </a-form-item>
          </div>
          <!-- Sub Header -->
          <a-form-item>
            <a-input
              v-model:value="formState.subheading"
              class="w-full"
              size="large"
              hide-details
              :placeholder="$t('msg.info.formDesc')"
              :bordered="false"
              @click="updateView"
            />
          </a-form-item>
        </a-form>
        <draggable
          :list="localColumns"
          item-key="fk_column_id"
          draggable=".item"
          group="div"
          class="h-100"
          @change="onMove($event)"
          @start="drag = true"
          @end="drag = false"
        >
          <template #item="{ element, index }">
            <div class="nc-editable item cursor-pointer hover:bg-primary/10 pa-3" @click="activeRow = element.title">
              <div class="flex">
                <div class="flex flex-1">
                  <SmartsheetHeaderVirtualCell
                    v-if="isVirtualCol(element)"
                    :column="{ ...element, title: element.label || element.title }"
                    :required="isRequired(element, element.required)"
                  />
                  <SmartsheetHeaderCell
                    v-else
                    :column="{ ...element, title: element.label || element.title }"
                    :required="isRequired(element, element.required)"
                  />
                </div>
                <div v-if="isUIAllowed('editFormView')" class="flex">
                  <MdiHideIcon class="opacity-0 nc-field-remove-icon" @click.stop="hideColumn(index)" />
                </div>
              </div>
              <div class="nc-input">
                <SmartsheetVirtualCell v-if="isVirtualCol(element)" v-model="formState[element.title]" :column="element" />
                <SmartsheetCell v-else v-model="formState[element.title]" :column="element" :edit-enabled="true" />
              </div>

              <div v-if="activeRow === element.title" class="">
                <a-input
                  v-model:value="element.label"
                  class="nc-input"
                  size="large"
                  :placeholder="$t('msg.info.formInput')"
                  @change="updateColMeta(element)"
                >
                </a-input>
                <a-input
                  v-model:value="element.description"
                  class="nc-input"
                  :placeholder="$t('msg.info.formHelpText')"
                  size="large"
                  @change="updateColMeta(element)"
                />
                <a-switch
                  v-model:checked="element.required"
                  class="mb-2"
                  :checked-children="$t('general.required')"
                  un-checked-children="Not Required"
                  @change="updateColMeta(element)"
                ></a-switch>
              </div>
              <span class="text-gray-500">{{ element.description }}</span>
            </div>
          </template>
          <template #footer>
            <div v-if="!localColumns.length" class="mt-4 border-dashed border-2 border-gray-400 py-3 text-gray-400 text-center">
              Drag and drop fields here to add
            </div>
          </template>
        </draggable>

        <div class="justify-center flex mt-5">
          <a-button class="flex items-center gap-2 !bg-primary text-white rounded" size="large" @click="submitForm">
            <!-- Submit -->
            {{ $t('general.submit') }}
          </a-button>
        </div>

        <!-- After form is submitted -->
        <div class="text-gray-500 mt-10 mb-4">
          {{ $t('msg.info.afterFormSubmitted') }}
        </div>
        <!-- Show this message -->
        <label class="text-gray-600 text-bold"> {{ $t('msg.info.showMessage') }}: </label>
        <a-textarea v-model="formState.success_msg" rows="3" hide-details @input="updateView" />

        <!-- Other options -->
        <div class="mt-4">
          <div class="my-4">
            <!-- Show "Submit Another Form" button -->
            <a-switch v-model:checked="formState.submit_another_form" v-t="[`a:form-view:submit-another-form`]" />
            <span class="ml-4">{{ $t('msg.info.submitAnotherForm') }}</span>
          </div>

          <div class="my-4">
            <!-- Show a blank form after 5 seconds -->
            <a-switch v-model:checked="formState.show_blank_form" v-t="[`a:form-view:show-blank-form`]" />
            <span class="ml-4">{{ $t('msg.info.showBlankForm') }}</span>
          </div>

          <div class="my-4">
            <a-switch v-model:checked="formState.email" v-t="[`a:form-view:email-me`]" />
            <!-- Email me at <email> -->
            <span class="ml-4">
              {{ $t('msg.info.emailForm') }} <span class="text-bold text-gray-600">{{ state.user.value?.email }}</span>
            </span>
          </div>
        </div>
      </a-card>
    </a-col>
  </a-row>
</template>

<style scoped lang="scss">
.nc-editable:hover {
  .nc-field-remove-icon {
    @apply opacity-100;
  }
}

.nc-input {
  @apply w-full !bg-white rounded px-2 min-h-[40px] mt-2 mb-2 pa-2 flex align-center border-solid border-1 border-primary;
}
</style>
