<script setup lang="ts">
import {
  FormulaError,
  UITypes,
  isHiddenCol,
  substituteColumnIdWithAliasInFormula,
  validateFormulaAndExtractTreeWithType,
} from 'nocodb-sdk'
import { ButtonActionsType, type ButtonType, type ColumnType, type HookType } from 'nocodb-sdk'
import { searchIcons } from '../../../utils/iconUtils'

const props = defineProps<{
  value: any
  fromTableExplorer?: boolean
}>()

const emit = defineEmits(['update:value'])

const buttonActionsType = {
  ...ButtonActionsType,
}

const { t } = useI18n()

const { isUIAllowed } = useRoles()

const { getMeta } = useMetas()

const { isFeatureEnabled } = useBetaFeatureToggle()

const vModel = useVModel(props, 'value', emit)

const meta = inject(MetaInj, ref())

const { isEdit, setAdditionalValidations, validateInfos, sqlUi, column, isWebhookCreateModalOpen, isAiMode } =
  useColumnCreateStoreOrThrow()

const uiTypesNotSupportedInFormulas = [UITypes.QrCode, UITypes.Barcode, UITypes.Button]

const webhooksStore = useWebhooksStore()

const { loadHooksList } = webhooksStore

await loadHooksList()

const { hooks } = toRefs(webhooksStore)

const selectedWebhook = ref<HookType>()

const manualHooks = computed(() => {
  return hooks.value.filter((hook) => hook.event === 'manual' && hook.active)
})

const buttonTypes = [
  {
    label: t('labels.openUrl'),
    value: ButtonActionsType.Url,
  },
  {
    label: t('labels.runWebHook'),
    value: ButtonActionsType.Webhook,
  },
  ...(isFeatureEnabled(FEATURE_FLAG.AI_FEATURES)
    ? [
        {
          label: t('labels.generateFieldDataUsingAi'),
          value: ButtonActionsType.Ai,
          tooltip: t('tooltip.generateFieldDataUsingAiButtonOption'),
        },
      ]
    : []),
]

const supportedColumns = computed(
  () =>
    meta?.value?.columns?.filter((col) => {
      if (uiTypesNotSupportedInFormulas.includes(col.uidt as UITypes)) {
        return false
      }

      if (isHiddenCol(col, meta.value)) {
        return false
      }

      return true
    }) || [],
)

const validators = {
  formula_raw: [
    {
      required: vModel.value.type === ButtonActionsType.Url,
      validator: (_: any, formula: any) => {
        return (async () => {
          if (vModel.value.type === ButtonActionsType.Url) {
            if (!formula?.trim()) throw new Error('Formula is required for URL Button')

            try {
              await validateFormulaAndExtractTreeWithType({
                column: column.value,
                formula,
                columns: supportedColumns.value,
                clientOrSqlUi: sqlUi.value,
                getMeta,
              })
            } catch (e: any) {
              if (e instanceof FormulaError && e.extra?.key) {
                throw new Error(t(e.extra.key, e.extra))
              }

              throw new Error(e.message)
            }
          }
        })()
      },
    },
  ],
  fk_webhook_id: [
    {
      required: vModel.value.type === ButtonActionsType.Webhook,
      validator: (_: any, fk_webhook_id: any) => {
        return new Promise<void>((resolve, reject) => {
          if (vModel.value.type === ButtonActionsType.Webhook && !fk_webhook_id) {
            reject(new Error(t('general.required')))
          }
          resolve()
        })
      },
    },
  ],
  color: [
    {
      validator: (_: any, color: any) => {
        return new Promise<void>((resolve, reject) => {
          if (!['brand', 'red', 'green', 'maroon', 'blue', 'orange', 'pink', 'purple', 'yellow', 'gray'].includes(color)) {
            reject(new Error(t('msg.invalidColor')))
          }
          resolve()
        })
      },
    },
  ],
  theme: [
    {
      validator: (_: any, theme: any) => {
        return new Promise<void>((resolve, reject) => {
          if (!['solid', 'light', 'text'].includes(theme)) {
            reject(new Error(t('msg.invalidTheme')))
          }
          resolve()
        })
      },
    },
  ],
  type: [
    {
      validator: (_: any, type: any) => {
        return new Promise<void>((resolve, reject) => {
          if (!Object.values(ButtonActionsType).includes(type)) {
            reject(new Error(t('msg.invalidType')))
          }
          resolve()
        })
      },
    },
  ],
  label: [
    {
      validator: (_: any, label: any) => {
        return new Promise<void>((resolve, reject) => {
          if (!(label.length > 0) && !vModel.value.icon) {
            reject(new Error(t('msg.invalidLabel')))
          }
          resolve()
        })
      },
    },
  ],

  ...((isEdit.value ? vModel.value.colOptions?.type : vModel.value.type) === ButtonActionsType.Ai
    ? {
        output_column_ids: [
          {
            required: true,
            message: 'At least one output field is required for AI Button',
          },
        ],
        formula_raw: [
          {
            required: true,
            message: 'Prompt required for AI Button',
          },
        ],
        fk_integration_id: [{ required: true, message: t('general.required') }],
      }
    : {}),
}

