<template>
  <text
    v-if="item.type === 'static-text' "
    class="form-static-text"
    :style="(params.style as any)"
    :class="(params.class as any)"
  >
    {{ params?.text ?? model ?? null }}
  </text>
  <Field
    ref="formItemRef"
    v-else-if="item.type && filedInternalTypes.includes(item.type)"
    :label="label"
    :multiline="item.type === 'textarea'"
    :tags="item.type.endsWith('-tag')"
    :name="item.name"
    :modelValue="model"
    @update:modelValue="onValueChanged"
    :maxlength="260"
    :showBottomBorder="!isLast"
    :required="Boolean(item.rules?.length)"
    :rules="item.rules"
    :disabled="disabled"
    :readonly="readonly"
    v-bind="{ 
      ...params,
      ...extraDefine?.itemProps || {},
      ...item.formProps,
    }"
  />
  <Field
    v-else
    ref="formItemRef"
    :label="label"
    :name="item.name"
    :required="Boolean(item.rules?.length)"
    :showRightArrow="extraDefine?.needArrow"
    :showBottomBorder="!isLast"
    :requireChildRef="() => itemRef"
    :rules="item.rules"
    :disabled="disabled"
    :readonly="readonly"
    v-bind="{ 
      ...extraDefine?.itemProps || {},
      ...item.formProps,
    }"
  >
    <!-- <text>fullName: {{item.name}}</text> -->
    <slot name="insertion">
      <template v-if="item.type === 'custom'">
        <slot name="formCeil" :data="data" />
      </template>
      <template v-else-if="item.type === 'number'">
        <Stepper
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="params"
        />
      </template>
      <template v-else-if="item.type === 'switch'">
        <Switch
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="params"
        />
      </template>
      <template v-else-if="item.type === 'radio-value'">
        <RadioValue
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any as RadioValueProps)"
        />
      </template>
      <template v-else-if="item.type === 'radio-id'">
        <RadioIdField
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any as RadioIdFieldProps)"
        />
      </template>
      <template v-else-if="item.type === 'select'">
        <view>
          <NaPickerField 
            ref="itemRef"
            :modelValue="model"
            :disabled="disabled || readonly"
            @update:modelValue="onValueChanged"
            v-bind="(params as any as PickerFieldProps)"
          />
        </view>
      </template>
      <template v-else-if="item.type === 'rate'">
        <Rate
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any as RateProps)"
        />
      </template>
      <template v-else-if="item.type === 'uploader'">
        <UploaderField
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled"
          :readonly="readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any as UploaderFieldProps)"
        />
      </template>
      <template v-else-if="item.type === 'select-id'">
        <PickerIdField 
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled"
          :readonly="readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any as PickerIdFieldProps)"
        />
      </template>
      <template v-else-if="item.type === 'select-city'">
        <PickerCityField
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled"
          :readonly="readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any)"
        />
      </template>
      <template v-else-if="item.type === 'select-address'">
        <PickerAddressField
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled"
          :readonly="readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any)"
        />
      </template>
      <template v-else-if="item.type === 'select-lonlat'">
        <PickerLonlat
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="(v:any) => onValueChanged(v)"
          v-bind="params"
        />
      </template>
      <template v-else-if="item.type === 'check-box'">
        <CheckBox
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="params"
        />
      </template>
      <template v-else-if="item.type === 'check-box-list'"> 
        <CheckBoxList
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params)"
        />
      </template>
      <template v-else-if="item.type === 'check-box-tree'"> 
        <CheckBoxTreeList
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="(params as any as CheckBoxTreeListProps)"
        />
      </template>
      <template v-else-if="item.type === 'check-box-int'"> 
        <CheckBoxToInt
          ref="itemRef"
          :modelValue="model"
          :disabled="disabled || readonly"
          @update:modelValue="onValueChanged"
          v-bind="params"
        />
      </template>
      <template v-else-if="item.type === 'datetime'">
        <view>
          <DateTimePickerField
            ref="itemRef"
            :modelValue="model"
            v-bind="params"
            @update:modelValue="(e: any) => onValueChanged(e)"
          />
        </view>
      </template>
      <template v-else-if="item.type === 'time'">
        <view>
          <TimePickerField
            ref="itemRef"
            :modelValue="model"
            v-bind="params"
            @update:modelValue="(e: any) => onValueChanged(e)"
          />
        </view>
      </template>
      <template v-else-if="item.type === 'date'">
        <view>
          <DatePickerField
            ref="itemRef"
            :modelValue="model"
            v-bind="params"
            @update:modelValue="(e: any) => onValueChanged(e)"
          />
        </view>
      </template>
      <template v-else-if="item.type === 'button'">
        <Button
          ref="itemRef"
          :disabled="disabled || readonly"
          v-bind="params"
        />
      </template>
      <template v-else-if="item.type === 'image'">
        <Image
          ref="itemRef"
          v-bind="params"
        />
      </template>
      <template v-else-if="item.type === 'alert'">
        <Alert
          ref="itemRef"
          v-bind="params"
        />
      </template>
      <ComponentRender v-else
        ref="itemRef"
        :modelValue="model"
        @update:modelValue="onValueChanged"
        :params="params"
        :item="item"
        :name="name"
        :isLast="isLast"
        :disabled="disabled"
        :readonly="readonly"
      />
    </slot>
  </Field>
