<script setup lang="ts">
import { computed, nextTick, reactive, ref, watch } from 'vue';
import dayjs from 'dayjs';
import { RDMS_WORKLOG_DIFFICULTY_DICT_CODE } from '@/constants/dict';
import { fetchGetProjectTaskWorklogPage } from '@/service/api/project';
import { useAuthStore } from '@/store/modules/auth';
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 DictSelect from '@/components/custom/dict-select.vue';

defineOptions({ name: 'ProjectExecutionTaskWorklogFormDialog' });

type Mode = 'create' | 'edit' | 'view';
type Granularity = 'day' | 'week';

interface Props {
  mode: Mode;
  rowData: Api.Project.TaskWorklog | null;
  projectId: string;
  executionId: string;
  taskId: string;
  taskOwnerId: string | null;
  /** 任务状态码；completed 时进度字段只读（后端不回写 task.progressRate，避免误改） */
  taskStatusCode: string;
  /** 创建模式下的进度兜底默认值（owner 路径会传 task.progressRate） */
  defaultOwnerProgressRate?: number;
  /** 附件上传目录；任务默认 task-worklog，复用方可按业务域覆盖 */
  attachmentDirectory?: string;
  /** 提交中（HTTP 进行中），由父组件控制 */
  confirmLoading?: boolean;
}

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

const props = withDefaults(defineProps<Props>(), {
  defaultOwnerProgressRate: 0,
  attachmentDirectory: 'task-worklog',
  confirmLoading: false
});
const emit = defineEmits<Emits>();

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

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

const authStore = useAuthStore();
const currentUserId = computed(() => authStore.userInfo.userId || '');
const isOwner = computed(() => Boolean(props.taskOwnerId && props.taskOwnerId === currentUserId.value));
const isView = computed(() => props.mode === 'view');
// 任务 completed 后填工时不回写进度（§4.8.4 备注），此时进度字段只读，避免误导用户以为能改任务进度
const isProgressReadonly = computed(() => isView.value || props.taskStatusCode === 'completed');
// 任务 completed 时编辑模式：除工作内容和附件外，其他字段全部禁用
const isTaskCompleted = computed(() => props.taskStatusCode === 'completed');
const isFieldDisabled = computed(() => isView.value || isTaskCompleted.value);

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

interface FormModel {
  granularity: Granularity;
  /** 'day' 时使用，YYYY-MM-DD */
  workDate: string | null;
  /** 'week' 时使用，ElDatePicker type='week' 返回 Date(周一) */
  weekDate: Date | null;
  /** 0.5 颗粒小时数 */
  durationHours: number | null;
  progressRate: number;
  /** 完成难度，字典 rdms_task_item_worklog_difficulty 的 value，默认 "2"；用户清空后为 null，由 required 校验拦截 */
  difficulty: string | null;
  workContent: string | null;
  attachments: Api.Project.AttachmentItem[];
}

const granularityOptions = [
  { label: '按天', value: 'day' as const },
  { label: '按周', value: 'week' as const }
];

const model = reactive<FormModel>({
  granularity: 'day',
  workDate: null,
  weekDate: null,
  durationHours: null,
  progressRate: 0,
  difficulty: '2',
  workContent: null,
  attachments: []
});

const dialogTitle = computed(() => {
  if (props.mode === 'create') return '填报';
  if (props.mode === 'view') return '查看填报';
  return '修改填报';
});
const dateFieldLabel = computed(() => (model.granularity === 'day' ? '工作日期' : '工作周次'));
const durationPlaceholder = computed(() => (model.granularity === 'day' ? '如 1.5' : '如 40'));

const workDateShortcuts = [
  { text: '今天', value: () => new Date() },
  { text: '昨天', value: () => dayjs().subtract(1, 'day').toDate() },
  { text: '前天', value: () => dayjs().subtract(2, 'day').toDate() }
];

