<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { useResizeObserver } from '@vueuse/core';
import dayjs from 'dayjs';
import { RDMS_REQ_PRIORITY_DICT_CODE, RDMS_TASK_ITEM_TYPE_DICT_CODE } from '@/constants/dict';
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 BusinessUserSelect from '@/components/custom/business-user-select.vue';
import DictSelect from '@/components/custom/dict-select.vue';
import { SHOW_TASK_PARENT_FIELD } from '../shared';
import IconMdiAutoFix from '~icons/mdi/auto-fix';

defineOptions({ name: 'ProjectExecutionTaskOperateDialog' });

type OperateMode = 'create' | 'edit';

export interface PlannedEndShortcutOffset {
  text: string;
  days?: number;
  months?: number;
  years?: number;
}

interface Props {
  mode: OperateMode;
  rowData: Api.Project.ProjectTask | null;
  executionData?: Api.Project.ProjectExecution | null;
  /** 创建模式下的父任务预填；编辑/查看模式忽略 */
  defaultParentTaskId?: string | null;
  userOptions: Api.SystemManage.UserSimple[];
  taskOptions: Api.Project.ProjectTask[];
  plannedEndShortcuts?: PlannedEndShortcutOffset[];
  /** 编辑态是否可内联管理协办人（对应 canManageTaskAssignee）；false 时只读回显 */
  canManageAssignee?: boolean;
}

interface Emits {
  (e: 'submit', payload: Api.Project.SaveProjectTaskParams): void;
}

const props = withDefaults(defineProps<Props>(), {
  executionData: null,
  defaultParentTaskId: null,
  canManageAssignee: false,
  plannedEndShortcuts: () => [
    { text: '三天', days: 3 },
    { text: '一星期', days: 7 },
    { text: '两星期', days: 14 },
    { text: '一个月', months: 1 },
    { text: '三个月', months: 3 }
  ]
});
const emit = defineEmits<Emits>();

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

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

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

interface FormModel {
  parentTaskId: string | null;
  taskTitle: string;
  type: string;
  ownerId: string | null;
  plannedStartDate: string | null;
  plannedEndDate: string | null;
  priority: string;
  taskDesc: string | null;
  assigneeUserIds: string[];
  attachments: Api.Project.AttachmentItem[];
}

const model = reactive<FormModel>({
  parentTaskId: null,
  taskTitle: '',
  type: '',
  ownerId: null,
  plannedStartDate: null,
  plannedEndDate: null,
  priority: '3',
  taskDesc: null,
  assigneeUserIds: [],
  attachments: []
});

const dialogTitle = computed(() => {
  if (props.mode === 'create') {
    return '新建任务';
  }
  return props.rowData?.taskTitle ? `编辑任务：${props.rowData.taskTitle}` : '编辑任务';
});

const canFillFromExecution = computed(() => props.mode === 'create' && Boolean(props.executionData));

const selectableParentTasks = computed(() => props.taskOptions.filter(item => item.id !== props.rowData?.id));

/** 编辑态 + 任务已开始（statusCode 离开 pending）→ 负责人锁定不可切换 */
const ownerLocked = computed(() => {
  const status = props.rowData?.statusCode;
  return props.mode === 'edit' && Boolean(status) && status !== 'pending';
});

/**
 * 负责人下拉选项 = 执行协办人池 ∪ 当前任务负责人（兜底）。
 * owner 可能已不在执行协办人池里，用任务自带 ownerNickname 兜底回显，避免锁定态显示成裸 userId。
 */
const ownerSelectOptions = computed<Api.SystemManage.UserSimple[]>(() => {
  const currentOwnerId = props.mode === 'create' ? model.ownerId : (props.rowData?.ownerId ?? null);
  if (!currentOwnerId || props.userOptions.some(item => item.id === currentOwnerId)) {
    return props.userOptions;
  }

  const fallbackNickname =
    props.mode === 'create'
      ? props.executionData?.ownerNickname || currentOwnerId
      : props.rowData?.ownerNickname || currentOwnerId;

  return [...props.userOptions, { id: currentOwnerId, nickname: fallbackNickname }];
});

