<script setup>
import { inject, onMounted, onBeforeUnmount, provide, reactive, ref, computed, toRefs } from "vue";
import AsyncValidator, {} from "async-validator";
import { formItemProps } from "./form-item";
import { useNamespace, useLocale, FormContextKey, FormItemContextKey, useId } from "@yh-ui/hooks";
import { useComponentTheme } from "@yh-ui/theme";
import { useConfig } from "@yh-ui/hooks";
import { get, set } from "@yh-ui/utils";
defineOptions({
  name: "YhFormItem"
});
const props = defineProps(formItemProps);
const ns = useNamespace("form-item");
const { t } = useLocale();
const formContext = inject(FormContextKey, null);
const { globalSize } = useConfig();
const { themeStyle } = useComponentTheme(
  "form-item",
  computed(
    () => props.themeOverrides || formContext?.themeOverrides
  )
);
const id = useId().value;
const labelId = `yh-label-${id}`;
const contentId = `yh-content-${id}`;
const errorId = `yh-error-${id}`;
const innerValidateStatus = ref("");
const innerValidateMessage = ref("");
const initialValue = ref(void 0);
const currentValidateStatus = computed(() => props.validateStatus || innerValidateStatus.value);
const currentValidateMessage = computed(() => props.error || innerValidateMessage.value);
const itemRules = computed(() => {
  const rules = [];
  if (props.rules) {
    const pRules = Array.isArray(props.rules) ? props.rules : [props.rules];
    rules.push(...pRules);
  }
  const formRules = formContext?.rules;
  if (formRules && props.prop) {
    const fRules = formRules[props.prop];
    if (fRules) {
      const fRulesArray = Array.isArray(fRules) ? fRules : [fRules];
      rules.push(...fRulesArray);
    }
  }
  return rules;
});
const isRequired = computed(() => {
  if (props.required) return true;
  return itemRules.value.some((rule) => !!rule.required);
});
const fieldValue = computed(() => {
  const model = formContext?.model;
  if (!model || !props.prop) return void 0;
  return get(model, props.prop);
});
const labelStyle = computed(() => {
  const width = props.labelWidth || formContext?.labelWidth;
  if (width) {
    return { width: typeof width === "number" ? `${width}px` : width };
  }
  return {};
});
const itemSize = computed(() => props.size || formContext?.size || globalSize.value || "default");
const isDisabled = computed(() => props.disabled || formContext?.disabled || false);
const validate = async (trigger = "", callback) => {
  const rules = trigger ? itemRules.value.filter(
    (rule) => !rule.trigger || (Array.isArray(rule.trigger) ? rule.trigger.includes(trigger) : rule.trigger === trigger)
  ) : itemRules.value;
  if (rules.length === 0) {
    callback?.(true);
    return true;
  }
  innerValidateStatus.value = "validating";
  const descriptor = { [props.prop]: rules };
  const validator = new AsyncValidator(descriptor);
  const model = { [props.prop]: fieldValue.value };
  return validator.validate(model, { firstFields: true }).then(() => {
    innerValidateStatus.value = "success";
    innerValidateMessage.value = "";
    callback?.(true);
    return true;
  }).catch((errorData) => {
    innerValidateStatus.value = "error";
    const { errors } = errorData || {};
    if (errors && errors.length > 0) {
      innerValidateMessage.value = errors[0].message || t("form.validationFailed");
    } else {
      innerValidateMessage.value = typeof errorData === "string" ? errorData : t("form.validationFailed");
    }
    callback?.(false);
    return Promise.reject(errorData);
  });
};
const resetField = () => {
  innerValidateStatus.value = "";
  innerValidateMessage.value = "";
  const model = formContext?.model;
  if (model && props.prop && initialValue.value !== void 0) {
    set(model, props.prop, initialValue.value);
  }
};
const clearValidate = () => {
  innerValidateStatus.value = "";
  innerValidateMessage.value = "";
};
const context = reactive({
  ...toRefs(props),
  validate,
  resetField,
  clearValidate,
  validateStatus: currentValidateStatus,
  validateMessage: currentValidateMessage,
  label: computed(() => props.label),
  disabled: isDisabled,
  size: itemSize,
  errorId,
  inputId: contentId
});
provide(FormItemContextKey, context);
onMounted(() => {
  if (props.prop) {
    formContext?.addField(context);
    if (fieldValue.value !== void 0) {
      try {
        initialValue.value = JSON.parse(JSON.stringify(fieldValue.value));
      } catch {
        initialValue.value = fieldValue.value;
      }
    }
  }
});
onBeforeUnmount(() => {
  if (props.prop) {
    formContext?.removeField(context);
  }
});
defineExpose({
  validate,
  resetField,
  clearValidate,
  validateStatus: currentValidateStatus,
  validateMessage: currentValidateMessage,
  size: itemSize
});
</script>

