import dayjs from 'dayjs';

export type WorkbenchTrend = 'up' | 'down' | 'flat';

export type WorkbenchTodoCategory = 'task' | 'ticket' | 'personal' | 'approval';

export type WorkbenchTodoMainTab = 'all' | WorkbenchTodoCategory;

export type WorkbenchTodoDeadlineFilter = 'overdue' | 'today' | 'week' | null;

export type WorkbenchTodoPriority = 'high' | 'mid' | 'low';

export type WorkbenchProjectStatus = 'active' | 'preview' | 'paused';

export interface WorkbenchKpiSource {
  /** 待办 */
  todo: {
    count: number;
    diffFromYesterday: number;
  };
  /** 我负责的任务 */
  task: {
    count: number;
    diffFromYesterday: number;
  };
  /** 我参与的项目 */
  project: {
    count: number;
    activeCount: number;
  };
  /** 我负责的需求 */
  requirement: {
    count: number;
    pendingReview: number;
  };
}

export interface WorkbenchKpiCard {
  key: 'todo' | 'task' | 'project' | 'requirement';
  label: string;
  value: number;
  trend: WorkbenchTrend;
  trendText: string;
  hint: string;
  icon: string;
  tone: 'sky' | 'emerald' | 'amber' | 'rose';
}

export interface WorkbenchTodoItemSource {
  id: string;
  category: WorkbenchTodoCategory;
  title: string;
  /** 创建时间，ISO 字符串。列表默认按这个升序 */
  createdTime: string;
  /** 截止时间，ISO 字符串 */
  deadline: string | null;
  /** 来源（提交人/项目名/工单号） */
  source: string;
  /** 优先级，用于前端排序与高亮 */
  priority?: WorkbenchTodoPriority;
  /** 是否逾期 */
  overdue?: boolean;
  /** 跳转路由 key（可选，未配则不跳转） */
  routeKey?: string;
  /** 审批业务类型 */
  approvalBizType?: 'overtime_application' | string;
  /** 审批业务 ID */
  approvalBizId?: string;
}

export interface WorkbenchTodoItem extends Omit<WorkbenchTodoItemSource, 'deadline'> {
  deadlineLabel: string;
  /** 相对截止天数：负数表示已逾期 */
  remainingDays: number | null;
  categoryLabel: string;
  categoryTone: 'sky' | 'emerald' | 'amber' | 'rose' | 'violet';
}

export interface WorkbenchActivityItemSource {
  id: string;
  actor: string;
  action: string;
  /** 目标对象（需求/任务/工单/项目名等） */
  target: string;
  /** 目标对象的种类，用于颜色 */
  targetKind: 'requirement' | 'task' | 'ticket' | 'project' | 'product';
  /** 时间，ISO */
  time: string;
  /** 是否 @ 了当前用户 */
  mentioned?: boolean;
}

export interface WorkbenchActivityItem extends Omit<WorkbenchActivityItemSource, 'time'> {
  timeLabel: string;
  relativeLabel: string;
  tone: 'sky' | 'emerald' | 'amber' | 'rose' | 'violet';
}

export interface WorkbenchProjectItemSource {
  id: string;
  name: string;
  code: string;
  status: WorkbenchProjectStatus;
  /** 我的角色 */
  myRole: string;
  /** 进度百分比 0-100 */
  progress: number;
  /** 我负责的任务数 */
  myTaskCount: number;
  /** 我负责的待处理任务数 */
  myPendingTaskCount: number;
  /** 最近活动时间，ISO */
  lastActiveTime: string;
}

export interface WorkbenchProjectItem extends Omit<WorkbenchProjectItemSource, 'lastActiveTime'> {
  statusLabel: string;
  statusTone: 'sky' | 'emerald' | 'amber';
  progress: number;
  lastActiveLabel: string;
}

const todoCategoryMeta: Record<
  WorkbenchTodoCategory,
  { label: string; tone: WorkbenchTodoItem['categoryTone']; icon: string }
> = {
  task: { label: '任务', tone: 'emerald', icon: 'mdi:checkbox-marked-circle-outline' },
  ticket: { label: '工单', tone: 'amber', icon: 'mdi:ticket-confirmation-outline' },
  personal: { label: '个人事项', tone: 'violet', icon: 'mdi:notebook-edit-outline' },
  approval: { label: '待审批', tone: 'sky', icon: 'mdi:checkbox-multiple-marked-outline' }
};

