<script setup lang="ts">
import type { FormType, GalleryType, GridType, KanbanType } from 'nocodb-sdk'
import { ViewTypes } from 'nocodb-sdk'
import { notification } from 'ant-design-vue'
import {
  inject,
  onClickOutside,
  onKeyStroke,
  provide,
  ref,
  useApi,
  useDebounceFn,
  useNuxtApp,
  useTabs,
  useViews,
  watch,
} from '#imports'
import { ActiveViewInj, MetaInj, ViewListInj } from '~/context'
import { extractSdkResponseErrorMsg, viewIcons } from '~/utils'
import MdiPlusIcon from '~icons/mdi/plus'
import MdiTrashCan from '~icons/mdi/trash-can'
import MdiContentCopy from '~icons/mdi/content-copy'
import MdiXml from '~icons/mdi/xml'
import MdiHook from '~icons/mdi/hook'

const meta = inject(MetaInj, ref())

const activeView = inject(ActiveViewInj, ref())

const { $e } = useNuxtApp()

const { addTab } = useTabs()

const { views, loadViews } = useViews(meta)

const { api } = useApi()

provide(ViewListInj, views)

/** Watch current views and on change set the next active view */
watch(
  views,
  (nextViews) => {
    if (nextViews.length) {
      activeView.value = nextViews[0]
    }
  },
  { immediate: true },
)

/** Sidebar visible */
const toggleDrawer = ref(false)

const isView = ref(false)

/** Is editing the view name enabled */
let isEditing = $ref<number | null>(null)

/** Helper to check if editing was disabled before the view navigation timeout triggers */
let isStopped = $ref(false)

/** Original view title when editing the view name */
let originalTitle = $ref<string | undefined>()

/** View type to create from modal */
let viewCreateType = $ref<ViewTypes>()

/** View title to create from modal (when duplicating) */
const viewCreateTitle = $ref('')

/** is view creation modal open */
let modalOpen = $ref(false)

/** Selected view(s) for menu */
const selected = ref<string[]>([])

/** Watch currently active view so we can mark it in the menu */
watch(activeView, (nextActiveView) => {
  const _nextActiveView = nextActiveView as GridType | FormType | KanbanType

  if (_nextActiveView && _nextActiveView.id) {
    selected.value = [_nextActiveView.id]
  }
})

/** Open view creation modal */
function openModal(type: ViewTypes) {
  modalOpen = true
  viewCreateType = type
}

/** Handle view creation */
function onCreate(view: GridType | FormType | KanbanType | GalleryType) {
  views.value?.push(view)
  activeView.value = view
  modalOpen = false
}

// todo: fix view type, alias is missing for some reason?
/** Navigate to view and add new tab if necessary */
function changeView(view: { id: string; alias?: string; title?: string; type: ViewTypes }) {
  activeView.value = view

  const tabProps = {
    id: view.id,
    title: (view.alias ?? view.title) || '',
    type: ViewTypes[view.type],
  }

  addTab(tabProps)
}

/** Debounce click handler so we can potentially enable editing view name {@see onDblClick} */
const onClick = useDebounceFn((view) => {
  if (isEditing !== null || isStopped) return

  changeView(view)
}, 250)

/** Enable editing view name on dbl click */
function onDblClick(index: number) {
  if (isEditing === null) {
    isEditing = index
    originalTitle = views.value[index].title
  }
}

/** Handle keydown on input field */
function onKeyDown(event: KeyboardEvent, index: number) {
  if (event.key === 'Escape') {
    onKeyEsc(event, index)
  } else if (event.key === 'Enter') {
    onKeyEnter(event, index)
  }
}

/** Rename view when enter is pressed */
function onKeyEnter(event: KeyboardEvent, index: number) {
  event.stopImmediatePropagation()
  event.preventDefault()

  onRename(index)
}

/** Disable renaming view when escape is pressed */
function onKeyEsc(event: KeyboardEvent, index: number) {
  event.stopImmediatePropagation()
  event.preventDefault()

  onCancel(index)
}