<template>
  <div
    :class="[ns.b(), ns.m(itemSize), ns.is('error', currentValidateStatus === 'error'), ns.is('validating', currentValidateStatus === 'validating'), ns.is('success', currentValidateStatus === 'success'), ns.is('required', isRequired), ns.is('disabled', isDisabled)]"
    :style="themeStyle"
    :data-prop="prop"
  >
    <!-- 标签 -->
    <label
      v-if="label || $slots.label"
      :id="labelId"
      :for="contentId"
      :class="ns.e('label')"
      :style="labelStyle"
    >
      <slot name="label">{{ label }}{{ formContext?.labelSuffix }}</slot>
    </label>

    <div :class="ns.e('content')" :id="contentId">
      <slot />

      <!-- 状态图标 -->
      <div
        v-if="formContext?.statusIcon && currentValidateStatus"
        :class="[ns.e('status-icon'), ns.is(currentValidateStatus)]"
      >
        <svg
          v-if="currentValidateStatus === 'success'"
          viewBox="0 0 1024 1024"
          width="16"
          height="16"
        >
          <path
            fill="currentColor"
            d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm-55.808 536.32l-114.944-114.88a32 32 0 1 0-45.248 45.248l137.536 137.472a32 32 0 0 0 45.248 0l310.4-310.272a32 32 0 1 0-45.248-45.248L456.192 600.32z"
          />
        </svg>
        <svg
          v-else-if="currentValidateStatus === 'error'"
          viewBox="0 0 1024 1024"
          width="16"
          height="16"
        >
          <path
            fill="currentColor"
            d="M512 64a448 448 0 1 1 0 896 448 448 0 0 1 0-896zm0 393.664L407.936 353.6a38.4 38.4 0 1 0-54.336 54.336L457.664 512 353.6 616.064a38.4 38.4 0 1 0 54.336 54.336L512 566.336 616.064 670.4a38.4 38.4 0 1 0 54.336-54.336L566.336 512 670.4 407.936a38.4 38.4 0 1 0-54.336-54.336L512 457.664z"
          />
        </svg>
      </div>

      <!-- 校验提示 - 确保即使没有点击提交，通过触发 blur/change 也能即时显示 -->
      <Transition name="yh-fade">
        <div
          v-if="currentValidateStatus === 'error' && showMessage && (formContext?.showMessage ?? true)"
          :id="errorId"
          :class="[ns.e('error'), ns.is(errorPosition)]"
          role="alert"
          aria-live="polite"
        >
          {{ currentValidateMessage }}
        </div>
      </Transition>
    </div>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
:root {
  --yh-form-item-height: 32px;
  --yh-form-item-margin-bottom: 22px;
  --yh-form-label-font-size: 14px;
}