const todoPriorityWeight: Record<WorkbenchTodoPriority, number> = {
  high: 0,
  mid: 1,
  low: 2
};

const activityToneMap: Record<WorkbenchActivityItemSource['targetKind'], WorkbenchActivityItem['tone']> = {
  requirement: 'sky',
  task: 'emerald',
  ticket: 'amber',
  project: 'violet',
  product: 'rose'
};

const projectStatusMeta: Record<WorkbenchProjectStatus, { label: string; tone: WorkbenchProjectItem['statusTone'] }> = {
  active: { label: '进行中', tone: 'emerald' },
  preview: { label: '试运行', tone: 'sky' },
  paused: { label: '已暂停', tone: 'amber' }
};

function clampPercent(value: number) {
  if (!Number.isFinite(value)) return 0;
  return Math.min(100, Math.max(0, Math.round(value)));
}

function formatRelative(value: string) {
  const target = dayjs(value);
  if (!target.isValid()) return '--';

  const now = dayjs();
  const diffMinutes = now.diff(target, 'minute');

  if (diffMinutes < 1) return '刚刚';
  if (diffMinutes < 60) return `${diffMinutes} 分钟前`;

  const diffHours = now.diff(target, 'hour');
  if (diffHours < 24) return `${diffHours} 小时前`;

  const diffDays = now.diff(target, 'day');
  if (diffDays < 7) return `${diffDays} 天前`;

  return target.format('MM-DD HH:mm');
}

function formatDeadline(value: string | null) {
  if (!value) return '不限';
  const target = dayjs(value);
  if (!target.isValid()) return '不限';

  const now = dayjs().startOf('day');
  const start = target.startOf('day');
  const days = start.diff(now, 'day');

  if (days === 0) return `今日 ${target.format('HH:mm')}`;
  if (days === 1) return `明日 ${target.format('HH:mm')}`;
  if (days === -1) return `昨日 ${target.format('HH:mm')}`;
  if (days > 0 && days <= 7) return `${days} 天后 ${target.format('MM-DD')}`;
  if (days < 0) return `逾期 ${Math.abs(days)} 天`;
  return target.format('YYYY-MM-DD');
}

function getRemainingDays(value: string | null) {
  if (!value) return null;
  const target = dayjs(value);
  if (!target.isValid()) return null;

  return target.startOf('day').diff(dayjs().startOf('day'), 'day');
}

export function buildWorkbenchKpiCards(source: WorkbenchKpiSource): WorkbenchKpiCard[] {
  function diffTrend(diff: number): { trend: WorkbenchTrend; text: string } {
    if (diff > 0) return { trend: 'up', text: `较昨日 +${diff}` };
    if (diff < 0) return { trend: 'down', text: `较昨日 ${diff}` };
    return { trend: 'flat', text: '与昨日持平' };
  }

  const todoTrend = diffTrend(source.todo.diffFromYesterday);
  const taskTrend = diffTrend(source.task.diffFromYesterday);

  return [
    {
      key: 'todo',
      label: '我的待办',
      value: source.todo.count,
      trend: todoTrend.trend,
      trendText: todoTrend.text,
      hint: '需要我处理的评审、任务、工单与 @ 提醒合计',
      icon: 'mdi:clipboard-text-clock-outline',
      tone: 'sky'
    },
    {
      key: 'task',
      label: '我负责的任务',
      value: source.task.count,
      trend: taskTrend.trend,
      trendText: taskTrend.text,
      hint: '当前由我负责的进行中任务',
      icon: 'mdi:checkbox-marked-circle-outline',
      tone: 'emerald'
    },
    {
      key: 'project',
      label: '我参与的项目',
      value: source.project.count,
      trend: 'flat',
      trendText: `进行中 ${source.project.activeCount} 个`,
      hint: '我作为成员参与的项目总数',
      icon: 'mdi:briefcase-outline',
      tone: 'amber'
    },
    {
      key: 'requirement',
      label: '我负责的需求',
      value: source.requirement.count,
      trend: 'flat',
      trendText: `待评审 ${source.requirement.pendingReview} 项`,
      hint: '由我担任产品经理或负责人的需求',
      icon: 'mdi:file-document-multiple-outline',
      tone: 'rose'
    }
  ];
}