</template>

<script setup lang="ts">
import { computed, inject, onBeforeUnmount, onMounted, ref, type PropType, type Ref } from 'vue';
import type { IDynamicFormItem, IDynamicFormItemCallback, IDynamicFormMessageCenter, IDynamicFormObject, IDynamicFormOptions, IDynamicFormRef } from '.';
import Field from '../form/Field.vue';
import Stepper from '../form/Stepper.vue';
import NaPickerField, { type PickerFieldProps } from '../form/PickerField.vue';
import CheckBox from '../form/CheckBox.vue';
import Switch from '../form/Switch.vue';
import RadioValue from './wrappers/RadioValue.vue';
import PickerIdField from './wrappers/PickerIdField.vue';
import CheckBoxList from './wrappers/CheckBoxList.vue';
import CheckBoxToInt from './wrappers/CheckBoxToInt.vue';
import type { RadioValueProps } from './wrappers/RadioValue';
import type { PickerIdFieldProps } from './wrappers/PickerIdField';
import PickerCityField from './wrappers/PickerCityField.vue';
import PickerLonlat from './wrappers/PickerLonlat.vue';
import DateTimePickerField from '../form/DateTimePickerField.vue';
import TimePickerField from '../form/TimePickerField.vue';
import DatePickerField from '../form/DatePickerField.vue';
import UploaderField, { type UploaderFieldProps } from '../form/UploaderField.vue';
import RadioIdField from './wrappers/RadioIdField.vue';
import type { RadioIdFieldProps } from './wrappers/RadioIdField';
import Rate, { type RateProps } from '../form/Rate.vue';
import ComponentConfigs from '@/common/components/dynamicf/ComponentConfigs';
import ComponentRender from '@/common/components/dynamicf/ComponentRender.vue';
import type { Rules } from 'async-validator';
import PickerAddressField from './wrappers/PickerAddressField.vue';
import Button from '../basic/Button.vue';
import Alert from '../feedback/Alert.vue';
import Image from '../basic/Image.vue';
import CheckBoxTreeList, { type CheckBoxTreeListProps } from './wrappers/CheckBoxTreeList.vue';
import { useInjectFormContext, useInjectFormItemContext } from '../form/FormContext';

export interface FormCeilProps {
  model: unknown,
  rawModel: unknown,
  parent?: IDynamicFormItem,
  parentModel: unknown,
  onModelUpdate: (v: unknown) => void,
  item: IDynamicFormItem,
  name: string,
  disabled: boolean,
  additionalProps: Record<string, unknown>,
}

const filedInternalTypes = [
  'text',
  'textarea',
  'text-tag',
]