.yh-form--inline {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0 18px;
}
.yh-form--inline .yh-form-item {
  display: inline-flex;
  align-items: flex-start;
  margin-right: 0;
  margin-bottom: 18px;
  vertical-align: top;
}
.yh-form--inline .yh-form-item__label {
  height: var(--yh-form-item-height);
  line-height: var(--yh-form-item-height);
}
.yh-form--inline .yh-form-item__content {
  line-height: var(--yh-form-item-height);
}
.yh-form--grid {
  display: flex;
  flex-wrap: wrap;
  margin-left: -12px;
  margin-right: -12px;
}
.yh-form--grid .yh-form-col {
  padding-left: 12px;
  padding-right: 12px;
  box-sizing: border-box;
  flex-shrink: 0;
  width: 100%;
}
.yh-form--grid .yh-form-col--1 {
  flex: 0 0 calc(4.1666666667% * 1);
  max-width: calc(4.1666666667% * 1);
}
.yh-form--grid .yh-form-col--2 {
  flex: 0 0 calc(4.1666666667% * 2);
  max-width: calc(4.1666666667% * 2);
}
.yh-form--grid .yh-form-col--3 {
  flex: 0 0 calc(4.1666666667% * 3);
  max-width: calc(4.1666666667% * 3);
}
.yh-form--grid .yh-form-col--4 {
  flex: 0 0 calc(4.1666666667% * 4);
  max-width: calc(4.1666666667% * 4);
}
.yh-form--grid .yh-form-col--5 {
  flex: 0 0 calc(4.1666666667% * 5);
  max-width: calc(4.1666666667% * 5);
}
.yh-form--grid .yh-form-col--6 {
  flex: 0 0 calc(4.1666666667% * 6);
  max-width: calc(4.1666666667% * 6);
}
.yh-form--grid .yh-form-col--7 {
  flex: 0 0 calc(4.1666666667% * 7);
  max-width: calc(4.1666666667% * 7);
}
.yh-form--grid .yh-form-col--8 {
  flex: 0 0 calc(4.1666666667% * 8);
  max-width: calc(4.1666666667% * 8);
}
.yh-form--grid .yh-form-col--9 {
  flex: 0 0 calc(4.1666666667% * 9);
  max-width: calc(4.1666666667% * 9);
}
.yh-form--grid .yh-form-col--10 {
  flex: 0 0 calc(4.1666666667% * 10);
  max-width: calc(4.1666666667% * 10);
}
.yh-form--grid .yh-form-col--11 {
  flex: 0 0 calc(4.1666666667% * 11);
  max-width: calc(4.1666666667% * 11);
}
.yh-form--grid .yh-form-col--12 {
  flex: 0 0 calc(4.1666666667% * 12);
  max-width: calc(4.1666666667% * 12);
}
.yh-form--grid .yh-form-col--13 {
  flex: 0 0 calc(4.1666666667% * 13);
  max-width: calc(4.1666666667% * 13);
}
.yh-form--grid .yh-form-col--14 {
  flex: 0 0 calc(4.1666666667% * 14);
  max-width: calc(4.1666666667% * 14);
}
.yh-form--grid .yh-form-col--15 {
  flex: 0 0 calc(4.1666666667% * 15);
  max-width: calc(4.1666666667% * 15);
}
.yh-form--grid .yh-form-col--16 {
  flex: 0 0 calc(4.1666666667% * 16);
  max-width: calc(4.1666666667% * 16);
}
.yh-form--grid .yh-form-col--17 {
  flex: 0 0 calc(4.1666666667% * 17);
  max-width: calc(4.1666666667% * 17);
}
.yh-form--grid .yh-form-col--18 {
  flex: 0 0 calc(4.1666666667% * 18);
  max-width: calc(4.1666666667% * 18);
}
.yh-form--grid .yh-form-col--19 {
  flex: 0 0 calc(4.1666666667% * 19);
  max-width: calc(4.1666666667% * 19);
}
.yh-form--grid .yh-form-col--20 {
  flex: 0 0 calc(4.1666666667% * 20);
  max-width: calc(4.1666666667% * 20);
}
.yh-form--grid .yh-form-col--21 {
  flex: 0 0 calc(4.1666666667% * 21);
  max-width: calc(4.1666666667% * 21);
}
.yh-form--grid .yh-form-col--22 {
  flex: 0 0 calc(4.1666666667% * 22);
  max-width: calc(4.1666666667% * 22);
}
.yh-form--grid .yh-form-col--23 {
  flex: 0 0 calc(4.1666666667% * 23);
  max-width: calc(4.1666666667% * 23);
}
.yh-form--grid .yh-form-col--24 {
  flex: 0 0 calc(4.1666666667% * 24);
  max-width: calc(4.1666666667% * 24);
}
.yh-form--large {
  --yh-form-item-height: 40px;
  --yh-form-label-font-size: 14px;
  --yh-component-size-default: 40px;
}

.yh-form--default {
  --yh-form-item-height: 32px;
  --yh-form-label-font-size: 14px;
  --yh-component-size-default: 32px;
}

.yh-form--small {
  --yh-form-item-height: 24px;
  --yh-form-label-font-size: 12px;
  --yh-component-size-default: 24px;
}

