<script setup lang="ts">
import { onKeyDown } from '@vueuse/core'
import { useProvideAttachmentCell } from './utils'
import { useSortable } from './sort'

interface Props {
  modelValue?: string | Record<string, any>[] | null
  rowIndex?: number
}

interface Emits {
  (event: 'update:modelValue', value: string | Record<string, any>[]): void
}

const { modelValue } = defineProps<Props>()

const emits = defineEmits<Emits>()

const dropZoneInjection = inject(DropZoneRef, ref())

const attachmentCellRef = ref<HTMLDivElement>()

const sortableRef = ref<HTMLDivElement>()

const currentCellRef = inject(CurrentCellInj, dropZoneInjection.value)

const isGallery = inject(IsGalleryInj, ref(false))

const isKanban = inject(IsKanbanInj, ref(false))

const isExpandedForm = inject(IsExpandedFormOpenInj, ref(false))

const isSurveyForm = inject(IsSurveyFormInj, ref(false))

const isGrid = inject(IsGridInj, ref(false))

const isAllowed = inject(IsAllowedInj, ref(true))

const isEditColumn = inject(EditColumnInj, ref(false))

const isUnderLookup = inject(IsUnderLookupInj, ref(false))
const canvasCellEventData = inject(CanvasCellEventDataInj, reactive<CanvasCellEventDataInjType>({}))
const isCanvasInjected = inject(IsCanvasInjectionInj, false)
const clientMousePosition = inject(ClientMousePositionInj, reactive(clientMousePositionDefaultValue))
const canvasSelectCell = inject(CanvasSelectCellInj, null)

const cellEventHook = inject(CellEventHookInj, null)

const { isMobileMode } = useGlobal()

const {
  isPublic,
  isForm,
  column,
  modalRendered,
  downloadAttachment,
  renameFile,
  modalVisible,
  attachments,
  visibleItems,
  onDrop,
  isLoading,
  selectedFile,
  isReadonly,
  storedFiles,
  removeFile,
  updateAttachmentTitle,
  isEditAllowed,
  isSharedForm,
  isUploading,
} = useProvideAttachmentCell(updateModelValue)

const { dragging } = useSortable(sortableRef, visibleItems, updateModelValue, isReadonly)

const showAllAttachments = ref(false)

const { width: sortableRefWidth } = useElementSize(sortableRef)

const maxVisibleCards = computed(() => {
  // min of total visible items and max cards per row * 2
  return Math.min(visibleItems.value.length, Math.floor((sortableRefWidth.value + 8) / (124 + 8)) * 2)
})

const expandedFormVisibelItems = computed(() => {
  if (showAllAttachments.value) {
    return visibleItems.value
  }

  return visibleItems.value.slice(0, maxVisibleCards.value)
})

const active = inject(ActiveCellInj, ref(false))

const { state: rowState, row } = useSmartsheetRowStoreOrThrow()

const meta = inject(MetaInj, ref())

if (!isPublic.value && !isForm.value && meta.value) {
  useProvideRowComments(meta, row)
}

const onDropAction = function (...args: any[]) {
  const draggingBool = unref(dragging)
  if (!draggingBool) {
    onDrop.apply(this, args)
  }
}
const { isOverDropZone } = useDropZone(currentCellRef as any, onDropAction)

/** on new value, reparse our stored attachments */
watch(
  () => modelValue,
  async (nextModel) => {
    if (nextModel) {
      try {
        const nextAttachments = ((typeof nextModel === 'string' ? JSON.parse(nextModel) : nextModel) || []).filter(Boolean)

        if (isPublic.value && isForm.value) {
          storedFiles.value = nextAttachments
        } else {
          attachments.value = nextAttachments
        }
      } catch (e) {
        console.error(e)
        if (isPublic.value && isForm.value) {
          storedFiles.value = []
        } else {
          attachments.value = []
        }
      }
    } else {
      if (isPublic.value && isForm.value) {
        storedFiles.value = []
      } else {
        attachments.value = []
      }
    }
  },
  {
    immediate: true,
  },
)

/** updates attachments array for autosave */
function updateModelValue(data: string | Record<string, any>[]) {
  emits('update:modelValue', data)
}

