import { type InjectionKey, inject, provide, type Ref, computed, ref, watch, type ComputedRef } from "vue";
import type { FieldProps } from "./Field.vue";
import type { RuleItem } from "async-validator";
import { useCellContext } from "../basic/CellContext";

/**
 * 校验触发时机
 * - blur: 失去焦点时触发校验
 * - change: 值改变时触发校验
 * - submit: 提交表单时触发校验
 */
export type ValidTrigger = "blur" | "change" | "submit";

/**
 * 表单项上下文
 */
export type FormItemContext = {
  errorState: ComputedRef<boolean>,
  getFieldName: () => string,
  /**
   * 触发表单条目获得焦点事件
   */
  onFieldFocus: () => void;
  /**
   * 触发表单条目失去焦点事件
   */
  onFieldBlur: () => void;
  /**
   * 触发表单条目值改变事件
   * @param newValue 新值
   */
  onFieldChange: (newValue: unknown) => void;
  /**
   * 清除表单条目校验状态
   */
  clearValidate: () => void;
  /**
   * 设置表单条目点击事件监听器，设置后表单项允许点击，点击后会触发点击事件。
   * @param listener 点击事件监听器
   */
  setOnClickListener: (listener: (() => void)|undefined) => void;
  /**
   * 获取表单组件中的当前值
   * @returns 表单组件中的当前值
   */
  getFormModelValue(): any;
  /**
   * 表单项是否禁用的状态
   */
  disabled: Ref<boolean|undefined>,
  /**
   * 表单项是否只读的状态
   */
  readonly: Ref<boolean|undefined>,
};
export type FormItemInternalContext = {
  /**
   * 获取表单组件的实例引用，如果没有子组件，则返回 Field 自身引用。
   * 
   * 本函数专用于动态表单，直接使用的情况可以在模板中绑定ref获取实例引用。
   * 
   * 只有 Field 组件设置了 requireChildRef 回调才能返回子组件实例引用，否则只会返回 Field 组件实例引用。
   * @returns 
   */
  getExpectedRef: () => any,
  getItemRules: () => RuleItem[],
  getFieldName: () => string,
  getValidateTrigger: () => ValidTrigger;
  getUniqueId: () => string,
  setErrorState: (errorMessage: string|null) => void;
  setBlurState(): void;
};
export type FormContext = {
  //由表单项组件调用
  onFieldFocus: (item: FormItemInternalContext) => void;
  onFieldBlur: (item: FormItemInternalContext) => void;
  onFieldChange: (item: FormItemInternalContext, newValue: unknown) => void;
  clearValidate: (item: FormItemInternalContext) => void;
  addFormItemField: (item: FormItemInternalContext) => number;
  removeFormItemField: (item: FormItemInternalContext) => void;
  //form props
  validateTrigger: Ref<ValidTrigger|undefined>;
  addRequireMark: Ref<boolean|undefined>;
  colon: Ref<boolean|undefined>;
  labelWidth: Ref<string|number|undefined>;
  labelAlign: Ref<"left"|"center"|"right"|undefined>;
  labelPosition: Ref<'top'|'left'|undefined>;
  labelFlex: Ref<number|undefined>;
  inputFlex: Ref<number|undefined>;
  showLabel: Ref<boolean|undefined>;
  name: Ref<string|undefined>;
  fieldProps: Ref<FieldProps|undefined>,
  disabled: Ref<boolean|undefined>,
  readonly: Ref<boolean|undefined>,
  getItemValue: (item: FormItemInternalContext) => unknown;
  getItemRequieed: (item: FormItemInternalContext) => boolean;
};

/**
 * 用于Props默认值回调中获取表单上下文
 * @returns FormContext
 */
export function propGetFormContext() {
  return inject<FormContext>('formContext', null as any);
}

export const FormItemContextContextKey: InjectionKey<FormItemContext> = Symbol('FormItemContext');

/**
 * 用于注入表单项上下文
 * @returns FormItemContext
 */
export function useInjectFormItemContext() : FormItemContext {
  const context = inject<FormItemContext>(FormItemContextContextKey, null as any);
  provide(FormItemContextContextKey, null as any as FormItemContext);
  return context as FormItemContext;
}
/**
 * 用于注入表单上下文
 * @returns FormContext
 */
export function useInjectFormContext() : FormContext {
  return inject<FormContext>('formContext', null as any);
}

/**
 * 用于注入表单项子组件值，用于实现表单项值的双向绑定。
 * 
 * 组件可以通过返回的 `value` 属性获取当前值，通过 `updateValue` 方法更新值，
 * 即使外部未绑定 `modelValue` 属性，也可以正常工作。
 * 
 * ```ts
 * const {
    value,
    updateValue,
  } = useFieldChildValueInjector(
    toRef(props, 'modelValue'), 
    (v) => emit('update:modelValue', v)
  );
 * ```
 * @param propsModelValue 组件外部传入的modelValue
 * @param emit 组件外部的emit
 * @param secondParentContext 二级父组件上下文，用于更新二级父组件的值。
 * @param fieldClick 表单项点击事件监听器，设置后表单项允许点击，点击后会触发点击事件。
 * @param initialValue 初始值
 * @returns 
 */
export function useFieldChildValueInjector<T>(
  propsModelValue: Ref<T>, 
  emit: (v: T) => void, 
  secondParentContext?: {
    getValue: () => T,
    updateValue: (v: T) => void,
  },
  fieldClick?: () => void,
  initialValue?: T,
) {
  const cellContext = useCellContext();
  const context = useInjectFormItemContext();
  const formContext = useInjectFormContext();
  const shadowRefValue = ref(propsModelValue.value ?? context.getFormModelValue() ?? initialValue) as Ref<T>;

  const value = computed(() => {
    if (secondParentContext)
      shadowRefValue.value = secondParentContext.getValue();
    return shadowRefValue.value
  });

  watch(() => propsModelValue.value, (v) => {
    shadowRefValue.value = v;    
  })

  /**
   * 更新表单项值
   * @param newValue 新值
   */
  function updateValue(newValue: T) {
    if (secondParentContext)
      secondParentContext.updateValue(newValue);
    else
      emit(newValue);
    shadowRefValue.value = newValue;
    context?.onFieldChange(newValue);
  }

  if (fieldClick) {
    if (context)
      context.setOnClickListener(fieldClick);
    else if (cellContext)
      cellContext.setOnClickListener(fieldClick);
  }

  const disabled = computed(() => formContext.disabled.value || context.disabled.value);
  const readonly = computed(() => formContext.readonly.value || context.readonly.value);

  return {
    /**
     * 临时值
     */
    value: value,
    updateValue,
    /**
     * 表单项上下文
     */
    context,
    /**
     * 表单上下文
     */
    formContext,
    /**
     * 指示顶层由表单和表单项设置的禁用状态
     */
    disabled,
    /**
     * 指示顶层由表单和表单项设置的只读状态
     */
    readonly,
  }
}