<script lang="ts" setup>
import { type FormBuilderElement, type IntegrationType } from 'nocodb-sdk'
import { FORM_BUILDER_NON_CATEGORIZED, FormBuilderInputType, iconMap } from '#imports'

const emit = defineEmits(['change'])

const workflowContext = inject(WorkflowVariableInj, null)

const {
  form,
  formState,
  formSchema,
  formElementsCategorized,
  isLoading,
  validateInfos,
  deepReference,
  setFormState,
  loadOptions,
  getFieldOptions,
  getIsLoadingFieldOptions,
  toggleGroup,
  isGroupCollapsed,
  disabled,
} = useFormBuilderHelperOrThrow()

const { loadIntegrations, addIntegration, integrations, eventBus, pageMode, IntegrationsPageMode } =
  useProvideIntegrationViewStore()

const selectMode = (field: FormBuilderElement) => {
  return field.selectMode === 'multipleWithInput' || field.selectMode === 'singleWithInput'
    ? 'tags'
    : field.selectMode === 'multiple'
    ? 'multiple'
    : undefined
}

const haveIntegrationInput = computed(() => {
  return unref(formSchema)?.some((field) => field.type === FormBuilderInputType.SelectIntegration)
})

const filteredIntegrations = computed(() => {
  if (!haveIntegrationInput.value) return {}

  return (unref(formSchema) || [])
    .filter((field) => field.type === FormBuilderInputType.SelectIntegration && field.model)
    .reduce((acc, field) => {
      acc[field.model!] = integrations.value.filter((integration) => {
        if (field.integrationFilter) {
          return (
            (!field.integrationFilter.type || field.integrationFilter.type === integration.type) &&
            (!field.integrationFilter.sub_type || field.integrationFilter.sub_type === integration.sub_type)
          )
        }
        return true
      })
      return acc
    }, {} as Record<string, IntegrationType[]>)
})

const integrationOptions = computed(() => {
  if (!haveIntegrationInput.value) return {}

  return Object.keys(filteredIntegrations.value).reduce((acc, key) => {
    acc[key] = filteredIntegrations.value[key]!.map((integration) => ({
      label: integration.title as string,
      value: integration.id as string,
    }))
    return acc
  }, {} as Record<string, { label: string; value: string }[]>)
})

const activeModel = ref<string | null>(null)

const handleAddNewConnection = (field: FormBuilderElement) => {
  const model = field.model!

  if (field.integrationFilter) {
    const filteredIntegrations = allIntegrations.filter((i) => {
      if (field.integrationFilter) {
        return (
          (!field.integrationFilter.type || field.integrationFilter.type === i.type) &&
          (!field.integrationFilter.sub_type || field.integrationFilter.sub_type === i.sub_type)
        )
      }
      return true
    })

    if (filteredIntegrations?.length === 1) {
      addIntegration(filteredIntegrations[0]!, false)

      activeModel.value = null

      nextTick(() => {
        activeModel.value = model
      })

      return
    }
  }
  activeModel.value = null

  nextTick(() => {
    pageMode.value = IntegrationsPageMode.LIST
    activeModel.value = model
  })
}

const workflowVariables = computed(() => {
  if (!workflowContext?.selectedNodeId?.value || !workflowContext?.getAvailableVariablesFlat) {
    return []
  }
  return workflowContext.getAvailableVariablesFlat(workflowContext.selectedNodeId.value)
})

// Get grouped workflow variables for WorkflowInput fields
const workflowVariablesGrouped = computed(() => {
  if (!workflowContext?.selectedNodeId?.value || !workflowContext?.getAvailableVariables) {
    return []
  }
  return workflowContext.getAvailableVariables(workflowContext.selectedNodeId.value)
})

const filterIntegration = computed(() => {
  if (!activeModel.value) return { type: () => true, sub_type: () => true }

  const field = (unref(formSchema) || []).find((field) => field.model === activeModel.value)

  return {
    type: (f: IntegrationCategoryItemType) => {
      return !!(!field?.integrationFilter?.type || f.value === field?.integrationFilter?.type)
    },
    sub_type: (f: IntegrationItemType) => {
      return !!(!field?.integrationFilter?.sub_type || f.sub_type === field?.integrationFilter?.sub_type)
    },
  }
})