export function buildWorkbenchTodoItems(source: readonly WorkbenchTodoItemSource[]): WorkbenchTodoItem[] {
  return [...source]
    .sort((left, right) => {
      const leftCreated = dayjs(left.createdTime).valueOf();
      const rightCreated = dayjs(right.createdTime).valueOf();
      if (leftCreated !== rightCreated) return leftCreated - rightCreated;
      const leftDeadline = left.deadline ? dayjs(left.deadline).valueOf() : Number.POSITIVE_INFINITY;
      const rightDeadline = right.deadline ? dayjs(right.deadline).valueOf() : Number.POSITIVE_INFINITY;
      return leftDeadline - rightDeadline;
    })
    .map(item => {
      const meta = todoCategoryMeta[item.category];
      return {
        ...item,
        deadlineLabel: formatDeadline(item.deadline),
        remainingDays: getRemainingDays(item.deadline),
        categoryLabel: meta.label,
        categoryTone: meta.tone
      } satisfies WorkbenchTodoItem;
    });
}

export function filterWorkbenchTodoItemsByCategory(items: readonly WorkbenchTodoItem[], tab: WorkbenchTodoMainTab) {
  if (tab === 'all') return [...items];
  return items.filter(item => item.category === tab);
}

export function filterWorkbenchTodoItemsByDeadline(
  items: readonly WorkbenchTodoItem[],
  filter: WorkbenchTodoDeadlineFilter
) {
  if (!filter) return [...items];
  if (filter === 'overdue') return items.filter(item => isOverdue(item));
  if (filter === 'today') return items.filter(item => item.remainingDays === 0);
  // filter === 'week'：含今日，含逾期一起暴露给用户处理
  return items.filter(item => item.remainingDays !== null && item.remainingDays <= 7);
}

export function isWorkbenchTodoOverdue(item: WorkbenchTodoItem) {
  return isOverdue(item);
}

function isOverdue(item: WorkbenchTodoItem) {
  if (item.overdue) return true;
  return item.remainingDays !== null && item.remainingDays < 0;
}

export function sortWorkbenchTodoItemsByPriority(
  items: readonly WorkbenchTodoItem[],
  direction: 'asc' | 'desc' = 'desc'
) {
  const factor = direction === 'desc' ? 1 : -1;
  return [...items].sort((left, right) => {
    const leftWeight = todoPriorityWeight[left.priority ?? 'low'];
    const rightWeight = todoPriorityWeight[right.priority ?? 'low'];
    if (leftWeight !== rightWeight) return (leftWeight - rightWeight) * factor;
    return dayjs(left.createdTime).valueOf() - dayjs(right.createdTime).valueOf();
  });
}

export function buildWorkbenchActivityItems(source: readonly WorkbenchActivityItemSource[]): WorkbenchActivityItem[] {
  return [...source]
    .filter(item => dayjs(item.time).isValid())
    .sort((left, right) => dayjs(right.time).valueOf() - dayjs(left.time).valueOf())
    .map(item => ({
      ...item,
      timeLabel: dayjs(item.time).format('YYYY-MM-DD HH:mm'),
      relativeLabel: formatRelative(item.time),
      tone: activityToneMap[item.targetKind]
    }));
}

export function buildWorkbenchProjectItems(source: readonly WorkbenchProjectItemSource[]): WorkbenchProjectItem[] {
  return source.map(item => {
    const meta = projectStatusMeta[item.status];
    return {
      ...item,
      statusLabel: meta.label,
      statusTone: meta.tone,
      progress: clampPercent(item.progress),
      lastActiveLabel: formatRelative(item.lastActiveTime)
    } satisfies WorkbenchProjectItem;
  });
}

export interface WorkbenchOwnedProjectMilestone {
  id: string;
  title: string;
  timeLabel: string;
  tone: 'amber' | 'slate';
}

export interface WorkbenchOwnedProjectMember {
  name: string;
  /** 负载 0-100（百分比） */
  load: number;
  level: 'ok' | 'warn' | 'over';
}

export interface WorkbenchOwnedProjectItemSource {
  id: string;
  name: string;
  code: string;
  /** 进度 0-100 */
  progress: number;
  executionCount: number;
  taskCount: number;
  memberCount: number;
  overdueCount: number;
  /** 距离计划结束剩余天数（负数表示已逾期） */
  remainingDays: number;
  /** 我在该项目中的角色 */
  myRole: string;
  milestones: WorkbenchOwnedProjectMilestone[];
  members: WorkbenchOwnedProjectMember[];
}