/** Close modal on escape press, disable dropzone as well */
onKeyDown('Escape', () => {
  modalVisible.value = false
  isOverDropZone.value = false
})

/** sync storedFiles state with row state */
watch(
  () => storedFiles.value.length || 0,
  () => {
    rowState.value[column.value!.title!] = storedFiles.value
  },
)

const isNewAttachmentModalOpen = ref(false)

watch(isNewAttachmentModalOpen, (newVal, oldVal) => {
  if (oldVal && !newVal) canvasSelectCell?.trigger()
})

watch(selectedFile, (newVal, oldVal) => {
  if (oldVal && !newVal) canvasSelectCell?.trigger()
})

const openAttachmentModal = (e: Event) => {
  if (!isEditAllowed.value) return

  e?.stopPropagation()
  isNewAttachmentModalOpen.value = true
}

onKeyDown('Enter', () => {
  if (!isUnderLookup.value && isCanvasInjected && !isExpandedForm.value && isGrid.value) {
    if (attachments.value.length) {
      modalRendered.value = true
      modalVisible.value = true
    } else if (isEditAllowed.value) {
      isNewAttachmentModalOpen.value = true
    }
  }
})
useSelectedCellKeydownListener(inject(ActiveCellInj, ref(false)), (e) => {
  if (isCanvasInjected) return
  if (modalVisible.value) return
  if (e.key === 'Enter' && !isReadonly.value && !selectedFile.value) {
    if (isNewAttachmentModalOpen.value) return
    e.stopPropagation()
    if (!isMobileMode.value && visibleItems.value.length) {
      modalRendered.value = true
      modalVisible.value = true
    } else {
      // open attachment modal
      openAttachmentModal(e)
    }
  }
})

const rowHeight = inject(RowHeightInj, ref())

const onExpand = () => {
  if (isMobileMode.value) return

  modalRendered.value = true
  modalVisible.value = true
}

const onFileClick = (item: any) => {
  if (isMobileMode.value && !isExpandedForm.value) return

  if (!isMobileMode.value && (isGallery.value || isKanban.value) && !isExpandedForm.value) return
  selectedFile.value = item
}

const keydownEnter = (e: KeyboardEvent) => {
  if (!isSurveyForm.value) {
    openAttachmentModal(e)
    e.stopPropagation()
  }
}
const keydownSpace = (e: KeyboardEvent) => {
  if (isSurveyForm.value) {
    openAttachmentModal(e)
    e.stopPropagation()
  }
}

const isConfirmModalOpen = ref(false)
const filetoDelete = reactive({
  title: '',
  i: 0,
})

function onRemoveFileClick(title: any, i: number) {
  isConfirmModalOpen.value = true
  filetoDelete.i = i
  filetoDelete.title = title
}

const handleFileDelete = (i: number) => {
  removeFile(i)
  isConfirmModalOpen.value = false
  filetoDelete.i = 0
  filetoDelete.title = ''
}

const attachmentSize = computed(() => {
  if (isForm.value || isExpandedForm.value) {
    return 'small'
  }

  switch (rowHeight.value) {
    case 1:
      return 'tiny'
    case 2:
      return 'tiny'
    case 4:
      return 'small'
    case 6:
      return 'small'
    default:
      return 'tiny'
  }
})

defineExpose({
  openFilePicker: openAttachmentModal,
  downloadAttachment,
  renameAttachment: renameFile,
  removeAttachment: onRemoveFileClick,
  updateAttachmentTitle,
  isEditAllowed,
})

const onCellEvent = (event?: Event) => {
  if (!(event instanceof KeyboardEvent) || !event.target || isActiveInputElementExist(event) || !visibleItems.value.length) return

  if (isExpandCellKey(event)) {
    if (modalVisible.value) {
      modalRendered.value = false
      modalVisible.value = false
    } else {
      onExpand()
    }

    return true
  }
}