const weekDateShortcuts = [
  { text: '本周', value: () => dayjs().startOf('isoWeek').toDate() },
  { text: '上周', value: () => dayjs().subtract(1, 'week').startOf('isoWeek').toDate() }
];

// EP type='week' 默认 firstDayOfWeek=7，从日历点选时返回当周"周日"作为周首日。
// 我们按 ISO 周（周一-周日）存储 / 展示，遇到周日时先 +1 天，避免 startOf('isoWeek') 回退到上一周。
function resolveIsoWeekStart(weekDate: Date | null) {
  if (!weekDate) return null;
  const picked = dayjs(weekDate);
  if (!picked.isValid()) return null;
  const aligned = picked.isoWeekday() === 7 ? picked.add(1, 'day') : picked;
  return aligned.startOf('isoWeek');
}

// 选中后鼠标悬浮 input 显示该周的起止日期（input 里默认只显示 "YYYY年第W周"）
const weekRangeTooltip = computed(() => {
  const start = resolveIsoWeekStart(model.weekDate);
  if (!start) return '';
  const end = start.add(6, 'day');
  return `${start.format('YYYY-MM-DD')} ~ ${end.format('YYYY-MM-DD')}`;
});

const rules = computed(
  () =>
    ({
      granularity: [createRequiredRule('请选择填报粒度')],
      workDate: [
        {
          required: true,
          validator: (_rule, value: string | null, callback) => {
            if (model.granularity !== 'day') {
              callback();
              return;
            }
            if (!value) {
              callback(new Error('请选择工作日期'));
              return;
            }
            callback();
          },
          trigger: 'change'
        }
      ],
      weekDate: [
        {
          required: true,
          validator: (_rule, value: Date | null, callback) => {
            if (model.granularity !== 'week') {
              callback();
              return;
            }
            if (!value) {
              callback(new Error('请选择工作周次'));
              return;
            }
            callback();
          },
          trigger: 'change'
        }
      ],
      durationHours: [
        {
          required: true,
          validator: (_rule, value: number | null, callback) => {
            if (value === null || value === undefined) {
              callback(new Error('请输入时长'));
              return;
            }
            if (value <= 0) {
              callback(new Error('时长必须大于 0'));
              return;
            }
            // 0.5 小时颗粒（避免浮点误差，乘 10 后判 5 的整数倍）
            if (Math.round(value * 10) % 5 !== 0) {
              callback(new Error('时长必须是 0.5 小时的整数倍'));
              return;
            }
            callback();
          },
          trigger: 'change'
        }
      ],
      progressRate: [
        {
          required: true,
          validator: (_rule, value: number, callback) => {
            if (value < 0 || value > 100) {
              callback(new Error('进度需在 0 到 100 之间'));
              return;
            }
            callback();
          },
          trigger: 'change'
        }
      ],
      difficulty: [createRequiredRule('请选择完成难度')],
      workContent: [
        {
          required: true,
          validator: (_rule, value: string | null, callback) => {
            if (!value || !value.trim()) {
              callback(new Error('请输入工作内容'));
              return;
            }
            callback();
          },
          trigger: 'blur'
        }
      ]
    }) satisfies Record<string, App.Global.FormRule[]>
);

async function loadAssigneeLatestProgress(): Promise<number> {
  if (!props.projectId || !props.executionId || !props.taskId || !currentUserId.value) {
    return 0;
  }

  const { error, data } = await fetchGetProjectTaskWorklogPage(props.projectId, props.executionId, props.taskId, {
    pageNo: 1,
    pageSize: 1,
    userId: currentUserId.value
  });

  if (error || !data?.list?.length) {
    return 0;
  }

  return data.list[0]?.progressRate ?? 0;
}