/** 编辑态无协办人管理权限时只读回显（create 恒可交互） */
const assigneeSelectDisabled = computed(() => props.mode !== 'create' && !props.canManageAssignee);

const assigneePlaceholder = computed(() => (assigneeSelectDisabled.value ? '暂无协办人' : '请选择协办人'));

/**
 * 候选项 = 执行协办人池 ∪ 当前任务已有协办人中已不在池里的成员 ∪ 当前任务负责人。
 * 已有协办人用任务自带 nickname 兜底回显，避免协办人被移出执行后在编辑态显示成裸 userId；
 * 负责人并入是为了让 owner 在协办人下拉里以「负责人」标记展示（不可移除），用 ownerNickname 兜底。
 */
const assigneeSelectOptions = computed(() => {
  const options = props.userOptions.map(item => ({ id: item.id, nickname: item.nickname }));
  const known = new Set(options.map(item => item.id));

  if (props.mode === 'create') {
    const ownerId = model.ownerId;
    if (ownerId && !known.has(ownerId)) {
      options.push({ id: ownerId, nickname: props.executionData?.ownerNickname || ownerId });
      known.add(ownerId);
    }
  } else if (props.rowData) {
    props.rowData.assignees?.forEach(assignee => {
      if (assignee.userId && !known.has(assignee.userId)) {
        options.push({ id: assignee.userId, nickname: assignee.nickname || assignee.userId });
        known.add(assignee.userId);
      }
    });

    const ownerId = props.rowData.ownerId;
    if (ownerId && !known.has(ownerId)) {
      options.push({ id: ownerId, nickname: props.rowData.ownerNickname || ownerId });
      known.add(ownerId);
    }
  }

  return options;
});

/** 左栏容器 ref：用其高度动态驱动右侧富文本，让两栏视觉等高 */
const leftColRef = ref<HTMLElement>();
const editorHeight = ref<string>('45vh');

// 右栏：富文本 + 附件区。预留附件区粗略高度（标题 + 按钮行 + 列表起始空间）
const ATTACHMENT_SECTION_RESERVE_PX = 140;

useResizeObserver(leftColRef, entries => {
  const h = entries[0]?.contentRect.height;
  if (h && h > 120) {
    editorHeight.value = `${Math.max(h - 60 - ATTACHMENT_SECTION_RESERVE_PX, 200)}px`;
  }
});

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(
  () =>
    ({
      taskTitle: [createRequiredRule('请输入任务名称')],
      type: [createRequiredRule('请选择任务类型')],
      priority: [createRequiredRule('请选择优先级')],
      ownerId: model.parentTaskId ? [] : [createRequiredRule('请选择负责人')],
      plannedStartDate: [createRequiredRule('请选择计划开始日期')],
      plannedEndDate: [
        createRequiredRule('请选择计划结束日期'),
        {
          validator: (_rule, value: string | null, callback) => {
            if (!isPlannedDateRangeValid(model.plannedStartDate, value)) {
              callback(new Error('计划结束日期不能早于计划开始日期'));
              return;
            }

            callback();
          },
          trigger: 'change'
        }
      ]
    }) satisfies Record<string, App.Global.FormRule[]>
);

function parsePlannedDate(value: string | null) {
  if (!value) {
    return null;
  }

  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? null : date;
}

function isPlannedDateRangeValid(startDate: string | null, endDate: string | null) {
  if (!startDate || !endDate) {
    return true;
  }

  return !dayjs(endDate).isBefore(dayjs(startDate), 'day');
}

function applyOffset(date: Date, offset: PlannedEndShortcutOffset) {
  if (offset.days) {
    date.setDate(date.getDate() + offset.days);
  }
  if (offset.months) {
    date.setMonth(date.getMonth() + offset.months);
  }
  if (offset.years) {
    date.setFullYear(date.getFullYear() + offset.years);
  }
}