onMounted(() => {
  cellEventHook?.on(onCellEvent)

  if (!isUnderLookup.value && isCanvasInjected && !isExpandedForm.value && isGrid.value && !isEditColumn.value) {
    forcedNextTick(() => {
      if (onCellEvent(canvasCellEventData.event)) return

      const clickableSelectors = ['.view-attachments', '.add-files', '.nc-attachment', '.empty-add-files']
        .map((selector) => `.nc-canvas-table-editable-cell-wrapper ${selector}`)
        .join(', ')
      const clickable = getElementAtMouse<HTMLElement>(clickableSelectors, clientMousePosition)
      if (clickable) {
        clickable.click()
      } else {
        if (attachments.value.length) {
          modalRendered.value = true
          modalVisible.value = true
        } else if (isEditAllowed.value) {
          isNewAttachmentModalOpen.value = true
        }
      }
    })
  }
})

onUnmounted(() => {
  cellEventHook?.off(onCellEvent)
})
</script>

<template>
  <div
    v-if="isExpandedForm || isForm"
    class="form-attachment-cell"
    :class="{
      'nc-has-attachments': visibleItems.length,
    }"
  >
    <LazyCellAttachmentCarousel v-if="selectedFile" />
    <div v-if="visibleItems.length > 0" ref="sortableRef" class="flex flex-wrap items-stretch mb-2 gap-2">
      <CellAttachmentCard
        v-for="(item, i) in expandedFormVisibelItems"
        :key="`${item?.title}-${i}`"
        v-model:dragging="dragging"
        class="nc-attachment-item group gap-2 flex border-1 bg-nc-bg-default rounded-md border-nc-border-gray-medium flex-col relative w-[124px] overflow-hidden"
        :attachment="item"
        :index="i"
        :allow-selection="false"
        :allow-rename="isEditAllowed"
        :allow-delete="!isReadonly"
        preview-class-override="!h-20"
        :rename-inline="false"
        :confirm-to-delete="true"
        @clicked="onFileClick(item)"
        @on-delete="onRemoveFileClick(item.title, i)"
      />
    </div>
    <div v-if="visibleItems.length > maxVisibleCards" class="mb-2">
      <NcButton type="text" size="small" @click="showAllAttachments = !showAllAttachments">
        {{
          showAllAttachments
            ? `${$t('general.showLess')}`
            : `+ ${visibleItems.length - maxVisibleCards} ${$t('general.more').toLowerCase()}`
        }}
      </NcButton>
    </div>
    <div
      class="flex"
      :class="{
        'w-full': !visibleItems.length || isUploading,
      }"
    >
      <NcTooltip
        :disabled="isEditAllowed || !isAllowed"
        :title="$t('tooltip.sourceDataIsReadonly')"
        class="flex items-center justify-between"
        :class="{
          'w-full': !visibleItems.length || isUploading,
        }"
      >
        <NcButton
          v-if="visibleItems.length || isUploading"
          data-testid="attachment-cell-file-picker-button"
          type="secondary"
          size="small"
          class="mb-1"
          :disabled="!isEditAllowed"
          @click="openAttachmentModal"
        >
          <div class="flex items-center gap-1.5 justify-center">
            <GeneralIcon icon="upload" class="text-current opacity-80 h-4 w-4" />
            <span>
              {{ $t('activity.uploadFiles') }}
            </span>
          </div>
        </NcButton>

        <div
          v-else
          data-testid="attachment-cell-file-picker-button"
          class="flex-none w-full border-dashed border-2 border-transparent rounded-lg text-center justify-center flex items-center flex-col p-3 text-nc-content-gray-subtle2"
          :class="{
            'cursor-not-allowed': !isEditAllowed,
            'cursor-pointer': isEditAllowed,
          }"
          @click="openAttachmentModal"
        >
          <GeneralIcon
            icon="upload"
            class="flex-none w-6 h-6"
            :class="{
              'text-nc-content-gray-muted': !isOverDropZone,
              'text-nc-content-brand': isOverDropZone && isEditAllowed,
            }"
          />
          <span class="py-3">
            {{ $t('labels.clickTo') }}

            <span
              :tabindex="0"
              class="font-semibold text-nc-content-brand focus:(!outline-none) focus-visible:(outline-none)"
              @keydown.enter="openAttachmentModal"
            >
              {{ $t('labels.browseFiles') }}
            </span>
            {{ $t('general.or') }}
            <span class="font-semibold"> {{ $t('labels.dragFilesHere') }} </span>

            {{ $t('labels.toUpload') }}
          </span>
        </div>

        <div
          v-if="isUploading && !isSharedForm"
          class="flex items-center gap-1.5 h-full pb-1 text-bodyDefaultSm !text-nc-content-gray-muted"
        >
          <GeneralLoader class="!text-inherit" />
          <div>{{ $t('labels.uploading') }}</div>
        </div>
      </NcTooltip>
    </div>

    <div
      v-if="isOverDropZone && isEditAllowed && !isReadonly && !dragging && currentCellRef"
      class="nc-is-over-drop-zone absolute inset-0 flex flex-col items-center justify-center pointer-events-none"
    >
      <template v-if="visibleItems.length">
        <GeneralIcon icon="upload" class="flex-none w-6 h-6 text-nc-content-brand" />

        <div class="p-3">
          <h1 class="text-nc-content-brand font-bold">{{ $t('labels.dropHere') }}</h1>
        </div>
      </template>
    </div>

    <GeneralDeleteModal
      v-model:visible="isConfirmModalOpen"
      entity-name="File"
      :on-delete="async () => handleFileDelete(filetoDelete.i)"
    >
      <template #entity-preview>
        <span>
          <div
            class="flex flex-row items-center py-2.25 px-2.5 bg-nc-bg-gray-extralight rounded-lg text-nc-content-gray-subtle mb-4"
          >
            <GeneralIcon icon="file" class="nc-view-icon"></GeneralIcon>
            <div
              class="capitalize text-ellipsis overflow-hidden select-none w-full pl-1.75"
              :style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
            >
              {{ filetoDelete.title }}
            </div>
          </div>
        </span>
      </template>
    </GeneralDeleteModal>
  </div>
  <div v-else ref="attachmentCellRef" class="nc-attachment-cell relative group color-transition" :data-row-height="rowHeight">
    <LazyCellAttachmentCarousel v-if="selectedFile" />

    <template v-if="!isReadonly && !dragging && !!currentCellRef">
      <general-overlay
        v-model="isOverDropZone"
        inline
        :target="currentCellRef"
        data-rec="true"
        class="nc-attachment-cell-dropzone text-white text-lg bg-gray-600/75 flex text-sm items-center justify-center gap-2"
      >
        {{ $t('labels.dropHere') }}
      </general-overlay>
    </template>

    <div
      v-if="!isReadonly && active && !visibleItems.length"
      :class="{ 'sm:(mx-auto px-4) xs:(w-full min-w-8)': !visibleItems.length }"
      class="group cursor-pointer flex nc-upload-btn gap-1 items-center rounded border-none"
      tabindex="0"
      @keydown.enter="keydownEnter"
      @keydown.space="keydownSpace"
    >
      <component :is="iconMap.reload" v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />

      <NcTooltip placement="bottom" class="w-full text-center">
        <template #title>
          <span data-rec="true">{{ $t('activity.attachmentDrop') }} </span>
        </template>

        <NcButton
          type="secondary"
          size="xs"
          data-testid="attachment-cell-file-picker-button"
          class="!px-2 !h-6 !min-w-[fit-content] empty-add-files"
          @click.stop="openAttachmentModal"
        >
          <div class="flex items-center gap-1 justify-center">
            <GeneralIcon icon="upload" class="text-nc-content-gray-muted text-[10px] h-3.5 w-3.5" />
            <span class="text-[11px]">
              {{ $t('activity.addFiles') }}
            </span>
          </div>
        </NcButton>
      </NcTooltip>
    </div>

    <template v-if="visibleItems.length > 0">
      <div
        ref="sortableRef"
        :class="{
          'justify-center': !isGallery && !isKanban,
        }"
        class="nc-attachment-wrapper flex cursor-pointer w-full items-center flex-wrap gap-2 mt-0 items-start overflow-y-auto nc-scrollbar-thin"
        :style="{
          height: `max(${
            !rowHeight || rowHeight === 1 ? Number(rowHeightInPx['1']) - 1 : rowHeightInPx[`${rowHeight}`] - 17
          }px, ${isGrid ? 22 : 32}px)`,
          paddingTop: !rowHeight || rowHeight === 1 ? '4px' : undefined,
          paddingBottom: !rowHeight || rowHeight === 1 ? '4px' : undefined,
        }"
      >
        <NcTooltip v-for="(item, i) of visibleItems" :key="item.url || item.title" placement="bottom" class="nc-attachment-item">
          <template #title>
            <div class="text-center w-full">{{ item.title }}</div>
          </template>
          <div
            class="aspect-square"
            :class="{
              'h-[24px]': !rowHeight || rowHeight === 1,
              'h-[32px]': rowHeight === 2,
              'h-[64px]': rowHeight === 4 || rowHeight === 6,
            }"
          >
            <CellAttachmentPreviewThumbnail
              :icon-width="24"
              :icon-height="24"
              class="nc-attachment rounded-lg overflow-hidden"
              :alt="item.title || `#${i}`"
              :attachment="item"
              :thumbnail="attachmentSize"
              object-fit="contain"
              @click="() => onFileClick(item)"
            />
          </div>
        </NcTooltip>
      </div>

      <NcTooltip
        placement="bottom"
        class="nc-action-icon !absolute hidden right-0 nc-text-area-expand-btn !group-hover:block z-3"
        :class="{
          'top-0': isGrid && !(!rowHeight || rowHeight === 1),
          'top-1': !isGrid,
        }"
        :style="isGrid && (!rowHeight || rowHeight === 1) ? { top: '50%', transform: 'translateY(-50%)' } : undefined"
      >
        <template #title>
          {{ isExpandedForm ? $t('activity.viewAttachment') : `${$t('activity.viewAttachment')} '${$t('tooltip.shiftSpace')}'` }}
        </template>
        <NcButton
          type="secondary"
          size="xsmall"
          data-testid="attachment-cell-file-picker-button"
          class="!p-0 !w-5 !h-5 !min-w-[fit-content] view-attachments"
          @click.stop="onExpand"
        >
          <component :is="iconMap.reload" v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />

          <component
            :is="iconMap.maximize"
            v-else
            class="transform group-hover:(!text-nc-content-gray) text-nc-content-gray-subtle w-3 h-3"
          />
        </NcButton>
      </NcTooltip>

      <NcTooltip
        v-if="isEditAllowed"
        placement="bottom"
        class="nc-action-icon !absolute hidden left-0 nc-text-area-expand-btn !group-hover:block z-3"
        :class="{
          'top-0': isGrid && !(!rowHeight || rowHeight === 1),
          'top-1': !isGrid,
        }"
        :style="isGrid && (!rowHeight || rowHeight === 1) ? { top: '50%', transform: 'translateY(-50%)' } : undefined"
      >
        <template #title>{{ $t('activity.addFiles') }}</template>
        <NcButton
          type="secondary"
          size="xsmall"
          data-testid="attachment-cell-file-picker-button"
          class="!p-0 !w-5 !h-5 !min-w-[fit-content] add-files"
          @click.stop="openAttachmentModal"
        >
          <GeneralIcon icon="ncPaperclip" class="w-3 group-hover:(!text-nc-content-gray) text-nc-content-gray-subtle" />
        </NcButton>
      </NcTooltip>
    </template>

    <LazyCellAttachmentModal v-if="modalRendered" />
  </div>
  <CellAttachmentAttachFile v-if="isNewAttachmentModalOpen" v-model:value="isNewAttachmentModalOpen" />
</template>

<style lang="scss">
.nc-data-cell {
  &:has(.form-attachment-cell.nc-has-attachments) {
    @apply !border-none pt-1 -mt-1 -ml-1;
    box-shadow: none !important;

    &:focus-within:not(.nc-readonly-div-data-cell):not(.nc-system-field) {
      box-shadow: none !important;
    }

    .nc-cell-attachment {
      @apply !border-none;
    }
  }

  &:has(.form-attachment-cell .nc-is-over-drop-zone) {
    @apply relative;

    &::after {
      @apply content-[''] block absolute inset-0 border-dashed border-2 border-nc-fill-primary rounded-lg pointer-events-none;
    }
  }
}
.nc-cell {
  .nc-attachment-cell {
    .nc-attachment {
      @apply min-h-5.5 !ring-1 !ring-nc-border-gray-dark !rounded;
    }

    .ghost,
    .ghost > * {
      @apply !pointer-events-none;
    }

    .dragging {
      .ant-tooltip {
        @apply !hidden;
      }
    }
  }
  .nc-attachment-item {
    @apply relative;
  }
}
</style>