onKeyStroke('Escape', (event) => {
  if (isEditing !== null) {
    onKeyEsc(event, isEditing)
  }
})

onKeyStroke('Enter', (event) => {
  if (isEditing !== null) {
    onKeyEnter(event, isEditing)
  }
})

/** Current input element, changes when edit is enabled on a view menu item */
let inputRef = $ref<HTMLInputElement>()

function setInputRef(el: HTMLInputElement) {
  if (el) {
    el.focus()
    inputRef = el
  }
}

/** Cancel editing when clicked outside of input */
onClickOutside(inputRef, () => {
  if (isEditing !== null) {
    onCancel(isEditing)
  }
})

/** Duplicate a view */
// todo: This is not really a duplication, maybe we need to implement a true duplication?
function onDuplicate(index: number) {
  const view: any = views.value[index]

  openModal(view.type)

  $e('c:view:copy', { view: view.type })
}

/** Delete a view */
async function onDelete(index: number) {
  const view: any = views.value[index]

  try {
    await api.dbView.delete(view.id)

    notification.success({
      message: 'View deleted successfully',
      duration: 3,
    })

    await loadViews()
  } catch (e: any) {
    notification.error({
      message: await extractSdkResponseErrorMsg(e),
      duration: 3,
    })
  }

  // telemetry event
  $e('a:view:delete', { view: view.type })
}

/** Rename a view */
async function onRename(index: number) {
  // todo: validate if title is unique and not empty
  if (isEditing === null) return
  const view = views.value[index]

  if (view.title === '' || view.title === originalTitle) {
    onCancel(index)
    return
  }

  try {
    // todo typing issues, order and id do not exist on all members of ViewTypes (Kanban, Gallery, Form, Grid)
    await api.dbView.update((view as any).id, {
      title: view.title,
      order: (view as any).order,
    })

    console.log('rename success')

    notification.success({
      message: 'View renamed successfully',
      duration: 3,
    })

    console.log('success')
  } catch (e: any) {
    notification.error({
      message: await extractSdkResponseErrorMsg(e),
      duration: 3,
    })
  }

  onStopEdit()
}

/** Cancel renaming view */
function onCancel(index: number) {
  views.value[index].title = originalTitle
  onStopEdit()
}

/** Stop editing view name, timeout makes sure that view navigation (click trigger) does not pick up before stop is done */
function onStopEdit() {
  isStopped = true
  isEditing = null
  originalTitle = ''

  setTimeout(() => {
    isStopped = false
  }, 250)
}

function onApiSnippet() {
  // get API snippet
  $e('a:view:api-snippet')
}
</script>