async function resolveDefaultProgressRate(): Promise<number> {
  // edit / view 都从已有 rowData 取，避免多发一次 latest-progress 请求
  if (props.mode === 'edit' || props.mode === 'view') {
    return props.rowData?.progressRate ?? 0;
  }

  if (isOwner.value) {
    return props.defaultOwnerProgressRate;
  }

  return loadAssigneeLatestProgress();
}

function detectGranularityFromRow(row: Api.Project.TaskWorklog): Granularity {
  if (!row.startDate || !row.endDate) {
    return 'day';
  }
  if (row.startDate === row.endDate) {
    return 'day';
  }
  const start = dayjs(row.startDate);
  const end = dayjs(row.endDate);
  if (start.isoWeekday() === 1 && end.isoWeekday() === 7 && end.diff(start, 'day') === 6) {
    return 'week';
  }
  return 'day';
}

function getStartEndFromModel(): { startDate: string; endDate: string } {
  if (model.granularity === 'day') {
    return { startDate: model.workDate!, endDate: model.workDate! };
  }
  const weekStart = resolveIsoWeekStart(model.weekDate)!;
  return {
    startDate: weekStart.format('YYYY-MM-DD'),
    endDate: weekStart.add(6, 'day').format('YYYY-MM-DD')
  };
}

watch(
  () => model.granularity,
  () => {
    // 切换粒度会让日期 ElFormItem 的 :prop 在 workDate/weekDate 之间切换，
    // ElForm 内部对所有已挂载字段触发一轮校验；这里整张表清一次校验提示，避免误报
    formRef.value?.clearValidate();
  }
);

async function handleConfirm() {
  if (isView.value) {
    visible.value = false;
    return;
  }

  await validate();

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

  const { startDate, endDate } = getStartEndFromModel();

  const payload: Api.Project.SaveTaskWorklogParams = {
    startDate,
    endDate,
    durationHours: Number(model.durationHours!.toFixed(1)),
    progressRate: Number(model.progressRate.toFixed(2)),
    difficulty: model.difficulty!,
    workContent: model.workContent?.trim() || null,
    attachments: [...model.attachments]
  };

  emit('submit', payload);
}

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

    const row = props.rowData;
    if (row) {
      const detected = detectGranularityFromRow(row);
      model.granularity = detected;
      if (detected === 'week') {
        model.workDate = null;
        // 用 dayjs 解析为本地日期 0 点，避免 new Date('YYYY-MM-DD') 按 UTC 解析在负时区误移一周
        model.weekDate = dayjs(row.startDate).toDate();
      } else {
        model.workDate = row.startDate || dayjs().format('YYYY-MM-DD');
        model.weekDate = null;
      }
      model.durationHours = typeof row.durationHours === 'number' ? row.durationHours : null;
      // PUT 需全字段回传，回显时按 row 原值；row.difficulty 兜底已在 normalize 层
      model.difficulty = row.difficulty || '2';
      model.workContent = row.workContent || null;
      model.attachments = row.attachments ? [...row.attachments] : [];
    } else {
      model.granularity = 'day';
      model.workDate = dayjs().format('YYYY-MM-DD');
      model.weekDate = null;
      model.durationHours = null;
      model.difficulty = '2';
      model.workContent = null;
      model.attachments = [];
    }

    // 异步取默认值期间先复位为 0，避免闪现上一次的旧值
    model.progressRate = 0;
    const defaultProgress = await resolveDefaultProgressRate();
    model.progressRate = defaultProgress;

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

defineExpose({
  /** 父组件在业务保存成功后调用 */
  async commit() {
    await attachmentUploaderRef.value?.commit();
  }
});
</script>