export interface WorkbenchOwnedProjectItem extends WorkbenchOwnedProjectItemSource {
  progress: number;
}

export function buildWorkbenchOwnedProjectItems(
  source: readonly WorkbenchOwnedProjectItemSource[]
): WorkbenchOwnedProjectItem[] {
  return source.map(item => ({
    ...item,
    progress: clampPercent(item.progress)
  }));
}

/** 工时分布行项：项目 / 个人事项 / 其他（杂项） */
export interface WorkbenchWorklogDistributionItem {
  /** 唯一 key；project 用 projectId，personal/other 用固定字面量 */
  key: string;
  label: string;
  hours: number;
  kind: 'project' | 'personal' | 'other';
  /** kind === 'project' 时携带，用于跳转项目对象上下文 */
  projectId?: string;
}

/** 单周工时数据源（按天填 / 按周填两类共存） */
export interface WorkbenchWeekWorklogSource {
  /** ISO 周首日（周一），YYYY-MM-DD */
  weekStart: string;
  /** 整周一笔填的工时总和（将按工作日 5 等分均摊到周一~周五） */
  weeklyFilledHours: number;
  /** 周一~周五按天填的工时，长度恒为 5 */
  dailyHours: [number, number, number, number, number];
  /** 工时分布；前端不再做截断，超出 5 行靠容器滚动 */
  distribution: WorkbenchWorklogDistributionItem[];
  /** 周目标小时数（默认 40） */
  target: number;
}

/** 双周工时 mock 容器：本周 + 上周 */
export interface WorkbenchMyWeekWorklogSource {
  current: WorkbenchWeekWorklogSource;
  previous: WorkbenchWeekWorklogSource;
}

/** 单周工时视图（builder 衍生） */
export interface WorkbenchWeekWorklogView {
  weekStart: string;
  weekLabel: string;
  /** 周一~周五按天填部分（5 长度） */
  dailyByDay: number[];
  /** 周一~周五按周均分部分（5 长度，每项 = weeklyFilledHours / 5） */
  dailyByWeekAvg: number[];
  /** 周一~周五合计（5 长度，dailyByDay + dailyByWeekAvg） */
  dailyTotal: number[];
  /** 累计（含按周） */
  totalHours: number;
  target: number;
  /** 累计 - 目标；正=领先、负=落后 */
  delta: number;
  /** 达成率，0-100 整数 */
  completionRate: number;
  distribution: WorkbenchWorklogDistributionItem[];
}

const WEEKDAY_COUNT = 5;

function roundHours(value: number) {
  return Math.round(value * 10) / 10;
}

export function buildWorkbenchWeekWorklogView(source: WorkbenchWeekWorklogSource): WorkbenchWeekWorklogView {
  const start = dayjs(source.weekStart);
  const weekLabel = start.isValid() ? `${start.isoWeekYear()}年第${start.isoWeek()}周` : source.weekStart;

  const weekAvg = roundHours(source.weeklyFilledHours / WEEKDAY_COUNT);
  const dailyByWeekAvg = Array.from({ length: WEEKDAY_COUNT }, () => weekAvg);
  const dailyByDay = source.dailyHours.map(roundHours);
  const dailyTotal = dailyByDay.map((h, i) => roundHours(h + dailyByWeekAvg[i]));
  const totalHours = roundHours(dailyTotal.reduce((s, h) => s + h, 0));
  const delta = roundHours(totalHours - source.target);
  const completionRate = source.target > 0 ? clampPercent((totalHours / source.target) * 100) : 0;

  return {
    weekStart: source.weekStart,
    weekLabel,
    dailyByDay,
    dailyByWeekAvg,
    dailyTotal,
    totalHours,
    target: source.target,
    delta,
    completionRate,
    distribution: source.distribution
  };
}

export function getGreeting(hour: number = dayjs().hour()) {
  if (hour < 6) return '凌晨好';
  if (hour < 11) return '早上好';
  if (hour < 13) return '中午好';
  if (hour < 18) return '下午好';
  if (hour < 22) return '晚上好';
  return '夜深了';
}

// === 团队工时分布（C12 · teamWorklog） ===