const setFormStateWithEmit = (path: string, value: any) => {
  setFormState(path, value)
  emit('change', path, value)
}

const isGroupCollapsibleInCategory = (category: string, groupName: string) => {
  const fields = formElementsCategorized.value[category] || []
  const firstFieldInGroup = fields.find((f) => f.group === groupName)
  return firstFieldInGroup?.groupCollapsible ?? false
}

const getGroupDefaultCollapsed = (category: string, groupName: string) => {
  const fields = formElementsCategorized.value[category] || []
  const firstFieldInGroup = fields.find((f) => f.group === groupName)
  return firstFieldInGroup?.groupDefaultCollapsed ?? true
}

const integegrationEventHandler = (event: IntegrationStoreEvents, payload: any) => {
  if (event === IntegrationStoreEvents.INTEGRATION_ADD && payload?.id && activeModel.value) {
    setFormStateWithEmit(activeModel.value, payload.id)
    activeModel.value = null
  }
}

const getSelectValue = (field: FormBuilderElement) => {
  const value = deepReference(field.model)
  if (field.selectMode === 'singleWithInput') {
    // Convert single value to array for tags mode
    return value && !ncIsArray(value) ? [value] : []
  }
  return value
}

const handleSelectChange = (field: FormBuilderElement, value: any) => {
  if (field.selectMode === 'singleWithInput') {
    // Convert array back to single value
    const singleValue = Array.isArray(value) ? value[value.length - 1] || null : value
    setFormStateWithEmit(field.model, singleValue)
  } else {
    setFormStateWithEmit(field.model, value)
  }
}

eventBus.on(integegrationEventHandler)

onBeforeUnmount(() => {
  eventBus.off(integegrationEventHandler)
})

watch(
  () => unref(formSchema),
  async () => {
    // if integration field is available, load the integration state
    if (haveIntegrationInput.value) {
      await loadIntegrations()
    }
  },
  { immediate: true },
)
</script>