if (isEdit.value) {
  const colOptions = vModel.value.colOptions as ButtonType
  vModel.value.type = colOptions?.type
  vModel.value.theme = colOptions?.theme
  vModel.value.label = colOptions?.label
  vModel.value.color = colOptions?.color
  vModel.value.fk_webhook_id = colOptions?.fk_webhook_id
  vModel.value.icon = colOptions?.icon
  selectedWebhook.value = hooks.value.find((hook) => hook.id === vModel.value?.fk_webhook_id)

  if (vModel.value.type === ButtonActionsType.Ai) {
    vModel.value.formula_raw = colOptions?.formula_raw || ''
    vModel.value.output_column_ids = colOptions?.output_column_ids || ''
    vModel.value.fk_integration_id = colOptions?.fk_integration_id
  }
} else {
  vModel.value.type = vModel.value?.type || buttonTypes[0].value

  if (vModel.value.type === ButtonActionsType.Ai) {
    vModel.value.theme = 'light'
    vModel.value.label = 'Generate data'
    vModel.value.color = 'purple'
    vModel.value.icon = 'ncAutoAwesome'
    vModel.value.output_column_ids = ''
  } else {
    vModel.value.theme = 'solid'
    vModel.value.label = 'Button'
    vModel.value.color = 'brand'
  }

  vModel.value.formula_raw = ''
}

setAdditionalValidations({
  ...validators,
})

// set default value
if (vModel.value?.type === ButtonActionsType.Url || (column.value?.colOptions as any)?.type === ButtonActionsType.Url) {
  if ((column.value?.colOptions as any)?.formula_raw) {
    vModel.value.formula_raw =
      substituteColumnIdWithAliasInFormula(
        (column.value?.colOptions as ButtonType)?.formula,
        meta?.value?.columns as ColumnType[],
        (column.value?.colOptions as any)?.formula_raw,
      ) || ''
  } else {
    vModel.value.formula_raw = ''
  }
}

const colorClass = {
  solid: {
    brand: 'bg-brand-500 text-white',
    red: 'bg-red-600 text-white',
    green: 'bg-green-600 text-white',
    maroon: 'bg-maroon-600 text-white',
    blue: 'bg-blue-600 text-white',
    orange: 'bg-orange-600 text-white',
    pink: 'bg-pink-600 text-white',
    purple: 'bg-purple-500 text-white',
    yellow: 'bg-yellow-600 text-white',
    gray: 'bg-gray-600 text-white',
  },
  light: {
    brand: 'bg-brand-200 text-gray-800',
    red: 'bg-red-200 text-gray-800',
    green: 'bg-green-200 text-gray-800',
    maroon: 'bg-maroon-200 text-gray-800',
    blue: 'bg-blue-200 text-gray-800',
    orange: 'bg-orange-200 text-gray-800',
    pink: 'bg-pink-200 text-gray-800',
    purple: 'bg-purple-200 text-gray-800',
    yellow: 'bg-yellow-200 text-gray-800',
    gray: 'bg-gray-200',
  },
  text: {
    brand: 'text-brand-500',
    red: 'text-red-600',
    green: 'text-green-600',
    maroon: 'text-maroon-600',
    blue: 'text-blue-600',
    orange: 'text-orange-600',
    pink: 'text-pink-600',
    purple: 'text-purple-500',
    yellow: 'text-yellow-600',
    gray: 'text-gray-600',
  },
}