export type WorkbenchTeamWorklogItemKind = 'project' | 'personal' | 'other';

export interface WorkbenchTeamWorklogItem {
  /** 项目 id 或 'personal' / 'other' */
  key: string;
  label: string;
  hours: number;
  kind: WorkbenchTeamWorklogItemKind;
}

export interface WorkbenchTeamWorklogMemberSource {
  memberId: string;
  memberName: string;
  items: WorkbenchTeamWorklogItem[];
}

export interface WorkbenchTeamWorklogSource {
  weekStart: string;
  /** 单人周应填工时（用于填报率口径） */
  expectedHoursPerMember: number;
  members: WorkbenchTeamWorklogMemberSource[];
}

/** 团队工时分布视图（builder 衍生） */
export interface WorkbenchTeamWorklogView {
  weekStart: string;
  weekLabel: string;
  members: Array<{
    memberId: string;
    memberName: string;
    totalHours: number;
  }>;
  /** 项目/个人/其他 列（保序，去重） */
  categories: Array<{ key: string; label: string; kind: WorkbenchTeamWorklogItemKind }>;
  /** 每个 category 一个 series，data[i] = 第 i 个成员该 category 的工时（缺则 0） */
  seriesMatrix: Array<{ key: string; label: string; kind: WorkbenchTeamWorklogItemKind; data: number[] }>;
  /** 团队总工时 */
  totalHours: number;
  /** 团队人均工时 */
  averageHours: number;
  /** 应填工时合计（人数 × expectedHoursPerMember） */
  expectedTotalHours: number;
  /** 填报率 0-100 整数（= 总工时 / 应填工时） */
  fillRate: number;
  /** 偏低人数：< 团队均值 × 0.8 */
  lowCount: number;
  /** 加班人数：> 45h（应填 × 1.125） */
  highCount: number;
  /** 工时最低成员（用于底部小结） */
  lowest: { memberName: string; hours: number } | null;
  /** 工时最高成员（用于底部小结） */
  highest: { memberName: string; hours: number } | null;
}

export function buildWorkbenchTeamWorklogView(source: WorkbenchTeamWorklogSource): WorkbenchTeamWorklogView {
  const start = dayjs(source.weekStart);
  const weekLabel = start.isValid() ? `${start.isoWeekYear()}年第${start.isoWeek()}周` : source.weekStart;

  // 列保序去重：按成员遍历顺序首次出现即入列；项目优先、个人/其他按出现顺序追加
  const categoryMap = new Map<string, { key: string; label: string; kind: WorkbenchTeamWorklogItemKind }>();
  for (const m of source.members) {
    for (const it of m.items) {
      if (!categoryMap.has(it.key)) {
        categoryMap.set(it.key, { key: it.key, label: it.label, kind: it.kind });
      }
    }
  }
  const categories = Array.from(categoryMap.values());

  const memberView = source.members.map(m => ({
    memberId: m.memberId,
    memberName: m.memberName,
    totalHours: roundHours(m.items.reduce((s, it) => s + it.hours, 0))
  }));

  const seriesMatrix = categories.map(cat => ({
    ...cat,
    data: source.members.map(m => {
      const hit = m.items.find(it => it.key === cat.key);
      return hit ? roundHours(hit.hours) : 0;
    })
  }));

  const totalHours = roundHours(memberView.reduce((s, m) => s + m.totalHours, 0));
  const memberCount = memberView.length;
  const averageHours = memberCount > 0 ? roundHours(totalHours / memberCount) : 0;
  const expectedTotalHours = memberCount * source.expectedHoursPerMember;
  const fillRate = expectedTotalHours > 0 ? clampPercent((totalHours / expectedTotalHours) * 100) : 0;

  const lowThreshold = averageHours * 0.8;
  const highThreshold = source.expectedHoursPerMember * 1.125;
  const lowCount = memberView.filter(m => m.totalHours < lowThreshold).length;
  const highCount = memberView.filter(m => m.totalHours > highThreshold).length;

  let lowest: { memberName: string; hours: number } | null = null;
  let highest: { memberName: string; hours: number } | null = null;
  for (const m of memberView) {
    if (!lowest || m.totalHours < lowest.hours) lowest = { memberName: m.memberName, hours: m.totalHours };
    if (!highest || m.totalHours > highest.hours) highest = { memberName: m.memberName, hours: m.totalHours };
  }

  return {
    weekStart: source.weekStart,
    weekLabel,
    members: memberView,
    categories,
    seriesMatrix,
    totalHours,
    averageHours,
    expectedTotalHours,
    fillRate,
    lowCount,
    highCount,
    lowest,
    highest
  };
}