<template>
  <div class="nc-form-builder nc-scrollbar-thin relative">
    <slot name="header"></slot>
    <a-form ref="form" :model="formState" hide-required-mark layout="vertical" class="flex flex-col gap-8 !pb-2">
      <template v-for="category in Object.keys(formElementsCategorized)" :key="category">
        <div class="nc-form-section">
          <div v-if="category !== FORM_BUILDER_NON_CATEGORIZED" class="nc-form-section-title">{{ category }}</div>
          <div class="nc-form-section-body-grid">
            <template
              v-for="(field, fieldIndex) in formElementsCategorized[category]"
              :key="field.model || `space-${fieldIndex}`"
            >
              <template v-if="field.type === FormBuilderInputType.Space">
                <div
                  :style="{
                    gridColumn: `span ${field.span || 24}`,
                  }"
                ></div>
              </template>

              <template v-else>
                <!-- Group toggle button (shown before first field in a collapsible group) -->
                <div
                  v-if="
                    field.group &&
                    isGroupCollapsibleInCategory(category, field.group) &&
                    (fieldIndex === 0 || formElementsCategorized[category][fieldIndex - 1]?.group !== field.group)
                  "
                  class="nc-group-toggle"
                  :style="{ gridColumn: 'span 24' }"
                >
                  <NcButton
                    type="text"
                    size="small"
                    class="!text-nc-content-gray"
                    @click="toggleGroup(`${category}-${field.group}`)"
                  >
                    <div class="flex items-center gap-2">
                      <GeneralIcon
                        :icon="
                          isGroupCollapsed(`${category}-${field.group}`, getGroupDefaultCollapsed(category, field.group))
                            ? 'ncChevronDown'
                            : 'ncChevronUp'
                        "
                        class="flex-none"
                      />
                      <span>{{
                        isGroupCollapsed(`${category}-${field.group}`, getGroupDefaultCollapsed(category, field.group))
                          ? field.groupLabel || $t('general.showMore')
                          : $t('general.showLess')
                      }}</span>
                    </div>
                  </NcButton>
                </div>

                <!-- Regular form field -->
                <a-form-item
                  v-if="
                    !field.group ||
                    !isGroupCollapsibleInCategory(category, field.group) ||
                    !isGroupCollapsed(`${category}-${field.group}`, getGroupDefaultCollapsed(category, field.group))
                  "
                  v-bind="validateInfos[field.model]"
                  class="nc-form-item"
                  :style="{
                    gridColumn: `span ${field.span || 24}`,
                  }"
                  :required="false"
                  :data-testid="`nc-form-input-${field.model}`"
                >
                  <template v-if="![FormBuilderInputType.Switch, FormBuilderInputType.Checkbox].includes(field.type)" #label>
                    <div class="flex items-center gap-1 w-full">
                      <div class="flex-1 flex items-center gap-1">
                        <span>{{ field.label }}</span>
                        <span
                          v-if="
                            field.required &&
                            ![
                              FormBuilderInputType.Select,
                              FormBuilderInputType.SelectIntegration,
                              FormBuilderInputType.SelectBase,
                            ].includes(field.type)
                          "
                          class="text-nc-content-red-medium"
                          >*</span
                        >
                        <NcTooltip v-if="field.helpText && field.showHintAsTooltip">
                          <template #title>
                            <div class="text-xs">
                              {{ field.helpText }}
                            </div>
                          </template>
                          <GeneralIcon icon="info" class="text-nc-content-gray-muted h-4" />
                        </NcTooltip>
                      </div>

                      <a
                        v-if="field.docsLink"
                        :href="field.docsLink"
                        target="_blank"
                        rel="noopener noreferrer"
                        class="text-xs justify-self-end no-underline hover:underline"
                      >
                        {{ $t('title.docs') }}
                      </a>
                    </div>
                  </template>
                  <template v-if="field.type === FormBuilderInputType.Input">
                    <a-input
                      autocomplete="off"
                      class="!w-full"
                      :disabled="disabled"
                      :value="deepReference(field.model)"
                      :placeholder="field.placeholder"
                      @update:value="setFormStateWithEmit(field.model, $event)"
                    >
                      <!-- Todo: @rameshmane7218 slots customization  -->
                      <!-- <template v-if="$slots[getValidSlotName(field.model, 'prefix')]" #prefix>
                        <slot :name="getValidSlotName(field.model, 'prefix')" />
                      </template>