const plannedEndDateShortcuts = computed(() =>
  props.plannedEndShortcuts.map(offset => ({
    text: offset.text,
    value: () => {
      let startDate = parsePlannedDate(model.plannedStartDate);

      if (!startDate) {
        startDate = new Date();
        model.plannedStartDate = dayjs(startDate).format('YYYY-MM-DD');
        window.$message?.info('未选择计划开始日期，已按今日为基准计算');
        nextTick(() => formRef.value?.clearValidate('plannedStartDate'));
      }

      const endDate = new Date(startDate.getTime());
      applyOffset(endDate, offset);
      return endDate;
    }
  }))
);

/**
 * 提交用：过滤掉 ownerId（后端契约：任务协办人不能等于 owner）+ 去重
 */
function normalizeAssigneeIds(ids: string[]) {
  return Array.from(new Set(ids.filter(id => id && id !== model.ownerId)));
}

/**
 * 自动加进 model.assigneeUserIds 的 owner，跟踪它以便 owner 切换时正确移除旧值。
 * 防止用户先选了某 A 作为 owner（自动加入），再换成 B 作为 owner 时，A 仍残留在协办人里。
 */
const autoOwnerAssigneeId = ref<string | null>(null);

/**
 * UI 层把 owner 也并进 model.assigneeUserIds，让协办人 select 视觉上显示 owner（带「负责人」标记、不可移除），
 * 让用户感知"负责人也在团队里"。create / edit 同口径；提交时由 normalizeAssigneeIds 过滤掉 owner。
 * 切换 owner 时按 previousOwnerId（上一任已并入的 owner）移除旧值，避免旧负责人残留在协办人里。
 */
function syncOwnerAssignee(ownerId: string | null, previousOwnerId: string | null = autoOwnerAssigneeId.value) {
  const current = Array.from(new Set((model.assigneeUserIds ?? []).filter(Boolean)));
  const withoutPrevious = previousOwnerId ? current.filter(userId => userId !== previousOwnerId) : current;
  model.assigneeUserIds = ownerId ? Array.from(new Set([...withoutPrevious, ownerId])) : withoutPrevious;
  autoOwnerAssigneeId.value = ownerId;
}

async function handleConfirm() {
  await validate();

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

  const payload: Api.Project.SaveProjectTaskParams = {
    parentTaskId: model.parentTaskId || null,
    taskTitle: model.taskTitle.trim(),
    type: model.type,
    ownerId: model.ownerId || null,
    plannedStartDate: model.plannedStartDate,
    plannedEndDate: model.plannedEndDate,
    priority: model.priority,
    taskDesc: isEmptyRichText(model.taskDesc) ? null : (model.taskDesc ?? null),
    attachments: [...model.attachments]
  };

  // create：作为创建入参直接生效；edit：父级据此与原协办人 diff 出加入/失效调用（更新接口本身忽略此字段）
  payload.assigneeUserIds = normalizeAssigneeIds(model.assigneeUserIds);

  emit('submit', payload);
}

function handleAssigneeChange(value: string[]) {
  // UI 层保持 owner 不掉队（owner 选项 disabled，正常无法取消，这里兜底）；提交时再由 normalizeAssigneeIds 过滤
  const cleaned = Array.from(new Set(value.filter(Boolean)));
  if (model.ownerId && !cleaned.includes(model.ownerId)) {
    cleaned.push(model.ownerId);
  }
  model.assigneeUserIds = cleaned;
}

function fillFromExecution() {
  const execution = props.executionData;
  if (!execution) return;

  model.taskTitle = execution.executionName || '';
  model.ownerId = execution.ownerId || null;
  model.plannedStartDate = execution.plannedStartDate || null;
  model.plannedEndDate = execution.plannedEndDate || null;
  model.priority = execution.priority || '3';
  model.taskDesc = execution.executionDesc || null;
  model.assigneeUserIds = props.userOptions.map(item => item.id);

  nextTick(() => {
    formRef.value?.clearValidate(['taskTitle', 'ownerId', 'plannedStartDate', 'plannedEndDate', 'priority']);
  });
}

