<script setup lang="ts">
import type { ColumnType, LinkToAnotherRecordType } from 'nocodb-sdk'
import { RelationTypes, UITypes, isLinksOrLTAR, isSystemColumn } from 'nocodb-sdk'

const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj, ref())
const isLocked = inject(IsLockedInj, ref(false))

const { gridViewCols, updateGridViewColumn, metaColumnById, showSystemFields } = useViewColumnsOrThrow()

const { fieldsToGroupBy, groupByLimit } = useViewGroupByOrThrow()

const { $e } = useNuxtApp()

const _groupBy = ref<{ fk_column_id?: string; sort: string; order: number }[]>([])

const groupBy = computed<{ fk_column_id?: string; sort: string; order: number }[]>(() => {
  const tempGroupBy: { fk_column_id?: string; sort: string; order: number }[] = []
  Object.values(gridViewCols.value).forEach((col) => {
    if (col.group_by) {
      tempGroupBy.push({
        fk_column_id: col.fk_column_id,
        sort: col.group_by_sort || 'asc',
        order: col.group_by_order || 1,
      })
    }
  })
  tempGroupBy.sort((a, b) => a.order - b.order)
  return tempGroupBy
})

const groupedByColumnIds = computed(() => groupBy.value.map((g) => g.fk_column_id).filter((g) => g))

const { eventBus } = useSmartsheetStoreOrThrow()

const { isMobileMode } = useGlobal()

const showCreateGroupBy = ref(false)

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

const columnByID = computed(() =>
  columns.value.reduce((obj, col) => {
    obj[col.id!] = col

    return obj
  }, {} as Record<string, ColumnType>),
)

const availableColumns = computed(() => {
  return columns.value
    ?.filter((c: ColumnType) => {
      if (c.uidt === UITypes.Links) {
        return true
      }
      if (isSystemColumn(metaColumnById?.value?.[c.id!])) {
        return (
          /** hide system columns if not enabled */
          showSystemFields.value
        )
      } else if (c.uidt === UITypes.QrCode || c.uidt === UITypes.Barcode || c.uidt === UITypes.ID) {
        return false
      } else {
        /** ignore hasmany and manytomany relations if it's using within sort menu */
        return !(isLinksOrLTAR(c) && (c.colOptions as LinkToAnotherRecordType).type !== RelationTypes.BELONGS_TO)
        /** ignore virtual fields which are system fields ( mm relation ) and qr code fields */
      }
    })
    .filter((c) => !groupBy.value.find((g) => g.fk_column_id === c.id))
})

const getColumnUidtByID = (key?: string) => {
  if (!key) return ''
  return columnByID.value[key]?.uidt || ''
}

const open = ref(false)

useMenuCloseOnEsc(open)

const saveGroupBy = async () => {
  if (view.value?.id) {
    try {
      for (const gby of _groupBy.value) {
        if (!gby.fk_column_id) continue
        const col = gridViewCols.value[gby.fk_column_id]
        if (col && (!col.group_by || col.group_by_order !== gby.order || col.group_by_sort !== gby.sort)) {
          await updateGridViewColumn(gby.fk_column_id, {
            group_by: true,
            group_by_order: gby.order,
            group_by_sort: gby.sort,
          })
        }
      }

      for (const gby of groupBy.value) {
        if (!gby.fk_column_id) continue
        if (_groupBy.value.find((g) => g.fk_column_id === gby.fk_column_id)) continue
        const col = gridViewCols.value[gby.fk_column_id]
        if (col && col.group_by) {
          await updateGridViewColumn(gby.fk_column_id, {
            group_by: false,
            group_by_order: undefined,
            group_by_sort: undefined,
          })
        }
      }

      $e('a:group-by:update', { groupBy: groupBy.value })

      eventBus.emit(SmartsheetStoreEvents.GROUP_BY_RELOAD)
    } catch (e) {
      message.error('There was an error while updating view!')
    }
  } else {
    message.error('View not found!!!')
  }
}

const addFieldToGroupBy = (column: ColumnType) => {
  _groupBy.value.push({ fk_column_id: column.id, sort: 'asc', order: _groupBy.value.length + 1 })
  saveGroupBy()
  showCreateGroupBy.value = false
}

const removeFieldFromGroupBy = async (index: string | number) => {
  if (groupedByColumnIds.value.length === 0) {
    open.value = false
    return
  }
  _groupBy.value.splice(+index, 1)
  await saveGroupBy()
}

watch(open, () => {
  if (open.value) {
    _groupBy.value = [...groupBy.value]
  } else {
    showCreateGroupBy.value = false
  }
})

eventBus.on(async (event, column) => {
  if (!column?.id) return

  if (event === SmartsheetStoreEvents.GROUP_BY_ADD) {
    addFieldToGroupBy(column)
  } else if (event === SmartsheetStoreEvents.GROUP_BY_REMOVE) {
    if (groupedByColumnIds.value.length === 0) return

    _groupBy.value = _groupBy.value.filter((g) => g.fk_column_id !== column.id)

    await saveGroupBy()
  }
})
</script>

