<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue';
import { useResizeObserver } from '@vueuse/core';
import dayjs from 'dayjs';
import { RDMS_PROJECT_EXECUTION_TYPE_DICT_CODE, RDMS_REQ_PRIORITY_DICT_CODE } from '@/constants/dict';
import { useForm, useFormRules } from '@/hooks/common/form';
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 RequirementTreePicker from '../components/requirement-tree-picker.vue';
import { isActiveExecutionAssignee, withVirtualOwnerAssignee } from '../shared';
import { useProjectRequirementOptions } from '../composables/use-project-requirement-options';

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);
}

defineOptions({ name: 'ProjectExecutionOperateDialog' });

type OperateMode = 'create' | 'edit' | 'view';

interface Props {
  mode: OperateMode;
  rowData: Api.Project.ProjectExecution | null;
  userOptions: Api.SystemManage.UserSimple[];
  currentAssignees?: Api.Project.ExecutionAssignee[];
  /** 当前项目 ID（父组件 useCurrentProject 取） */
  projectId: string;
}

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

const props = defineProps<Props>();
const emit = defineEmits<Emits>();

function resolveUserLabel(userId: string | null | undefined, fallbackNickname?: string | null) {
  if (!userId) {
    return '';
  }

  return fallbackNickname || props.userOptions.find(item => item.id === userId)?.nickname || userId;
}

function resolveAssigneeLabel(assignee: Api.Project.ExecutionAssignee) {
  return resolveUserLabel(assignee.userId, assignee.userNickname);
}

const ownerDisplayName = computed(() => resolveUserLabel(props.rowData?.ownerId, props.rowData?.ownerNickname));

// view / edit 模式下协办人 select 的展示数据：先过滤掉失效项，再兜底前置一行虚拟负责人
// 让用户视觉上感知"负责人也在团队里"；虚拟行不会发到后端（这里 select 是 disabled 仅展示）
const activeAssignees = computed(() => {
  const filtered = (props.currentAssignees ?? []).filter(assignee => isActiveExecutionAssignee(assignee));
  return withVirtualOwnerAssignee(filtered, props.rowData?.ownerId, ownerDisplayName.value, props.rowData?.id ?? '');
});

const activeAssigneeIds = computed(() => activeAssignees.value.map(assignee => assignee.userId));

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

const { formRef, validate } = useForm();
const { createRequiredRule } = useFormRules();
const autoOwnerAssigneeId = ref<string | null>(null);

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

useResizeObserver(leftColRef, entries => {
  const h = entries[0]?.contentRect.height;
  if (h && h > 120) {
    // 减去 BusinessFormSection 标题（h4）大致高度，让富文本所在 section 与左栏底对齐
    editorHeight.value = `${Math.max(h - 60, 240)}px`;
  }
});

function parsePlannedDate(value: string | null | undefined) {
  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');
}

const model = reactive<Api.Project.SaveProjectExecutionParams>({
  executionName: '',
  executionType: '',
  ownerId: '',
  projectRequirementId: null,
  plannedStartDate: null,
  plannedEndDate: null,
  priority: '3',
  executionDesc: null,
  assigneeUserIds: []
});

const { treeData: requirementTreeData, reload: reloadRequirementOptions } = useProjectRequirementOptions(
  () => props.projectId || null
);

// 编辑模式下若已关联的需求不在新拉到的树里（已删除/移出当前项目），用 rowData 回显其名称
const requirementFallbackName = computed(() => {
  const name = props.rowData?.projectRequirementName;
  return name ? `${name}（不可用）` : null;
});

function buildEndDateShortcut(text: string, mutator: (date: Date) => void) {
  return {
    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());
      mutator(endDate);
      return endDate;
    }
  };
}