const isDropdownOpen = ref(false)

const updateButtonTheme = (type: string, name: string) => {
  vModel.value.theme = type
  vModel.value.color = name
  isDropdownOpen.value = false
}

const isWebHookSelectionDropdownOpen = ref(false)

const isButtonIconDropdownOpen = ref(false)

const iconSearchQuery = ref('')

const icons = computed(() => {
  return searchIcons(iconSearchQuery.value)
})

const eventList = ref<Record<string, any>[]>([
  { text: [t('general.manual'), t('general.trigger')], value: ['manual', 'trigger'] },
])

const isWebhookModal = ref(false)

const newWebhook = () => {
  selectedWebhook.value = undefined
  isWebhookModal.value = true
  isWebhookCreateModalOpen.value = true
}

const onClose = (hook: HookType) => {
  selectedWebhook.value = hook.id ? hook : undefined
  vModel.value.fk_webhook_id = hook.id
  isWebhookModal.value = false
  setTimeout(() => {
    isWebhookCreateModalOpen.value = false
  }, 500)
}

const onSelectWebhook = (hook: HookType) => {
  vModel.value.fk_webhook_id = hook.id
  selectedWebhook.value = hook
  isWebHookSelectionDropdownOpen.value = false
  isWebhookModal.value = false
}

const removeIcon = () => {
  vModel.value.icon = null
  isButtonIconDropdownOpen.value = false
}

const editWebhook = () => {
  if (selectedWebhook.value) {
    isWebhookCreateModalOpen.value = true
    isWebhookModal.value = true
  }
}

const selectIcon = (icon: string) => {
  vModel.value.icon = icon
  isButtonIconDropdownOpen.value = false
}

const handleUpdateActionType = (type: ButtonActionsType) => {
  // We are using `formula_raw` in both type url & ai, so it's imp to reset it
  if (type !== ButtonActionsType.Ai) {
    setAdditionalValidations({
      formula_raw: validators.formula_raw,
    })
  }

  vModel.value.formula_raw = ''
}

watch(isWebhookModal, (newVal) => {
  if (!newVal) {
    setTimeout(() => {
      isWebhookCreateModalOpen.value = false
    }, 500)
  }
})
</script>