// === 团队负载（C13 · teamLoad） ===

export type WorkbenchTeamLoadLevel = 'high' | 'mid' | 'normal';

export type WorkbenchTeamLoadItemKind = 'project' | 'personal';

export interface WorkbenchTeamLoadItemSource {
  /** projectId 或 'personal' */
  key: string;
  label: string;
  kind: WorkbenchTeamLoadItemKind;
  /** 该项目/个人事项下，该成员进行中的任务/事项数（任务按"负责人/协办人"口径，单任务多人各算 1 条） */
  count: number;
}

export interface WorkbenchTeamLoadMemberSource {
  memberId: string;
  memberName: string;
  /** 进行中按项目 + 个人事项的拆分（合计即"进行中总数"） */
  items: WorkbenchTeamLoadItemSource[];
  /** 今天 ≤ 计划结束 ≤ 今天+3 天 且未完成 */
  dueSoon: number;
  /** 计划结束 < 今天 且未完成 */
  overdue: number;
}

export interface WorkbenchTeamLoadSource {
  /** 数据快照所属周（与 C12 对齐口径） */
  weekStart: string;
  members: WorkbenchTeamLoadMemberSource[];
}

export interface WorkbenchTeamLoadSegment extends WorkbenchTeamLoadItemSource {
  /** 段宽占"柱子内部"的百分比（段总和 = 100%） */
  widthPercent: number;
}

export interface WorkbenchTeamLoadMember extends Omit<WorkbenchTeamLoadMemberSource, 'items'> {
  /** items 合计 */
  inProgress: number;
  /** dueSoon + overdue */
  urgent: number;
  level: WorkbenchTeamLoadLevel;
  /** 项目段（去掉 count = 0） */
  segments: WorkbenchTeamLoadSegment[];
  /** 柱子总长占容器百分比（0-100），溢出时 = 100 */
  barWidthPercent: number;
  /** 溢出数量：inProgress - scaleMax（不溢出 = 0），柱子末端显示 "+N" */
  overflowExtra: number;
}

export interface WorkbenchTeamLoadView {
  weekStart: string;
  /** 已按 level → inProgress desc 排序，高负载置顶 */
  members: WorkbenchTeamLoadMember[];
  /** 柱子量程上限（固定值，避免某个成员极端值把全员压扁） */
  scaleMax: number;
  highCount: number;
  midCount: number;
  /** 临期+逾期总条数（团队聚合） */
  urgentTotal: number;
}

const TEAM_LOAD_INPROGRESS_HIGH = 6;
const TEAM_LOAD_INPROGRESS_MID = 4;
const TEAM_LOAD_URGENT_HIGH = 2;
const TEAM_LOAD_URGENT_MID = 1;
/** 柱子量程上限：固定 10，确保高负载阈值 6 在 60% 位置可辨；超出走 "+N" 文字 */
const TEAM_LOAD_SCALE_MAX = 10;

function resolveTeamLoadLevel(inProgress: number, urgent: number): WorkbenchTeamLoadLevel {
  if (inProgress >= TEAM_LOAD_INPROGRESS_HIGH || urgent >= TEAM_LOAD_URGENT_HIGH) return 'high';
  if (inProgress >= TEAM_LOAD_INPROGRESS_MID || urgent >= TEAM_LOAD_URGENT_MID) return 'mid';
  return 'normal';
}

const LEVEL_RANK: Record<WorkbenchTeamLoadLevel, number> = { high: 0, mid: 1, normal: 2 };