<template v-if="$slots[getValidSlotName(field.model, 'suffix')]" #suffix>
                        <slot :name="getValidSlotName(field.model, 'suffix')" />
                      </template> -->
                    </a-input>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.Textarea">
                    <a-textarea
                      class="!w-full !rounded-lg !text-sm !min-h-[90px] max-h-[500px] nc-scrollbar-thin"
                      size="large"
                      hide-details
                      :value="deepReference(field.model)"
                      :placeholder="field.placeholder"
                      @update:value="setFormStateWithEmit(field.model, $event)"
                    />
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.Number">
                    <a-input-number
                      autocomplete="off"
                      class="!w-full !rounded-lg"
                      :controls="false"
                      :disabled="disabled"
                      :value="deepReference(field.model)"
                      :placeholder="field.placeholder"
                      @update:value="setFormStateWithEmit(field.model, $event)"
                    />
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.Password">
                    <a-input-password
                      readonly
                      :disabled="disabled"
                      onfocus="this.removeAttribute('readonly');"
                      onblur="this.setAttribute('readonly', true);"
                      autocomplete="off"
                      :value="deepReference(field.model)"
                      :placeholder="field.placeholder"
                      @update:value="setFormStateWithEmit(field.model, $event)"
                    />
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.Select">
                    <NcFormBuilderInputMountedWrapper @mounted="loadOptions(field)">
                      <NcSelect
                        :disabled="disabled"
                        :value="getSelectValue(field)"
                        :mode="selectMode(field)"
                        :max-tag-count="field.selectMode === 'singleWithInput' ? 1 : undefined"
                        show-search
                        :placeholder="field.placeholder"
                        :loading="field.fetchOptionsKey && getIsLoadingFieldOptions(field.model)"
                        @update:value="handleSelectChange(field, $event)"
                      >
                        <a-select-option
                          v-for="option in field.fetchOptionsKey ? getFieldOptions(field.model) : field.options"
                          :key="option.value"
                          :value="option.value"
                        >
                          <div class="w-full flex gap-2 items-center" :data-testid="option.value">
                            <GeneralIcon v-if="option.icon" :icon="option.icon" class="flex-none h-4 w-4" />
                            <NcTooltip class="flex-1 truncate min-w-0" show-on-truncate-only>
                              <template #title>
                                {{ option.label }}
                              </template>
                              {{ option.label }}
                            </NcTooltip>
                            <component
                              :is="iconMap.check"
                              v-if="
                                ncIsArray(getSelectValue(field))
                                  ? getSelectValue(field).includes(option.value)
                                  : getSelectValue(field) === option.value
                              "
                              id="nc-selected-item-icon"
                              class="text-nc-content-brand w-4 h-4"
                            />
                          </div>
                        </a-select-option>
                      </NcSelect>
                    </NcFormBuilderInputMountedWrapper>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.Switch">
                    <div class="flex flex-col px-2" :class="field.border ? 'border-1 rounded-lg shadow' : ''">
                      <div class="flex items-center aa">
                        <NcSwitch
                          :disabled="disabled"
                          :checked="!!deepReference(field.model)"
                          @update:checked="setFormStateWithEmit(field.model, $event)"
                        />
                        <span class="ml-[6px] font-bold">{{ field.label }}</span>
                        <NcTooltip v-if="field.helpText">
                          <template #title>
                            <div class="text-xs">
                              {{ field.helpText }}
                            </div>
                          </template>
                          <GeneralIcon icon="info" class="text-nc-content-gray-muted h-4 ml-1" />
                        </NcTooltip>
                      </div>
                      <div v-if="field.helpText && !field.showHintAsTooltip" class="w-full mt-1 pl-[35px]">
                        <div class="text-xs text-nc-content-gray-muted">{{ field.helpText }}</div>
                      </div>
                    </div>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.SelectIntegration">
                    <a-select
                      :value="deepReference(field.model)"
                      :options="integrationOptions[field.model]"
                      dropdown-match-select-width
                      class="nc-select nc-select-shadow"
                      placeholder="Select Integration"
                      allow-clear
                      :disabled="disabled"
                      show-search
                      @update:value="setFormStateWithEmit(field.model, $event)"
                    >
                      <template #suffixIcon>
                        <GeneralIcon icon="ncChevronDown" class="text-nc-content-gray-muted" />
                      </template>
                      <a-select-option
                        v-for="integration in filteredIntegrations[field.model]"
                        :key="integration.id"
                        :value="integration.id"
                      >
                        <div class="w-full flex gap-2 items-center" :data-testid="integration.title">
                          <GeneralIntegrationIcon v-if="integration?.sub_type" :type="integration.sub_type" />
                          <NcTooltip class="flex-1 truncate" show-on-truncate-only>
                            <template #title>
                              {{ integration.title }}
                            </template>
                            {{ integration.title }}
                          </NcTooltip>
                          <component
                            :is="iconMap.check"
                            v-if="formState.fk_integration_id === integration.id"
                            id="nc-selected-item-icon"
                            class="text-nc-content-brand w-4 h-4"
                          />
                        </div>
                      </a-select-option>

                      <template #dropdownRender="{ menuNode: menu }">
                        <component :is="menu" />
                        <a-divider style="margin: 4px 0" />
                        <div
                          class="px-1.5 flex items-center text-nc-content-brand text-sm cursor-pointer"
                          @mousedown.prevent
                          @click="handleAddNewConnection(field)"
                        >
                          <div class="w-full flex items-center gap-2 px-2 py-2 rounded-md hover:bg-nc-bg-gray-light">
                            <GeneralIcon icon="plus" class="flex-none" />
                            {{ $t('general.new') }} {{ $t('general.connection').toLowerCase() }}
                          </div>
                        </div>
                      </template>
                    </a-select>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.SelectBase">
                    <NcFormBuilderInputSelectBase
                      :value="deepReference(field.model)"
                      :disabled="disabled"
                      :filter-option="field.filterOption"
                      @update:value="setFormStateWithEmit(field.model, $event)"
                    />
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.SelectTable">
                    <NcFormBuilderInputMountedWrapper @mounted="loadOptions(field)">
                      <NcFormBuilderInputSelectTable
                        :multiple="field?.selectMode === 'multiple'"
                        :value="deepReference(field.model)"
                        :disabled="disabled"
                        :options="getFieldOptions(field.model)"
                        @update:value="setFormStateWithEmit(field.model, $event)"
                      />
                    </NcFormBuilderInputMountedWrapper>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.SelectView">
                    <NcFormBuilderInputMountedWrapper @mounted="loadOptions(field)">
                      <NcFormBuilderInputSelectView
                        :multiple="field?.selectMode === 'multiple'"
                        :value="deepReference(field.model)"
                        :disabled="disabled"
                        :options="getFieldOptions(field.model)"
                        @update:value="setFormStateWithEmit(field.model, $event)"
                      />
                    </NcFormBuilderInputMountedWrapper>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.SelectField">
                    <NcFormBuilderInputMountedWrapper @mounted="loadOptions(field)">
                      <NcFormBuilderInputSelectField
                        :multiple="field?.selectMode === 'multiple'"
                        :value="deepReference(field.model)"
                        :disabled="disabled"
                        :options="getFieldOptions(field.model)"
                        @update:value="setFormStateWithEmit(field.model, $event)"
                      />
                    </NcFormBuilderInputMountedWrapper>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.OAuth">
                    <NcFormBuilderInputOAuth
                      :value="deepReference(field.model)"
                      :element="field"
                      :have-value="!!deepReference(field.model)"
                      :form-data="formState"
                      @update:value="setFormStateWithEmit(field.model, $event)"
                    />
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.Checkbox">
                    <div
                      class="px-3 py-2 border-1 cursor-pointer rounded-lg shadow-default transition-shadow border-nc-border-gray-medium"
                      @click="setFormStateWithEmit(field.model, !deepReference(field.model))"
                    >
                      <div class="flex gap-3">
                        <NcCheckbox :disabled="disabled" :checked="deepReference(field.model)" />
                        <div class="text-nc-content-gray text-caption">
                          {{ field.label }}
                        </div>
                      </div>
                      <div v-if="field.description" class="text-nc-content-gray-muted text-bodySm mt-1 pl-7.8">
                        {{ field.description }}
                      </div>
                    </div>
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.WorkflowInput">
                    <NcFormBuilderInputWorkflowInput
                      :model-value="deepReference(field.model)"
                      :placeholder="field.placeholder"
                      :variables="workflowVariables"
                      :read-only="disabled"
                      :plugins="field.plugins"
                      :grouped-variables="workflowVariablesGrouped"
                      @update:model-value="setFormStateWithEmit(field.model, $event)"
                    />
                  </template>
                  <template v-else-if="field.type === FormBuilderInputType.FieldMapping">
                    <NcFormBuilderInputMountedWrapper @mounted="loadOptions(field)">
                      <NcFormBuilderInputFieldMapping
                        :model-value="deepReference(field.model)"
                        :element="field"
                        :disabled="disabled"
                        @update:model-value="setFormStateWithEmit(field.model, $event)"
                      />
                    </NcFormBuilderInputMountedWrapper>
                  </template>
                  <div
                    v-if="field.helpText && field.type !== FormBuilderInputType.Switch && !field.showHintAsTooltip"
                    class="w-full mt-1"
                  >
                    <div class="text-xs text-nc-content-gray-muted">{{ field.helpText }}</div>
                  </div>
                </a-form-item>
              </template>
            </template>
          </div>
        </div>
      </template>
    </a-form>
    <template v-if="haveIntegrationInput && activeModel">
      <WorkspaceIntegrationsTab
        is-modal
        :filter-category="filterIntegration.type"
        :filter-integration="filterIntegration.sub_type"
      />
      <WorkspaceIntegrationsEditOrAdd />
    </template>
    <general-overlay :model-value="isLoading" inline transition class="!bg-opacity-15">
      <div class="flex items-center justify-center h-full w-full !bg-nc-bg-default !bg-opacity-85 z-1000">
        <a-spin size="large" />
      </div>
    </general-overlay>
  </div>
