<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { FEEDBACK_TYPE_DICT_CODE } from '@/constants/dict';
import { fetchCreateFeedback, fetchUpdateFeedback } from '@/service/api';
import { useForm, useFormRules } from '@/hooks/common/form';
import BusinessAttachmentUploader from '@/components/custom/business-attachment-uploader.vue';
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
import BusinessFormSection from '@/components/custom/business-form-section.vue';
import BusinessRichTextEditor from '@/components/custom/business-rich-text-editor.vue';
import DictSelect from '@/components/custom/dict-select.vue';

defineOptions({ name: 'FeedbackOperateDialog' });

type OperateMode = 'create' | 'edit';

interface Props {
  mode: OperateMode;
  rowData?: Api.Feedback.FeedbackItem | null;
}

const props = withDefaults(defineProps<Props>(), {
  rowData: null
});

const emit = defineEmits<{
  submitted: [];
}>();

const visible = defineModel<boolean>('visible', { default: false });

const { formRef, validate } = useForm();
const { createRequiredRule } = useFormRules();

const submitting = ref(false);
const attachmentUploaderRef = ref<InstanceType<typeof BusinessAttachmentUploader> | null>(null);
const richTextEditorRef = ref<InstanceType<typeof BusinessRichTextEditor> | null>(null);

interface FormModel {
  /** DictSelect 选中后为字符串；提交时 Number() 转字典 Integer（非 ID，允许转 number） */
  type: string | number | null;
  title: string;
  content: string;
  contact: string;
  attachments: Api.Project.AttachmentItem[];
}

const model = reactive<FormModel>({
  type: null,
  title: '',
  content: '',
  contact: '',
  attachments: []
});

const dialogTitle = computed(() => (props.mode === 'create' ? '提交意见反馈' : '编辑意见反馈'));

/** 富文本是否为空：去标签去 &nbsp; 后无文本，且无图片 */
function isEmptyRichText(html: string | null | undefined) {
  if (!html) {
    return true;
  }
  const text = html
    .replace(/<[^>]+>/g, '')
    .replace(/&nbsp;/g, '')
    .trim();
  if (text) {
    return false;
  }
  return !/<img\b/i.test(html);
}

const rules = computed(
  () =>
    ({
      type: [createRequiredRule('请选择反馈分类')],
      title: [createRequiredRule('请输入标题')],
      content: [
        {
          validator: (_rule, _value, callback) => {
            if (isEmptyRichText(model.content)) {
              callback(new Error('请输入详细描述'));
              return;
            }
            callback();
          },
          trigger: 'blur'
        }
      ]
    }) satisfies Record<string, App.Global.FormRule[]>
);

function applyRowDataToModel() {
  const row = props.rowData;
  if (props.mode === 'edit' && row) {
    // 字典选项 value 为字符串，回显需转字符串才能与下拉项严格匹配（否则显示原始数字）
    model.type = row.type === null || row.type === undefined ? null : String(row.type);
    model.title = row.title ?? '';
    model.content = row.content ?? '';
    model.contact = row.contact ?? '';
    model.attachments = row.attachments ? [...row.attachments] : [];
    return;
  }
  model.type = null;
  model.title = '';
  model.content = '';
  model.contact = '';
  model.attachments = [];
}

async function handleConfirm() {
  await validate();

  if (attachmentUploaderRef.value?.hasUploading) {
    window.$message?.warning('附件还在上传中，请稍候');
    return;
  }

  submitting.value = true;

  const basePayload: Api.Feedback.FeedbackSubmitParams = {
    type: Number(model.type),
    title: model.title.trim(),
    content: model.content,
    contact: model.contact.trim() || undefined,
    attachments: [...model.attachments]
  };

  const { error } =
    props.mode === 'edit' && props.rowData
      ? await fetchUpdateFeedback({ id: props.rowData.id, ...basePayload })
      : await fetchCreateFeedback(basePayload);

  submitting.value = false;

  if (error) {
    return;
  }

  // 成功后先 commit，置 committed=true，避免 destroy-on-close 触发 rollback 误删已存文件/图片
  await Promise.all([attachmentUploaderRef.value?.commit(), richTextEditorRef.value?.commit()]);
  window.$message?.success(props.mode === 'edit' ? '保存成功' : '提交成功');
  visible.value = false;
  emit('submitted');
}

async function handleCancel() {
  await Promise.all([attachmentUploaderRef.value?.rollback(), richTextEditorRef.value?.rollback()]);
}

watch(visible, async value => {
  if (!value) {
    return;
  }
  applyRowDataToModel();
  await nextTick();
  // 让附件 / 富文本组件把当前 model 视作 original，必须在 model 填充之后
  attachmentUploaderRef.value?.initSession();
  richTextEditorRef.value?.initSession();
  formRef.value?.clearValidate();
});
</script>

<template>
  <BusinessFormDialog
    v-model="visible"
    :title="dialogTitle"
    width="980px"
    max-body-height="76vh"
    :confirm-loading="submitting"
    @confirm="handleConfirm"
    @cancel="handleCancel"
  >
    <ElForm
      ref="formRef"
      :model="model"
      :rules="rules"
      label-position="top"
      :validate-on-rule-change="false"
      class="feedback-operate-dialog__form"
    >
      <div class="feedback-operate-dialog__grid">
        <div class="feedback-operate-dialog__col-left">
          <BusinessFormSection title="反馈信息">
            <ElFormItem label="反馈分类" prop="type">
              <DictSelect v-model="model.type" :dict-code="FEEDBACK_TYPE_DICT_CODE" placeholder="请选择反馈分类" />
            </ElFormItem>

            <ElFormItem label="标题" prop="title">
              <ElInput v-model="model.title" maxlength="100" show-word-limit placeholder="请输入标题" />
            </ElFormItem>

            <ElFormItem label="联系方式">
              <ElInput v-model="model.contact" maxlength="100" placeholder="选填，方便我们联系你" />
            </ElFormItem>
          </BusinessFormSection>

          <BusinessFormSection title="附件">
            <ElFormItem class="feedback-operate-dialog__attachment-item">
              <BusinessAttachmentUploader
                ref="attachmentUploaderRef"
                v-model="model.attachments"
                directory="feedback"
              />
            </ElFormItem>
          </BusinessFormSection>
        </div>

        <div class="feedback-operate-dialog__col-right">
          <BusinessFormSection title="详细描述">
            <ElFormItem prop="content" class="feedback-operate-dialog__desc-item">
              <BusinessRichTextEditor
                ref="richTextEditorRef"
                v-model="model.content"
                :height="380"
                upload-directory="feedback"
                placeholder="请输入详细描述"
              />
            </ElFormItem>
          </BusinessFormSection>
        </div>
      </div>
    </ElForm>
  </BusinessFormDialog>
</template>

<style scoped>
.feedback-operate-dialog__grid {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 24px;
  align-items: start;
}

.feedback-operate-dialog__col-left,
.feedback-operate-dialog__col-right {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.feedback-operate-dialog__desc-item,
.feedback-operate-dialog__attachment-item {
  margin-bottom: 0;
}

@media (width <= 1024px) {
  .feedback-operate-dialog__grid {
    grid-template-columns: 1fr;
  }
}
</style>