const plannedEndDateShortcuts = [
  buildEndDateShortcut('一星期', date => date.setDate(date.getDate() + 7)),
  buildEndDateShortcut('两星期', date => date.setDate(date.getDate() + 14)),
  buildEndDateShortcut('一个月', date => date.setMonth(date.getMonth() + 1)),
  buildEndDateShortcut('三个月', date => date.setMonth(date.getMonth() + 3)),
  buildEndDateShortcut('半年', date => date.setMonth(date.getMonth() + 6)),
  buildEndDateShortcut('一年', date => date.setFullYear(date.getFullYear() + 1))
];

const isView = computed(() => props.mode === 'view');

const dialogTitle = computed(() => {
  if (props.mode === 'create') {
    return '新建执行';
  }
  if (props.mode === 'view') {
    return props.rowData?.executionName ? `查看执行：${props.rowData.executionName}` : '查看执行';
  }
  return props.rowData?.executionName ? `编辑执行：${props.rowData.executionName}` : '编辑执行';
});
const rules = computed(
  () =>
    ({
      executionName: [createRequiredRule('请输入执行名称')],
      executionType: [createRequiredRule('请选择执行类型')],
      priority: [createRequiredRule('请选择优先级')],
      ownerId: props.mode === 'create' ? [createRequiredRule('请选择执行负责人')] : [],
      assigneeUserIds: props.mode === 'create' ? [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 normalizeAssigneeUserIds(assigneeUserIds?: string[]) {
  return Array.from(new Set(assigneeUserIds?.filter(Boolean) ?? []));
}

function getUserRoleName(item: Api.SystemManage.UserSimple) {
  return item.deptName || '';
}

function syncOwnerAssignee(ownerId: string | null, previousOwnerId: string | null = autoOwnerAssigneeId.value) {
  if (props.mode !== 'create') {
    return;
  }

  const currentAssigneeUserIds = normalizeAssigneeUserIds(model.assigneeUserIds);
  const assigneeUserIds = previousOwnerId
    ? currentAssigneeUserIds.filter(userId => userId !== previousOwnerId)
    : currentAssigneeUserIds;

  model.assigneeUserIds = ownerId ? normalizeAssigneeUserIds([...assigneeUserIds, ownerId]) : assigneeUserIds;
  autoOwnerAssigneeId.value = ownerId;
}

function ensureOwnerInAssignees() {
  if (props.mode !== 'create' || !model.ownerId) {
    return;
  }

  model.assigneeUserIds = normalizeAssigneeUserIds([...(model.assigneeUserIds || []), model.ownerId]);
}

async function handleConfirm() {
  ensureOwnerInAssignees();
  await validate();

  emit('submit', {
    executionName: model.executionName.trim(),
    executionType: model.executionType.trim(),
    ownerId: model.ownerId,
    projectRequirementId: model.projectRequirementId,
    plannedStartDate: model.plannedStartDate,
    plannedEndDate: model.plannedEndDate,
    priority: model.priority,
    executionDesc: isEmptyRichText(model.executionDesc) ? null : (model.executionDesc ?? null),
    assigneeUserIds: props.mode === 'create' ? normalizeAssigneeUserIds(model.assigneeUserIds) : undefined
  });
}

function handleAssigneeChange(value: string[]) {
  if (props.mode !== 'create') {
    return;
  }

  model.assigneeUserIds = normalizeAssigneeUserIds(model.ownerId ? [...value, model.ownerId] : value);
}

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

    model.executionName = props.rowData?.executionName || '';
    model.executionType = props.rowData?.executionType || '';
    model.ownerId = props.rowData?.ownerId || '';
    model.projectRequirementId = props.rowData?.projectRequirementId ?? null;
    model.plannedStartDate = props.rowData?.plannedStartDate || null;
    model.plannedEndDate = props.rowData?.plannedEndDate || null;
    model.priority = props.rowData?.priority || '3';
    model.executionDesc = props.rowData?.executionDesc || null;
    model.assigneeUserIds = [];
    autoOwnerAssigneeId.value = null;

    // 每次打开弹层都重拉一次非终态项目需求，保证下拉新鲜（避免页面挂载后到打开期间需求被关掉）
    reloadRequirementOptions();

    await nextTick();
    formRef.value?.clearValidate();
  }
);

watch(
  () => model.ownerId,
  (ownerId, previousOwnerId) => {
    syncOwnerAssignee(ownerId || null, previousOwnerId || null);
  }
);
</script>

<template>
  <BusinessFormDialog
    v-model="visible"
    :title="dialogTitle"
    width="1100px"
    max-body-height="78vh"
    @confirm="handleConfirm"
  >
    <template v-if="isView" #footer="{ close }">
      <ElButton type="primary" @click="close">关闭</ElButton>
    </template>
    <ElForm
      ref="formRef"
      :model="model"
      :rules="rules"
      label-position="top"
      :validate-on-rule-change="false"
      class="execution-operate-dialog__form"
      :class="{ 'is-view': isView }"
    >
      <div class="execution-operate-dialog__grid">
        <div ref="leftColRef" class="execution-operate-dialog__col-left">
          <BusinessFormSection title="执行信息">
            <ElFormItem label="执行名称" prop="executionName">
              <ElInput v-model="model.executionName" :readonly="isView" maxlength="200" placeholder="请输入执行名称" />
            </ElFormItem>

            <ElFormItem label="执行类型" prop="executionType">
              <DictSelect
                v-model="model.executionType"
                :dict-code="RDMS_PROJECT_EXECUTION_TYPE_DICT_CODE"
                :disabled="isView"
                filterable
                placeholder="请选择执行类型"
              />
            </ElFormItem>

            <ElFormItem label="关联项目需求" prop="projectRequirementId">
              <RequirementTreePicker
                v-model="model.projectRequirementId"
                :data="requirementTreeData"
                :selected-name="requirementFallbackName"
                :disabled="isView"
                placeholder="搜索或选择关联的项目需求（可不选）"
              />
            </ElFormItem>

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

            <ElFormItem v-if="mode === 'create'" label="负责人" prop="ownerId">
              <BusinessUserSelect v-model="model.ownerId" :options="userOptions" placeholder="请选择负责人" />
            </ElFormItem>
            <ElFormItem v-else>
              <template #label>
                <template v-if="isView">负责人</template>
                <span v-else 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>
              <ElInput
                :model-value="ownerDisplayName"
                readonly
                class="execution-operate-dialog__readonly-input"
                placeholder="未设置负责人"
              />
            </ElFormItem>

            <ElFormItem v-if="mode === 'create'" label="执行协办人" prop="assigneeUserIds">
              <ElSelect
                v-model="model.assigneeUserIds"
                multiple
                filterable
                collapse-tags
                collapse-tags-tooltip
                :max-collapse-tags="2"
                class="w-full"
                placeholder="请选择执行协办人"
                @change="handleAssigneeChange"
              >
                <ElOption
                  v-for="item in userOptions"
                  :key="item.id"
                  :label="item.id === model.ownerId ? `${item.nickname}（负责人）` : item.nickname"
                  :value="item.id"
                  :disabled="item.id === model.ownerId"
                >
                  <div class="execution-assignee-option">
                    <span class="execution-assignee-option__name">
                      {{ item.nickname }}
                      <span v-if="item.id === model.ownerId" class="execution-assignee-option__owner">负责人</span>
                    </span>
                    <span v-if="getUserRoleName(item)" class="execution-assignee-option__role">
                      {{ getUserRoleName(item) }}
                    </span>
                  </div>
                </ElOption>
              </ElSelect>
            </ElFormItem>
            <ElFormItem v-else>
              <template #label>
                <template v-if="isView">执行协办人</template>
                <span v-else 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
                :model-value="activeAssigneeIds"
                multiple
                disabled
                collapse-tags
                collapse-tags-tooltip
                :max-collapse-tags="2"
                class="w-full"
                placeholder="暂无在岗协办人"
              >
                <ElOption
                  v-for="assignee in activeAssignees"
                  :key="assignee.id"
                  :label="resolveAssigneeLabel(assignee)"
                  :value="assignee.userId"
                />
              </ElSelect>
            </ElFormItem>

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

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

        <div class="execution-operate-dialog__col-right">
          <BusinessFormSection title="执行说明">
            <ElFormItem class="execution-operate-dialog__desc-item">
              <BusinessRichTextEditor
                v-model="model.executionDesc"
                :disabled="isView"
                :height="editorHeight"
                upload-directory="execution"
                placeholder="请输入执行说明"
              />
            </ElFormItem>
          </BusinessFormSection>
        </div>
      </div>
    </ElForm>
  </BusinessFormDialog>
</template>

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

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

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

.execution-operate-dialog__desc-item {
  margin-bottom: 0;
}

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

:deep(.execution-operate-dialog__date-picker.el-date-editor.el-input) {
  width: 100%;
}

:deep(.execution-operate-dialog__readonly-input .el-input__wrapper) {
  background: linear-gradient(180deg, rgb(241 245 249 / 98%), rgb(226 232 240 / 94%)), rgb(241 245 249);
  box-shadow: 0 0 0 1px rgb(203 213 225 / 96%) inset;
  cursor: default;
}

:deep(.execution-operate-dialog__readonly-input .el-input__wrapper:hover),
:deep(.execution-operate-dialog__readonly-input.is-focus .el-input__wrapper) {
  box-shadow: 0 0 0 1px rgb(203 213 225 / 96%) inset;
}

:deep(.execution-operate-dialog__readonly-input .el-input__inner) {
  color: rgb(51 65 85 / 96%);
  cursor: default;
  -webkit-text-fill-color: rgb(51 65 85 / 96%);
}

.execution-operate-dialog__form.is-view :deep(.el-input__wrapper),
.execution-operate-dialog__form.is-view :deep(.el-select__wrapper),
.execution-operate-dialog__form.is-view :deep(.el-textarea__inner) {
  background: linear-gradient(180deg, rgb(241 245 249 / 98%), rgb(226 232 240 / 94%)), rgb(241 245 249);
  box-shadow: 0 0 0 1px rgb(203 213 225 / 96%) inset;
  cursor: default;
}

.execution-operate-dialog__form.is-view :deep(.el-input__wrapper:hover),
.execution-operate-dialog__form.is-view :deep(.el-input__wrapper.is-focus),
.execution-operate-dialog__form.is-view :deep(.el-select__wrapper:hover),
.execution-operate-dialog__form.is-view :deep(.el-select__wrapper.is-focused),
.execution-operate-dialog__form.is-view :deep(.el-textarea__inner:hover),
.execution-operate-dialog__form.is-view :deep(.el-textarea__inner:focus) {
  box-shadow: 0 0 0 1px rgb(203 213 225 / 96%) inset;
}

.execution-operate-dialog__form.is-view :deep(.el-input__inner),
.execution-operate-dialog__form.is-view :deep(.el-select__placeholder),
.execution-operate-dialog__form.is-view :deep(.el-select__selected-item),
.execution-operate-dialog__form.is-view :deep(.el-textarea__inner) {
  color: rgb(51 65 85 / 96%);
  cursor: default;
  -webkit-text-fill-color: rgb(51 65 85 / 96%);
}

.execution-assignee-option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  min-width: 0;
}

.execution-assignee-option__name {
  display: inline-flex;
  align-items: center;
  min-width: 0;
  gap: 6px;
  overflow: hidden;
  color: rgb(15 23 42 / 94%);
  font-weight: 500;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.execution-assignee-option__owner {
  flex: 0 0 auto;
  color: var(--el-color-primary);
  font-size: 12px;
  font-weight: 500;
}

.execution-assignee-option__role {
  flex: 0 0 auto;
  max-width: 48%;
  overflow: hidden;
  color: rgb(100 116 139 / 88%);
  font-size: 12px;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>