<template>
  <BusinessFormDialog
    v-model="visible"
    :title="dialogTitle"
    preset="md"
    :confirm-loading="props.confirmLoading"
    @confirm="handleConfirm"
  >
    <ElForm ref="formRef" :model="model" :rules="rules" label-position="top" :validate-on-rule-change="false">
      <ElRow :gutter="16">
        <ElCol :span="12">
          <ElFormItem label="填报粒度" prop="granularity">
            <ElSegmented v-model="model.granularity" :options="granularityOptions" :disabled="isFieldDisabled" />
          </ElFormItem>
        </ElCol>
        <ElCol :span="12">
          <ElFormItem :label="dateFieldLabel" :prop="model.granularity === 'day' ? 'workDate' : 'weekDate'">
            <ElDatePicker
              v-if="model.granularity === 'day'"
              v-model="model.workDate"
              type="date"
              value-format="YYYY-MM-DD"
              placeholder="选择工作日期"
              :shortcuts="isFieldDisabled ? undefined : workDateShortcuts"
              :disabled="isFieldDisabled"
              class="task-worklog-form-dialog__date-picker"
            />
            <ElTooltip v-else :content="weekRangeTooltip" :disabled="!weekRangeTooltip" placement="top">
              <span class="task-worklog-form-dialog__week-wrapper">
                <ElDatePicker
                  v-model="model.weekDate"
                  type="week"
                  format="YYYY[年第]ww[周]"
                  placeholder="选择工作周次"
                  :shortcuts="isFieldDisabled ? undefined : weekDateShortcuts"
                  :disabled="isFieldDisabled"
                  class="task-worklog-form-dialog__date-picker"
                />
              </span>
            </ElTooltip>
          </ElFormItem>
        </ElCol>
        <ElCol :span="8">
          <ElFormItem label="时长（小时）" prop="durationHours">
            <ElInputNumber
              v-model="model.durationHours"
              :min="0.5"
              :step="0.5"
              :precision="1"
              :placeholder="durationPlaceholder"
              :disabled="isFieldDisabled"
              controls-position="right"
              class="w-full"
            />
          </ElFormItem>
        </ElCol>
        <ElCol :span="8">
          <ElFormItem label="进度（%）" prop="progressRate">
            <ElInputNumber
              v-model="model.progressRate"
              :min="0"
              :max="100"
              :step="1"
              :precision="2"
              :disabled="isProgressReadonly"
              controls-position="right"
              class="w-full"
            />
          </ElFormItem>
        </ElCol>
        <ElCol :span="8">
          <ElFormItem label="完成难度" prop="difficulty">
            <DictSelect
              v-model="model.difficulty"
              :dict-code="RDMS_WORKLOG_DIFFICULTY_DICT_CODE"
              placeholder="请选择完成难度"
              :disabled="isFieldDisabled"
              show-remark
            />
          </ElFormItem>
        </ElCol>
        <ElCol :span="24">
          <ElFormItem label="工作内容" prop="workContent">
            <ElInput
              v-model="model.workContent"
              type="textarea"
              :autosize="{ minRows: 3, maxRows: 6 }"
              :maxlength="isView ? undefined : 2000"
              :show-word-limit="!isView"
              :disabled="isView"
              placeholder="简述本次填报的工作内容"
            />
          </ElFormItem>
        </ElCol>
        <ElCol :span="24">
          <ElFormItem label="附件" class="task-worklog-form-dialog__attachment-item">
            <BusinessAttachmentUploader
              ref="attachmentUploaderRef"
              v-model="model.attachments"
              :disabled="isView"
              :directory="props.attachmentDirectory"
            />
          </ElFormItem>
        </ElCol>
      </ElRow>
    </ElForm>

    <template v-if="isView" #footer="{ close }">
      <ElButton type="primary" @click="close">关闭</ElButton>
    </template>
  </BusinessFormDialog>
</template>

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

.task-worklog-form-dialog__week-wrapper {
  display: block;
  width: 100%;
}

/* 预留附件区域最小高度（触发器一行 + 2 条附件占位），避免前两条附件加进去时 align-center dialog 重新居中产生抖动 */
.task-worklog-form-dialog__attachment-item :deep(.el-form-item__content) {
  min-height: 120px;
  align-content: flex-start;
}
</style>