<template>
  <div class="relative flex flex-col gap-4">
    <a-row :gutter="8">
      <a-col :span="12">
        <a-form-item v-bind="validateInfos.label" class="mt-4" :label="$t('general.label')">
          <a-input
            v-model:value="vModel.label"
            class="nc-column-label-input nc-input-shadow !rounded-lg"
            :class="{
              'nc-ai-input': isAiMode,
            }"
            placeholder="Button"
          />
        </a-form-item>
      </a-col>
      <a-col :span="6">
        <a-form-item :label="$t('general.style')" v-bind="validateInfos.theme">
          <NcDropdown v-model:visible="isDropdownOpen" class="nc-color-picker-dropdown-trigger">
            <div
              :class="{
                'nc-button-style-dropdown': isDropdownOpen,
                '!border-nc-border-purple !shadow-selected-ai': isDropdownOpen && isAiMode,
                '!border-brand-500 !shadow-selected': isDropdownOpen && !isAiMode,
              }"
              class="flex items-center justify-between border-1 h-8 px-[11px] border-gray-300 !w-full transition-all cursor-pointer !rounded-lg"
            >
              <div
                :class="`${vModel.color ?? 'brand'} ${vModel.theme ?? 'solid'}`"
                class="flex items-center justify-center nc-cell-button rounded-md h-6 w-6 gap-2"
              >
                <component :is="iconMap.cellText" class="w-4 h-4" />
              </div>
              <GeneralIcon icon="arrowDown" class="text-gray-500 !w-4 !h-4" />
            </div>
            <template #overlay>
              <div class="bg-white space-y-2 p-2 rounded-lg">
                <div v-for="[type, colors] in Object.entries(colorClass)" :key="type" class="flex gap-2">
                  <div v-for="[name, color] in Object.entries(colors)" :key="name">
                    <button
                      :class="{
                        [color]: true,
                        '!border-transparent': type !== 'text',
                      }"
                      class="border-1 border-gray-200 flex items-center justify-center rounded h-6 w-6"
                      @click="updateButtonTheme(type, name)"
                    >
                      <component :is="iconMap.cellText" class="w-3.5 h-3.5" />
                    </button>
                  </div>
                </div>
              </div>
            </template>
          </NcDropdown>
        </a-form-item>
      </a-col>
      <a-col :span="6">
        <a-form-item :label="$t('labels.icon')" v-bind="validateInfos.icon">
          <NcDropdown v-model:visible="isButtonIconDropdownOpen" class="nc-color-picker-dropdown-trigger">
            <div
              :class="{
                'nc-button-style-dropdown ': isButtonIconDropdownOpen,
                '!border-nc-border-purple !shadow-selected-ai': isButtonIconDropdownOpen && isAiMode,
                '!border-brand-500 !shadow-selected': isButtonIconDropdownOpen && !isAiMode,
              }"
              class="flex items-center justify-center border-1 h-8 px-[11px] border-gray-300 !w-full transition-all cursor-pointer !rounded-lg"
            >
              <div class="flex w-full items-center leading-5 justify-between gap-1">
                <GeneralIcon v-if="vModel.icon" :icon="vModel.icon as any" class="w-4 h-4 text-gray-700" />
                <div v-else class="text-sm flex items-center leading-5 text-gray-500">
                  {{ $t('labels.selectIcon') }}
                </div>
                <GeneralIcon icon="arrowDown" class="text-gray-500 !w-4 !h-4" />
              </div>
            </div>
            <template #overlay>
              <div class="bg-white w-80 space-y-3 h-70 overflow-y-auto rounded-lg">
                <div class="!sticky top-0 flex gap-2 bg-white px-2 py-2">
                  <a-input
                    ref="inputRef"
                    v-model:value="iconSearchQuery"
                    :placeholder="$t('placeholder.searchIcons')"
                    class="nc-dropdown-search-unified-input z-10 nc-input-shadow"
                    :class="{
                      'nc-ai-input': isAiMode,
                    }"
                  >
                    <template #prefix> <GeneralIcon icon="search" class="nc-search-icon h-3.5 w-3.5 mr-1" /> </template
                  ></a-input>
                  <NcButton size="small" class="!px-4" type="text" @click="removeIcon">
                    <span class="text-[13px]">
                      {{ $t('general.remove') }}
                    </span>
                  </NcButton>
                </div>

                <div class="grid px-3 auto-rows-max pb-2 nc-scrollbar-md gap-3 grid-cols-10">
                  <component
                    :is="icon"
                    v-for="({ icon, name }, i) in icons"
                    :key="i"
                    :icon="icon"
                    class="w-6 hover:bg-gray-100 cursor-pointer rounded p-1 text-gray-700 h-6"
                    @click="selectIcon(name)"
                  />
                </div>
              </div>
            </template>
          </NcDropdown>
        </a-form-item>
      </a-col>
    </a-row>
    <a-row :gutter="8">
      <a-col :span="24">
        <a-form-item :label="$t('labels.onClick')" v-bind="validateInfos.type">
          <a-select
            v-model:value="vModel.type"
            class="w-52 nc-button-type-select nc-select-shadow"
            :class="{
              'nc-ai-input': isAiMode,
            }"
            dropdown-class-name="nc-dropdown-button-cell-type"
            @change="handleUpdateActionType"
          >
            <template #suffixIcon> <GeneralIcon icon="arrowDown" class="text-gray-500" /> </template>

            <a-select-option v-for="(type, i) of buttonTypes" :key="i" :value="type.value">
              <NcTooltip :disabled="!type.tooltip" placement="right" class="w-full" :title="type.tooltip">
                <div class="flex gap-2 w-full capitalize text-gray-800 truncate items-center">
                  <div class="flex-1">
                    {{ type.label }}
                  </div>
                  <component
                    :is="iconMap.check"
                    v-if="vModel.type === type.value"
                    id="nc-selected-item-icon"
                    class="text-primary w-4 h-4"
                  />
                </div>
              </NcTooltip>
            </a-select-option>
          </a-select>
        </a-form-item>
      </a-col>
    </a-row>
    <div v-if="vModel?.type === buttonActionsType.Url">
      <SmartsheetColumnFormulaInputHelper
        v-model:value="vModel.formula_raw"
        suggestion-height="medium"
        editor-height="50px"
        disable-suggestion-headers
        :label="$t('labels.urlFormula')"
        :error="validateInfos.formula_raw?.validateStatus === 'error'"
      />
    </div>

    <a-form-item v-if="vModel?.type === buttonActionsType.Webhook">
      <div class="mb-2 text-gray-800 text-[13px] flex justify-between">
        {{ $t('labels.webhook') }}
        <a
          class="font-medium"
          href="https://docs.nocodb.com/fields/field-types/custom-types/button#create-a-button-field"
          target="_blank"
        >
          Docs
        </a>
      </div>
      <div class="flex rounded-lg">
        <NcDropdown v-model:visible="isWebHookSelectionDropdownOpen" :trigger="['click']">
          <template #overlay>
            <NcListWithSearch
              v-if="isWebHookSelectionDropdownOpen"
              :is-parent-open="isWebHookSelectionDropdownOpen"
              :search-input-placeholder="$t('placeholder.searchFields')"
              :option-config="{ selectOptionEvent: ['c:actions:webhook'], optionClassName: '' }"
              :options="manualHooks"
              :selected-option-id="selectedWebhook?.id"
              disable-mascot
              class="max-h-72 max-w-85"
              filter-field="title"
              show-selected-option
              @selected="onSelectWebhook"
            >
              <template v-if="isUIAllowed('hookCreate')" #bottom>
                <a-divider style="margin: 4px 0" />
                <div class="flex items-center text-brand-500 text-sm cursor-pointer" @click="newWebhook">
                  <div class="w-full flex justify-between items-center gap-2 px-2 py-2 rounded-md hover:bg-gray-100">
                    {{ $t('general.create') }} {{ $t('objects.webhook').toLowerCase() }}
                    <GeneralIcon icon="plus" class="flex-none" />
                  </div>
                </div>
              </template>
            </NcListWithSearch>
          </template>
          <div
            :class="{
              'nc-button-style-dropdown shadow-dropdown-open remove-right-shadow': isWebHookSelectionDropdownOpen,
            }"
            class="nc-button-webhook-select border-r-0 flex items-center justify-center border-1 h-8 px-[8px] border-gray-300 !w-full transition-all cursor-pointer !rounded-l-lg"
          >
            <div class="flex w-full items-center gap-2">
              <div
                :key="selectedWebhook?.id"
                class="flex items-center overflow-x-clip truncate text-ellipsis w-full gap-1 text-gray-800"
              >
                <NcTooltip
                  :class="{
                    'text-gray-500': !selectedWebhook?.title,
                  }"
                  class="truncate max-w-full"
                  show-on-truncate-only
                >
                  <template #title>
                    {{ !selectedWebhook?.title ? $t('labels.selectAWebhook') : selectedWebhook?.title }}
                  </template>
                  {{ !selectedWebhook?.title ? $t('labels.selectAWebhook') : selectedWebhook?.title }}
                </NcTooltip>
              </div>
              <GeneralIcon
                icon="arrowDown"
                :class="{
                  'transform rotate-180': isWebHookSelectionDropdownOpen,
                }"
                class="text-gray-500 transition-all transition-transform"
              />
            </div>
          </div>
        </NcDropdown>
        <NcButton
          size="small"
          type="secondary"
          class="!rounded-l-none border-l-[#d9d9d9] !hover:bg-white nc-button-style-dropdown"
          :class="{
            'nc-button-style-dropdown shadow-dropdown-open remove-left-shadow': isWebHookSelectionDropdownOpen,
          }"
          @click="editWebhook"
        >
          <GeneralIcon
            :class="{
              'text-gray-400': !selectedWebhook,
              'text-gray-700': selectedWebhook,
            }"
            icon="ncEdit"
          />
        </NcButton>
      </div>
    </a-form-item>

    <Webhook
      v-if="isWebhookModal"
      v-model:value="isWebhookModal"
      :hook="selectedWebhook"
      :event-list="eventList"
      @close="onClose"
    />
  </div>