</template>

<style lang="scss" scoped>
.nc-form-item {
  @apply px-0.5;
  margin-bottom: 12px;
}

:deep(.ant-collapse-header) {
  @apply !-mt-4 !p-0 flex items-center !cursor-default children:first:flex;
}

:deep(.ant-collapse-icon-position-right > .ant-collapse-item > .ant-collapse-header .ant-collapse-arrow) {
  @apply !right-0;
}

:deep(.ant-collapse-content-box) {
  @apply !px-0 !pb-0 !pt-3;
}

:deep(.ant-form-item-explain-error) {
  @apply !text-xs;
}

:deep(.ant-divider) {
  @apply m-0;
}

:deep(.ant-form-item-with-help .ant-form-item-explain) {
  @apply !min-h-0;
}

:deep(.ant-select .ant-select-selector .ant-select-selection-item) {
  @apply font-weight-400;
}

.nc-form-builder {
  :deep(.ant-input-affix-wrapper),
  :deep(.ant-input),
  :deep(.ant-select) {
    @apply !appearance-none border-solid rounded-md;
  }

  :deep(.ant-input-password) {
    input {
      @apply !border-none my-0;
    }
  }

  :deep(.ant-form-item-label > label.ant-form-item-required:after) {
    @apply content-['*'] inline-block text-inherit text-nc-content-red-medium ml-1;
  }

  :deep(.ant-form-item-label label) {
    @apply w-full;
  }

  :deep(.ant-form-item) {
    &.ant-form-item-has-error {
      &:not(:has(.ant-input-password)) .ant-input {
        &:not(:hover):not(:focus):not(:disabled) {
          @apply shadow-default;
        }

        &:hover:not(:focus):not(:disabled) {
          @apply shadow-hover;
        }

        &:focus {
          @apply shadow-error ring-0;
        }
      }

      .ant-input-number,
      .ant-input-affix-wrapper.ant-input-password {
        &:not(:hover):not(:focus-within):not(:disabled) {
          @apply shadow-default;
        }

        &:hover:not(:focus-within):not(:disabled) {
          @apply shadow-hover;
        }

        &:focus-within {
          @apply shadow-error ring-0;
        }
      }
    }

    &:not(.ant-form-item-has-error) {
      &:not(:has(.ant-input-password)) .ant-input {
        &:not(:hover):not(:focus):not(:disabled) {
          @apply shadow-default border-nc-border-gray-medium;
        }

        &:hover:not(:focus):not(:disabled) {
          @apply border-nc-border-gray-medium shadow-hover;
        }

        &:focus {
          @apply shadow-selected ring-0;
        }
      }

      .ant-input-number,
      .ant-input-affix-wrapper.ant-input-password {
        &:not(:hover):not(:focus-within):not(:disabled) {
          @apply shadow-default border-nc-border-gray-medium;
        }

        &:hover:not(:focus-within):not(:disabled) {
          @apply border-nc-border-gray-medium shadow-hover;
        }

        &:focus-within {
          @apply shadow-selected ring-0;
        }
      }
    }
  }

  :deep(.ant-row:not(.ant-form-item)) {
    @apply !-mx-1.5;

    & > .ant-col {
      @apply !px-1.5;
    }
  }

  :deep(.ant-form-item) {
    @apply !mb-0;
  }
}

.nc-group-toggle {
  @apply mt-2 mb-2;

  button {
    @apply hover: !text-nc-content-brand;
  }
}
</style>

<style lang="scss"></style>