.yh-form-item {
  display: flex;
  align-items: flex-start;
  margin-bottom: var(--yh-form-item-margin-bottom);
  position: relative;
}
.yh-form--label-left .yh-form-item__label {
  justify-content: flex-start;
}
.yh-form--label-top .yh-form-item {
  flex-direction: column;
}
.yh-form--label-top .yh-form-item__label {
  justify-content: flex-start;
  margin-bottom: 8px;
  width: 100% !important;
  text-align: left;
  height: auto;
  line-height: 1.5;
}
.yh-form--label-top .yh-form-item__content {
  width: 100%;
  align-self: stretch;
}
.yh-form-item__label {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  padding-right: 12px;
  font-size: var(--yh-form-label-font-size);
  color: var(--yh-text-color-regular, #606266);
  height: var(--yh-form-item-height);
  line-height: var(--yh-form-item-height);
  box-sizing: border-box;
  flex-shrink: 0;
  white-space: nowrap;
}
.yh-form-item__content {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  flex: 1;
  position: relative;
  font-size: 14px;
  min-width: 0;
  min-height: var(--yh-form-item-height);
  line-height: var(--yh-form-item-height);
}
.yh-form-item__content > .yh-input,
.yh-form-item__content > .yh-select,
.yh-form-item__content > .yh-input-number,
.yh-form-item__content > .yh-date-picker {
  width: 100%;
}
.yh-form-item__error {
  color: var(--yh-color-danger, #f56c6c);
  font-size: 12px;
  line-height: 1;
  padding-top: 4px;
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  z-index: 1;
}
.yh-form-item__error.is-center {
  text-align: center;
}
.yh-form-item__error.is-right {
  text-align: right;
}
.yh-form-item__status-icon {
  position: absolute;
  right: 10px;
  top: 0;
  height: var(--yh-form-item-height);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  pointer-events: none;
}
.yh-form-item__status-icon svg {
  width: 14px;
  height: 14px;
}
.yh-form-item__status-icon.is-success {
  color: var(--yh-color-success, #67c23a);
}
.yh-form-item__status-icon.is-error {
  color: var(--yh-color-danger, #f56c6c);
}
.yh-form-item__status-icon.is-validating {
  color: var(--yh-color-primary, #409eff);
}
.yh-form-item__status-icon.is-validating svg {
  animation: yh-rotating 2s linear infinite;
}
.yh-form-item.is-required:not(.is-no-asterisk) > .yh-form-item__label::before {
  content: "*";
  color: var(--yh-color-danger, #f56c6c);
  margin-right: 4px;
}
.yh-form-item.is-error .yh-input__inner,
.yh-form-item.is-error .yh-input-number__wrapper,
.yh-form-item.is-error .yh-select__wrapper,
.yh-form-item.is-error .yh-textarea__inner {
  border-color: var(--yh-color-danger, #f56c6c) !important;
}
.yh-form-item.is-error .yh-input__inner:hover, .yh-form-item.is-error .yh-input__inner:focus,
.yh-form-item.is-error .yh-input-number__wrapper:hover,
.yh-form-item.is-error .yh-input-number__wrapper:focus,
.yh-form-item.is-error .yh-select__wrapper:hover,
.yh-form-item.is-error .yh-select__wrapper:focus,
.yh-form-item.is-error .yh-textarea__inner:hover,
.yh-form-item.is-error .yh-textarea__inner:focus {
  border-color: var(--yh-color-danger, #f56c6c) !important;
}
.yh-form-item.is-success .yh-input__inner,
.yh-form-item.is-success .yh-input-number__wrapper,
.yh-form-item.is-success .yh-select__wrapper,
.yh-form-item.is-success .yh-textarea__inner {
  border-color: var(--yh-color-success, #67c23a) !important;
}
.yh-form-item--large {
  --yh-form-item-height: 40px;
  --yh-component-size-default: 40px;
}

.yh-form-item--default {
  --yh-form-item-height: 32px;
  --yh-component-size-default: 32px;
}

.yh-form-item--small {
  --yh-form-item-height: 24px;
  --yh-form-label-font-size: 12px;
  --yh-component-size-default: 24px;
}

@keyframes yh-rotating {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}
.yh-fade-enter-active,
.yh-fade-leave-active {
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.yh-fade-enter-from,
.yh-fade-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}
</style>