</template>

<style scoped lang="scss">
.shadow-dropdown-open {
  @apply transition-all duration-0.3s;

  &:not(:focus-within) {
    box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.24);
  }
}

:deep(.ant-form-item-label > label) {
  @apply !text-small !leading-[18px] mb-2 !text-gray-800 flex;
}

.nc-list-with-search {
  @apply w-full;
}
.remove-right-shadow {
  clip-path: inset(-2px 0px -2px -2px) !important;
}

.remove-left-shadow {
  clip-path: inset(-2px -2px -2px 0px) !important;
}

.mono-font {
  font-family: 'JetBrainsMono', monospace;
}

.nc-button-style-dropdown {
  @apply border-[#d9d9d9];
}

.nc-cell-button {
  &.solid {
    @apply text-white;

    &.brand {
      @apply bg-brand-500;
    }

    &.red {
      @apply bg-red-600;
    }

    &.green {
      @apply bg-green-600;
    }

    &.maroon {
      @apply bg-maroon-600;
    }

    &.blue {
      @apply bg-blue-600;
    }

    &.orange {
      @apply bg-orange-600;
    }

    &.pink {
      @apply bg-pink-600;
    }

    &.purple {
      @apply bg-purple-500;
    }

    &.yellow {
      @apply bg-yellow-600;
    }

    &.gray {
      @apply bg-gray-600;
    }
  }

  &.light {
    @apply text-gray-700;

    &.brand {
      @apply bg-brand-200;
    }

    &.red {
      @apply bg-red-200;
    }

    &.green {
      @apply bg-green-200;
    }

    &.maroon {
      @apply bg-maroon-200;
    }

    &.blue {
      @apply bg-blue-200;
    }

    &.orange {
      @apply bg-orange-200;
    }

    &.pink {
      @apply bg-pink-200;
    }

    &.purple {
      @apply bg-purple-200;
    }

    &.yellow {
      @apply bg-yellow-200;
    }

    &.gray {
      @apply bg-gray-200;
    }
  }

  &.text {
    @apply border-1 border-gray-200 rounded;

    &.brand {
      @apply text-brand-500;
    }

    &.red {
      @apply text-red-600;
    }

    &.green {
      @apply text-green-600;
    }

    &.maroon {
      @apply text-maroon-600;
    }

    &.blue {
      @apply text-blue-600;
    }

    &.orange {
      @apply text-orange-600;
    }

    &.pink {
      @apply text-pink-600;
    }

    &.purple {
      @apply text-purple-500;
    }

    &.yellow {
      @apply text-yellow-600;
    }

    &.gray {
      @apply text-gray-600;
    }
  }
}
</style>