<template>
  <a-layout-sider class="shadow" :width="toggleDrawer ? 0 : 250">
    <div class="flex flex-col h-full">
      <a-menu class="flex-1" :selected-keys="selected">
        <h3 class="pt-3 px-3 text-xs font-semibold">{{ $t('objects.views') }}</h3>

        <a-menu-item
          v-for="(view, i) of views"
          :key="view.id"
          class="group !flex !items-center !h-[30px]"
          @dblclick="onDblClick(i)"
          @click="onClick(view)"
        >
          <div v-t="['a:view:open', { view: view.type }]" class="text-xs flex items-center w-full gap-2">
            <component :is="viewIcons[view.type].icon" :class="`text-${viewIcons[view.type].color}`" />

            <a-input
              v-if="isEditing === i"
              :ref="setInputRef"
              v-model:value="view.title"
              @blur="onCancel(i)"
              @keydown="onKeyDown($event, i)"
            />
            <div v-else>{{ view.alias || view.title }}</div>

            <div class="flex-1" />

            <template v-if="isEditing !== i">
              <div class="flex items-center gap-1">
                <a-tooltip placement="left">
                  <template #title>
                    {{ $t('activity.copyView') }}
                  </template>

                  <MdiContentCopy class="hidden group-hover:block text-gray-500" @click.stop="onDuplicate(i)" />
                </a-tooltip>

                <a-popconfirm
                  placement="left"
                  :title="$t('msg.info.deleteProject')"
                  :ok-text="$t('general.yes')"
                  :cancel-text="$t('general.no')"
                  @confirm="onDelete(i)"
                >
                  <a-tooltip placement="left">
                    <template #title>
                      {{ $t('activity.deleteView') }}
                    </template>

                    <MdiTrashCan class="hidden group-hover:block text-red-500" @click.stop />
                  </a-tooltip>
                </a-popconfirm>
              </div>
            </template>
          </div>
        </a-menu-item>

        <a-divider class="my-2" />

        <h3 class="px-3 text-xs font-semibold">{{ $t('activity.createView') }}</h3>

        <a-menu-item key="grid" class="group !flex !items-center !h-[30px]" @click="openModal(ViewTypes.GRID)">
          <a-tooltip placement="left">
            <template #title>
              {{ $t('msg.info.addView.grid') }}
            </template>

            <div class="text-xs flex items-center h-full w-full gap-2">
              <component :is="viewIcons[ViewTypes.GRID].icon" :class="`text-${viewIcons[ViewTypes.GRID].color}`" />

              <div>{{ $t('objects.viewType.grid') }}</div>

              <div class="flex-1" />

              <MdiPlusIcon class="group-hover:text-primary" />
            </div>
          </a-tooltip>
        </a-menu-item>

        <a-menu-item key="gallery" class="group !flex !items-center !h-[30px]" @click="openModal(ViewTypes.GALLERY)">
          <a-tooltip placement="left">
            <template #title>
              {{ $t('msg.info.addView.gallery') }}
            </template>

            <div class="text-xs flex items-center h-full w-full gap-2">
              <component :is="viewIcons[ViewTypes.GALLERY].icon" :class="`text-${viewIcons[ViewTypes.GALLERY].color}`" />

              <div>{{ $t('objects.viewType.gallery') }}</div>

              <div class="flex-1" />

              <MdiPlusIcon class="group-hover:text-primary" />
            </div>
          </a-tooltip>
        </a-menu-item>

        <a-menu-item v-if="!isView" key="form" class="group !flex !items-center !h-[30px]" @click="openModal(ViewTypes.FORM)">
          <a-tooltip placement="left">
            <template #title>
              {{ $t('msg.info.addView.form') }}
            </template>

            <div class="text-xs flex items-center h-full w-full gap-2">
              <component :is="viewIcons[ViewTypes.FORM].icon" :class="`text-${viewIcons[ViewTypes.FORM].color}`" />

              <div>{{ $t('objects.viewType.form') }}</div>

              <div class="flex-1" />

              <MdiPlusIcon class="group-hover:text-primary" />
            </div>
          </a-tooltip>
        </a-menu-item>

        <div class="flex flex-col gap-4 mt-8">
          <button
            class="flex items-center gap-2 w-full mx-3 p-4 rounded bordered !bg-primary text-white transform translate-x-4 hover:translate-x-0 transition duration-150 ease"
            @click="onApiSnippet"
          >
            <MdiXml />Get API Snippet
          </button>

          <button
            class="flex items-center gap-2 w-full mx-3 p-4 rounded border-1 border-solid border-black transform translate-x-4 hover:translate-x-0 transition duration-150 ease"
            @click="onApiSnippet"
          >
            <MdiHook />{{ $t('objects.webhooks') }}
          </button>
        </div>

        <general-flipping-card class="my-4 h-[250px] w-[250px]">
          <template #front>
            <general-social />

            <a v-t="['e:hiring']" href="https://angel.co/company/nocodb" target="_blank"> 🚀 We are Hiring! 🚀 </a>
          </template>
        </general-flipping-card>
      </a-menu>
    </div>

    <DlgViewCreate v-if="views" v-model="modalOpen" :title="viewCreateTitle" :type="viewCreateType" @created="onCreate" />
  </a-layout-sider>
</template>

<style scoped>
:deep(.ant-menu-title-content) {
  @apply w-full;
}
</style>
