<script setup lang="ts">
import { UITypes } from 'nocodb-sdk'
import type { ColumnType } from 'nocodb-sdk'
import {
  ActiveCellInj,
  ColumnInj,
  IsFormInj,
  ReadonlyInj,
  computed,
  isBoolean,
  isCurrency,
  isDate,
  isDateTime,
  isDecimal,
  isDuration,
  isFloat,
  isInt,
  isMultiSelect,
  isPercent,
  isRating,
  isReadonlyDateTime,
  isReadonlyUser,
  isSingleSelect,
  isTextArea,
  isTime,
  isUser,
  isYear,
  provide,
  ref,
  storeToRefs,
  toRef,
  useBase,
} from '#imports'
import type { Filter } from '#imports'
import SingleSelect from '~/components/cell/SingleSelect.vue'
import MultiSelect from '~/components/cell/MultiSelect.vue'
import DatePicker from '~/components/cell/DatePicker.vue'
import YearPicker from '~/components/cell/YearPicker.vue'
import TimePicker from '~/components/cell/TimePicker.vue'
import Rating from '~/components/cell/Rating.vue'
import Duration from '~/components/cell/Duration.vue'
import Percent from '~/components/cell/Percent.vue'
import Currency from '~/components/cell/Currency.vue'
import Decimal from '~/components/cell/Decimal.vue'
import Integer from '~/components/cell/Integer.vue'
import Float from '~/components/cell/Float.vue'
import Text from '~/components/cell/Text.vue'
import User from '~/components/cell/User.vue'

interface Props {
  // column could be possibly undefined when the filter is created
  column?: ColumnType
  filter: Filter
}

interface Emits {
  (event: 'updateFilterValue', model: any): void
}

const props = defineProps<Props>()

const emit = defineEmits<Emits>()

const column = toRef(props, 'column')

const editEnabled = ref(true)

provide(ColumnInj, column)

provide(EditModeInj, readonly(editEnabled))

provide(ReadonlyInj, ref(false))

const checkTypeFunctions: Record<string, (column: ColumnType, abstractType?: string) => boolean> = {
  isSingleSelect,
  isMultiSelect,
  isDate,
  isYear,
  isDateTime,
  isTime,
  isRating,
  isDuration,
  isPercent,
  isCurrency,
  isDecimal,
  isReadonlyDateTime,
  isInt,
  isFloat,
  isTextArea,
  isLinks: (col: ColumnType) => col.uidt === UITypes.Links,
  isUser,
  isReadonlyUser,
}

type FilterType = keyof typeof checkTypeFunctions

const { sqlUis } = storeToRefs(useBase())

const sqlUi = ref(column.value?.source_id ? sqlUis.value[column.value?.source_id] : Object.values(sqlUis.value)[0])

const abstractType = computed(() => column.value && sqlUi.value.getAbstractType(column.value))

const checkType = (filterType: FilterType) => {
  const checkTypeFunction = checkTypeFunctions[filterType]

  if (!column.value || !checkTypeFunction) {
    return false
  }

  return checkTypeFunction(column.value, abstractType.value)
}

const filterInput = computed({
  get: () => {
    return props.filter.value
  },
  set: (value) => {
    emit('updateFilterValue', value)
  },
})

const booleanOptions = [
  { value: true, label: 'true' },
  { value: false, label: 'false' },
  { value: null, label: 'unset' },
]

const renderSingleSelect = (op: string) => {
  // use MultiSelect for SingleSelect columns for anyof / nanyof filters
  if (['anyof', 'nanyof'].includes(op)) {
    return MultiSelect
  }
  return SingleSelect
}

const renderDateFilterInput = (sub_op: string) => {
  if (['daysAgo', 'daysFromNow', 'pastNumberOfDays', 'nextNumberOfDays'].includes(sub_op)) {
    return Decimal
  }
  return DatePicker
}

const componentMap: Partial<Record<FilterType, any>> = computed(() => {
  return {
    isSingleSelect: renderSingleSelect(props.filter.comparison_op!),
    isMultiSelect: MultiSelect,
    isDate: renderDateFilterInput(props.filter.comparison_sub_op!),
    isYear: YearPicker,
    isDateTime: renderDateFilterInput(props.filter.comparison_sub_op!),
    isReadonlyDateTime: renderDateFilterInput(props.filter.comparison_sub_op!),
    isTime: TimePicker,
    isRating: Rating,
    isDuration: Duration,
    isPercent: Percent,
    isCurrency: Currency,
    isDecimal: Decimal,
    isInt: Integer,
    isFloat: Float,
    isLinks: Integer,
    isUser: User,
    isReadonlyUser: User,
  }
})

const filterType = computed(() => {
  return Object.keys(componentMap.value).find((key) => checkType(key as FilterType))
})

const componentProps = computed(() => {
  switch (filterType.value) {
    case 'isSingleSelect':
    case 'isMultiSelect': {
      return { disableOptionCreation: true }
    }
    case 'isPercent':
    case 'isDecimal':
    case 'isFloat':
    case 'isLinks':
    case 'isInt': {
      return { class: 'h-32px' }
    }
    case 'isDuration': {
      return { showValidationError: false }
    }
    case 'isUser': {
      return { forceMulti: true }
    }
    case 'isReadonlyUser': {
      if (['anyof', 'nanyof'].includes(props.filter.comparison_op!)) {
        return { forceMulti: true }
      }
      return {}
    }
    default: {
      return {}
    }
  }
})

const hasExtraPadding = computed(() => {
  return (
    column.value &&
    (column.value?.uidt === UITypes.Links ||
      isInt(column.value, abstractType) ||
      isDate(column.value, abstractType) ||
      isDateTime(column.value, abstractType) ||
      isTime(column.value, abstractType) ||
      isYear(column.value, abstractType))
  )
})

const isInputBoxOnFocus = ref(false)

// provide the following to override the default behavior and enable input fields like in form
provide(ActiveCellInj, ref(true))
provide(IsFormInj, ref(true))
</script>

<template>
  <a-select
    v-if="column && isBoolean(column, abstractType)"
    v-model:value="filterInput"
    :disabled="filter.readOnly"
    :options="booleanOptions"
  />
  <div
    v-else
    class="bg-white border-1 flex flex-grow min-h-4 h-full px-1 items-center nc-filter-input-wrapper !rounded-lg"
    :class="{ 'px-2': hasExtraPadding, 'border-brand-500': isInputBoxOnFocus }"
    @mouseup.stop
  >
    <component
      :is="filterType ? componentMap[filterType] : Text"
      v-model="filterInput"
      :disabled="filter.readOnly"
      placeholder="Enter a value"
      :column="column"
      class="flex !rounded-lg"
      v-bind="componentProps"
      location="filter"
      @focus="isInputBoxOnFocus = true"
      @blur="isInputBoxOnFocus = false"
    />
  </div>
</template>

<style lang="scss" scoped>
:deep(input) {
  @apply py-1.5;
}

:deep(.ant-picker) {
  @apply !py-0;
}
</style>