<template>
  <NcDropdown
    v-model:visible="open"
    offset-y
    :trigger="['click']"
    class="!xs:hidden"
    overlay-class-name="nc-dropdown-group-by-menu nc-toolbar-dropdown overflow-hidden"
  >
    <div :class="{ 'nc-active-btn': groupedByColumnIds?.length }">
      <NcButton
        v-e="['c:group-by']"
        :disabled="isLocked"
        class="nc-group-by-menu-btn nc-toolbar-btn !border-0 !h-7"
        size="small"
        type="secondary"
      >
        <div class="flex items-center gap-1">
          <div class="flex items-center gap-2">
            <component :is="iconMap.group" class="h-4 w-4" />

            <!-- Group By -->
            <span v-if="!isMobileMode" class="text-capitalize !text-[13px] font-medium">{{ $t('activity.group') }}</span>
          </div>
          <span v-if="groupedByColumnIds?.length" class="bg-brand-50 text-brand-500 py-1 px-2 text-md rounded-md">{{
            groupedByColumnIds.length
          }}</span>
        </div>
      </NcButton>
    </div>
    <template #overlay>
      <SmartsheetToolbarCreateGroupBy
        v-if="!_groupBy.length"
        :is-parent-open="open"
        :columns="fieldsToGroupBy"
        @created="addFieldToGroupBy"
      />
      <div
        v-else
        class="flex flex-col bg-white overflow-auto nc-group-by-list menu-filter-dropdown max-h-[max(80vh,500px)] min-w-102 pt-2 pb-2 pl-4"
        data-testid="nc-group-by-menu"
      >
        <div class="group-by-grid max-h-100 nc-scrollbar-thing pr-4 py-2" @click.stop>
          <template v-for="[i, group] of Object.entries(_groupBy)" :key="`grouped-by-${group.fk_column_id}`">
            <LazySmartsheetToolbarFieldListAutoCompleteDropdown
              v-model="group.fk_column_id"
              class="caption nc-sort-field-select w-44 flex flex-grow"
              :columns="fieldsToGroupBy"
              :allow-empty="true"
              @change="saveGroupBy"
              @click.stop
            />
            <NcSelect
              ref=""
              v-model:value="group.sort"
              class="shrink grow-0 nc-sort-dir-select"
              :label="$t('labels.operation')"
              dropdown-class-name="sort-dir-dropdown nc-dropdown-sort-dir"
              :disabled="!group.fk_column_id"
              @change="saveGroupBy"
              @click.stop
            >
              <a-select-option
                v-for="(option, j) of getSortDirectionOptions(getColumnUidtByID(group.fk_column_id), true)"
                :key="j"
                :value="option.value"
              >
                <div class="w-full flex items-center justify-between gap-2">
                  <div class="truncate flex-1">{{ option.text }}</div>
                  <component
                    :is="iconMap.check"
                    v-if="group.sort === option.value"
                    id="nc-selected-item-icon"
                    class="text-primary w-4 h-4"
                  />
                </div>
              </a-select-option>
            </NcSelect>

            <a-tooltip placement="right" title="Remove" class="flex-none min-w-40">
              <NcButton
                v-e="['c:group-by:remove']"
                class="nc-group-by-item-remove-btn min-w-40"
                size="small"
                type="text"
                @click.stop="removeFieldFromGroupBy(i)"
              >
                <component :is="iconMap.deleteListItem" />
              </NcButton>
            </a-tooltip>
          </template>
        </div>
        <NcDropdown
          v-if="availableColumns.length && fieldsToGroupBy.length > _groupBy.length && _groupBy.length < groupByLimit"
          v-model:visible="showCreateGroupBy"
          :trigger="['click']"
          overlay-class-name="nc-toolbar-dropdown"
        >
          <NcButton
            v-e="['c:group-by:add']"
            class="nc-add-group-by-btn !text-brand-500 mt-1 mb-2"
            style="width: fit-content"
            size="small"
            type="text"
            @click.stop="showCreateGroupBy = true"
          >
            <div class="flex gap-1 items-center">
              <div class="flex">
                {{ $t('activity.addSubGroup') }}
              </div>
              <GeneralIcon icon="plus" />
            </div>
          </NcButton>
          <template #overlay>
            <SmartsheetToolbarCreateGroupBy
              :is-parent-open="showCreateGroupBy"
              :columns="fieldsToGroupBy"
              @created="addFieldToGroupBy"
            />
          </template>
        </NcDropdown>
      </div>
    </template>
  </NcDropdown>
</template>

<style scoped>
.group-by-grid {
  display: grid;
  grid-template-columns: auto 150px auto;
  @apply gap-x-2 gap-y-3;
}
</style>