const props = defineProps({	
  item: {
    type: Object as PropType<IDynamicFormItem>,
    required: true,
  },
  name: {
    type: String,
    default: ''
  },
  parent: {
    type: Object as PropType<IDynamicFormItem>,
    default: null
  },
  disabled: {
    type: Boolean,
    default: false
  },
  model: {
    type: null
  },
  parentModel: {
    type: null
  },
  rawModel: {
    type: Object as PropType<Record<string, unknown>>,
    default: null
  },
  noLabel: {
    type: Boolean,
    default: false
  },
  formWrapperColDefault: {
    type: Object,
    default: null
  },
  formLabelColDefault: {
    type: Object,
    default: null
  },
  isFirst: {
    type: Boolean,
    default: false,
  },
  isLast: {
    type: Boolean,
    default: false,
  },
});

const emit = defineEmits([ 'update:model' ]);
const formItemRef = ref();
const finalOptions = inject<Ref<IDynamicFormOptions>>('finalOptions'); 
const globalParams = inject<Ref<IDynamicFormObject>>('globalParams');
const formRef = inject<IDynamicFormRef>('formRef');
const formName = inject('formName', '');
const context = useInjectFormItemContext();
const formContext = useInjectFormContext();
const disabled = computed(() => props.disabled || formContext?.disabled.value || context?.disabled.value);
const readonly = computed(() => formContext?.readonly.value || context?.readonly.value);

function evaluateCallback(val: unknown|IDynamicFormItemCallback<unknown>) {
  if (typeof val === 'object' && typeof (val as IDynamicFormItemCallback<unknown>).callback === 'function')
    return (val as IDynamicFormItemCallback<unknown>).callback(
      props.model, 
      props.rawModel, 
      props.parentModel, 
      {
        item: props.item, 
        parent: props.parent,
        form: formRef!,
        formGlobalParams: globalParams?.value || {},
        formRules: (finalOptions?.value.formRules ?? {}) as Record<string, Rules>,
        isFirst: props.isFirst,
        isLast: props.isLast,
      }
    );
  return val as unknown;
}
function evaluateCallbackObj(val: Record<string, unknown|IDynamicFormItemCallback<unknown>>) {
  const newObj = {} as Record<string, unknown>;
  for (const key in val) {
    if (Object.prototype.hasOwnProperty.call(val, key))
      newObj[key] = evaluateCallback(val[key]);
  }
  return newObj;
}

const extraDefine = computed(() => ComponentConfigs.find((item) => item.name === props.item.type))
const params = computed(() => {
  return {
    ...extraDefine.value?.props || {},
    ...evaluateCallbackObj(props.item.additionalProps as any)
  } as Record<string, unknown>
})
const label = computed(() => evaluateCallback(props.item.label) as string)
const data = computed<FormCeilProps>(() => {
  return {
    name: props.name,
    item: props.item,
    model: props.model,
    onModelUpdate: onValueChanged,
    rawModel: props.rawModel,
    parentModel: props.parentModel,
    parent: props.parent,
    rules: props.item.rules,
    disabled: props.disabled,
    additionalProps: props.item.additionalProps as Record<string, unknown>,
  }
})

const itemRef = ref();
const messageCenter = inject<IDynamicFormMessageCenter>('messageCenter');
 
function onValueChanged(v: any) {
  props.item.watch?.(props.model, v, props.rawModel, getComponentRef());
  emit('update:model', v);
}
function getComponentRef() {
  if (typeof itemRef.value.getItemRef === 'function')
    return itemRef.value.getItemRef();
  return itemRef.value;
}

onMounted(() => {
  props.item.mounted?.(props.model, props.rawModel, getComponentRef());
  messageCenter?.addWidgetRef(props.item.name, props.item.type ?? '', getComponentRef);
})
onBeforeUnmount(() => {
  props.item.beforeUnmount?.(props.model, props.rawModel, getComponentRef()); 
  messageCenter?.removeWidgetRef(props.item.name, props.item.type ?? '', getComponentRef);
})

defineOptions({
  options: {
    virtualHost: true,
  }
})
</script>