function applyBasicFieldsFromRow(row: Api.Project.ProjectTask | null) {
  model.taskTitle = row?.taskTitle || '';
  model.type = row?.type || '';
  model.ownerId = row?.ownerId || null;
  model.plannedStartDate = row?.plannedStartDate || null;
  model.plannedEndDate = row?.plannedEndDate || null;
  model.priority = row?.priority || '3';
  model.taskDesc = row?.taskDesc || null;
}

function applyRowDataToModel() {
  const row = props.rowData;
  model.parentTaskId = props.mode === 'create' ? (props.defaultParentTaskId ?? null) : row?.parentTaskId || null;
  applyBasicFieldsFromRow(row);
  // 编辑态回显任务现有协办人 + 并入负责人（owner 仅做视觉展示，提交时过滤）；创建态留空
  const baseAssignees = props.mode === 'create' ? [] : (row?.assignees ?? []).map(item => item.userId);
  model.assigneeUserIds =
    model.ownerId && !baseAssignees.includes(model.ownerId) ? [...baseAssignees, model.ownerId] : baseAssignees;
  model.attachments = row?.attachments ? [...row.attachments] : [];
}

watch(
  () => visible.value,
  async value => {
    if (!value) {
      return;
    }

    applyRowDataToModel();
    // 记录已自动并入协办人列表的 owner，供后续切换负责人时移除上一任
    autoOwnerAssigneeId.value = model.ownerId || null;

    await nextTick();
    // 让附件组件把当前 model 视作 original，必须在 model 填充之后
    attachmentUploaderRef.value?.initSession();
    richTextEditorRef.value?.initSession();
    formRef.value?.clearValidate();
  }
);

watch(
  () => model.ownerId,
  ownerId => {
    // previousOwnerId 取 autoOwnerAssigneeId（上一任已并入的 owner），避免跨任务残留误删协办人
    syncOwnerAssignee(ownerId || null);
  }
);

defineExpose({
  /** 父组件在业务保存成功后调用，触发删除被标记的附件 + 已被删的富文本图片 */
  async commit() {
    await Promise.all([attachmentUploaderRef.value?.commit(), richTextEditorRef.value?.commit()]);
  }
});
</script>