export function buildWorkbenchTeamLoadView(source: WorkbenchTeamLoadSource): WorkbenchTeamLoadView {
  const scaleMax = TEAM_LOAD_SCALE_MAX;

  const enriched: WorkbenchTeamLoadMember[] = source.members.map(m => {
    const inProgress = m.items.reduce((s, it) => s + it.count, 0);
    const urgent = m.dueSoon + m.overdue;
    const barWidthPercent = scaleMax > 0 ? Math.min(100, (inProgress / scaleMax) * 100) : 0;
    const overflowExtra = Math.max(0, inProgress - scaleMax);
    // 段宽永远相对柱子内部按 count/inProgress 算（段总和 = 100%）
    // 柱子总长靠 barWidthPercent 控制；不溢出时柱子未顶满，溢出时柱子顶满 + 末端 +N
    const segments: WorkbenchTeamLoadSegment[] = m.items
      .filter(it => it.count > 0)
      .map(it => ({
        ...it,
        widthPercent: inProgress > 0 ? (it.count / inProgress) * 100 : 0
      }));
    return {
      memberId: m.memberId,
      memberName: m.memberName,
      dueSoon: m.dueSoon,
      overdue: m.overdue,
      inProgress,
      urgent,
      level: resolveTeamLoadLevel(inProgress, urgent),
      segments,
      barWidthPercent,
      overflowExtra
    };
  });

  enriched.sort((a, b) => {
    if (LEVEL_RANK[a.level] !== LEVEL_RANK[b.level]) return LEVEL_RANK[a.level] - LEVEL_RANK[b.level];
    if (a.inProgress !== b.inProgress) return b.inProgress - a.inProgress;
    return b.urgent - a.urgent;
  });

  const highCount = enriched.filter(m => m.level === 'high').length;
  const midCount = enriched.filter(m => m.level === 'mid').length;
  const urgentTotal = enriched.reduce((s, m) => s + m.urgent, 0);

  return {
    weekStart: source.weekStart,
    members: enriched,
    scaleMax,
    highCount,
    midCount,
    urgentTotal
  };
}

export type WorkbenchHealthLevel = 'green' | 'yellow' | 'red';

export interface WorkbenchProjectHealthCardSource {
  projectId: string;
  projectName: string;
  code: string;
  health: WorkbenchHealthLevel;
  riskCount: number;
  overdueTasks: number;
  backlogRequirements: number;
}

export interface WorkbenchProjectHealthCard extends WorkbenchProjectHealthCardSource {
  healthLabel: string;
}

export function buildWorkbenchProjectHealthCards(
  source: readonly WorkbenchProjectHealthCardSource[]
): WorkbenchProjectHealthCard[] {
  const labelMap: Record<WorkbenchHealthLevel, string> = { green: '健康', yellow: '关注', red: '风险' };
  return source.map(s => ({ ...s, healthLabel: labelMap[s.health] }));
}

export interface WorkbenchProgressBarSource {
  projectId: string;
  projectName: string;
  /** 完成率 0-100 */
  weekCompletionRate: number;
}

export type WorkbenchProgressBar = WorkbenchProgressBarSource;

export function buildWorkbenchProgressBars(source: readonly WorkbenchProgressBarSource[]): WorkbenchProgressBar[] {
  return source.map(s => ({ ...s, weekCompletionRate: Math.min(100, Math.max(0, Math.round(s.weekCompletionRate))) }));
}

export interface WorkbenchMyExecutionItemSource {
  id: string;
  executionName: string;
  /** 关联项目 */
  projectId: string;
  projectName: string;
  /** 执行状态编码（projectExecution 域：pending / active / paused / completed / cancelled） */
  statusCode: string;
  /** 状态名（后端字典返回） */
  statusName: string;
  /** 优先级编码（取 RDMS_REQ_PRIORITY_DICT_CODE 字典） */
  priority: string;
  /** 计划起止 */
  plannedStartDate: string | null;
  plannedEndDate: string | null;
  /** 实际起止 */
  actualStartDate: string | null;
  actualEndDate: string | null;
  /** 进度（0-100 整数） */
  progressRate: number;
  /** 关联项目需求 ID（可选） */
  projectRequirementId?: string;
  /** 关联项目需求名称（可选） */
  projectRequirementName?: string;
}

export type WorkbenchMyExecutionItem = WorkbenchMyExecutionItemSource;

/** 过滤掉已完成 / 已取消 / 进度满 100 的执行（默认不在工作台呈现） */
export function buildWorkbenchMyExecutionItems(
  source: readonly WorkbenchMyExecutionItemSource[]
): WorkbenchMyExecutionItem[] {
  return source.filter(item => {
    if (item.statusCode === 'completed' || item.statusCode === 'cancelled') return false;
    if (item.progressRate >= 100) return false;
    return true;
  });
}