<template>
  <BusinessFormDialog
    v-model="visible"
    :title="dialogTitle"
    width="1100px"
    max-body-height="78vh"
    @confirm="handleConfirm"
  >
    <ElForm
      ref="formRef"
      :model="model"
      :rules="rules"
      label-position="top"
      :validate-on-rule-change="false"
      class="task-operate-dialog__form"
    >
      <div class="task-operate-dialog__grid">
        <div ref="leftColRef" class="task-operate-dialog__col-left">
          <BusinessFormSection title="任务信息">
            <template v-if="canFillFromExecution" #title>
              <span class="task-operate-dialog__section-title">
                <span>任务信息</span>
                <ElTooltip content="一键带出当前执行信息" placement="top">
                  <ElButton text type="primary" class="task-operate-dialog__title-action" @click="fillFromExecution">
                    <template #icon>
                      <IconMdiAutoFix class="text-16px" />
                    </template>
                  </ElButton>
                </ElTooltip>
              </span>
            </template>

            <ElFormItem label="任务名称" prop="taskTitle">
              <ElInput v-model="model.taskTitle" maxlength="200" placeholder="请输入任务名称" />
            </ElFormItem>

            <ElFormItem label="任务类型" prop="type">
              <DictSelect
                v-model="model.type"
                :dict-code="RDMS_TASK_ITEM_TYPE_DICT_CODE"
                placeholder="请选择任务类型"
              />
            </ElFormItem>

            <ElFormItem v-if="SHOW_TASK_PARENT_FIELD" label="父任务">
              <ElSelect v-model="model.parentTaskId" clearable filterable class="w-full" placeholder="请选择父任务">
                <ElOption
                  v-for="item in selectableParentTasks"
                  :key="item.id"
                  :label="item.taskTitle"
                  :value="item.id"
                />
              </ElSelect>
            </ElFormItem>

            <ElFormItem label="优先级" prop="priority">
              <DictSelect
                v-model="model.priority"
                :dict-code="RDMS_REQ_PRIORITY_DICT_CODE"
                placeholder="请选择优先级"
                show-remark
              />
            </ElFormItem>

            <ElFormItem label="负责人" prop="ownerId">
              <template v-if="ownerLocked" #label>
                <span class="business-form-label-with-tip">
                  <ElTooltip
                    content="任务已开始，负责人不可变更。"
                    popper-class="business-form-label-tooltip"
                    placement="top-start"
                  >
                    <span class="business-form-label-tip">
                      <icon-fe:question />
                    </span>
                  </ElTooltip>
                  <span>负责人</span>
                </span>
              </template>
              <BusinessUserSelect
                v-model="model.ownerId"
                :options="ownerSelectOptions"
                :disabled="ownerLocked"
                placeholder="请选择负责人"
              />
            </ElFormItem>

            <ElFormItem label="协办人" prop="assigneeUserIds">
              <template v-if="mode !== 'create'" #label>
                <span class="business-form-label-with-tip">
                  <ElTooltip
                    content="协办人的加入 / 失效变更历史可在列表「协办人」按钮中查看。"
                    popper-class="business-form-label-tooltip"
                    placement="top-start"
                  >
                    <span class="business-form-label-tip">
                      <icon-fe:question />
                    </span>
                  </ElTooltip>
                  <span>协办人</span>
                </span>
              </template>
              <ElSelect
                v-model="model.assigneeUserIds"
                multiple
                filterable
                collapse-tags
                collapse-tags-tooltip
                :max-collapse-tags="2"
                :disabled="assigneeSelectDisabled"
                class="w-full"
                :placeholder="assigneePlaceholder"
                @change="handleAssigneeChange"
              >
                <ElOption
                  v-for="item in assigneeSelectOptions"
                  :key="item.id"
                  :label="item.id === model.ownerId ? `${item.nickname}（负责人）` : item.nickname"
                  :value="item.id"
                  :disabled="item.id === model.ownerId"
                />
              </ElSelect>
            </ElFormItem>

            <ElFormItem label="计划开始日期" prop="plannedStartDate">
              <ElDatePicker
                v-model="model.plannedStartDate"
                type="date"
                value-format="YYYY-MM-DD"
                placeholder="选择计划开始日期"
                class="task-operate-dialog__date-picker"
              />
            </ElFormItem>

            <ElFormItem label="计划结束日期" prop="plannedEndDate">
              <ElDatePicker
                v-model="model.plannedEndDate"
                type="date"
                value-format="YYYY-MM-DD"
                placeholder="选择计划结束日期"
                :shortcuts="plannedEndDateShortcuts"
                class="task-operate-dialog__date-picker"
              />
            </ElFormItem>
          </BusinessFormSection>
        </div>

        <div class="task-operate-dialog__col-right">
          <BusinessFormSection title="任务说明">
            <ElFormItem class="task-operate-dialog__desc-item">
              <BusinessRichTextEditor
                ref="richTextEditorRef"
                v-model="model.taskDesc"
                :height="editorHeight"
                upload-directory="task"
                placeholder="请输入任务说明"
              />
            </ElFormItem>
          </BusinessFormSection>

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

<style scoped>
.task-operate-dialog__section-title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.task-operate-dialog__title-action {
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: 6px;
}

.task-operate-dialog__title-action:hover {
  background-color: var(--el-color-primary-light-9);
}

.task-operate-dialog__grid {
  display: grid;
  grid-template-columns: 360px 1fr;
  gap: 24px;
  align-items: start;
}

.task-operate-dialog__col-left,
.task-operate-dialog__col-right {
  min-width: 0;
}

.task-operate-dialog__col-right {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

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

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

:deep(.task-operate-dialog__date-picker.el-date-editor.el-input) {
  width: 100%;
}
</style>
