import prisma from '../lib/prisma'
import type { RiskLevel, RiskType } from '@prisma/client'
import { decrypt } from '../lib/crypto'
import { calcIndividualRetireAge, calcRetirementDaysLeft } from './retirement.service'

function daysBetween(a: Date, b: Date): number {
  return Math.floor((a.getTime() - b.getTime()) / (1000 * 60 * 60 * 24))
}

/** 解密员工月薪 */
function safeDecryptSalary(encrypted: string): number {
  try {
    return Number(decrypt(encrypted)) || 0
  } catch {
    return Number(encrypted) || 0
  }
}

/**
 * 社平工资3倍封顶（参照上海2024年度社平工资约12307元/月，3倍≈36921元）
 * 《劳动合同法》第47条：月工资高于社平3倍的按3倍计，且补偿年限最高12年
 */
const SOCIAL_AVG_SALARY_3X = 36921

/**
 * 风险量化：根据风险类型和员工数据估算预估损失金额
 * 依据《劳动合同法》《劳动法》设定法定上限
 * @returns { estimatedLoss, lossRange, deadline }
 */
function estimateRiskCost(
  risk: { type: RiskType; level: RiskLevel; title: string; description: string },
  employee?: { hireDate: Date; monthlySalary?: string } | null,
): { estimatedLoss: number; lossRange: [number, number]; deadline?: Date; lossReason?: string } {
  const today = new Date()
  const rawSalary = employee ? safeDecryptSalary(employee.monthlySalary || '0') : 0
  // 第47条：高收入者月薪封顶为社平3倍
  const salary = Math.min(rawSalary, SOCIAL_AVG_SALARY_3X)
  const workYears = employee
    ? Math.max(0, (today.getFullYear() - employee.hireDate.getFullYear()) +
      (today.getMonth() - employee.hireDate.getMonth()) / 12)
    : 0
  // 经济补偿月数：每满1年1个月，不满半年算0.5个月，第47条上限12年
  const n = Math.min(12, Math.max(0.5, Math.round(workYears * 2) / 2))

  // 根据风险标题关键词判断类型并估算
  const title = risk.title

  // 未签合同（>30天）：双倍工资
  // 《劳动合同法》第82条：超过1个月不满1年未签书面合同，每月支付2倍工资
  // 超过1年视为已订立无固定期限合同，不再适用双倍工资，故最多罚11个月
  if (title.includes('未签合同') && title.includes('天')) {
    const daysMatch = title.match(/(\d+)天/)
    const days = daysMatch ? parseInt(daysMatch[1]) : 0
    const months = Math.min(11, Math.ceil(days / 30)) // 法定上限11个月
    const loss = salary * months
    return {
      estimatedLoss: loss,
      lossRange: [loss * 0.5, loss],
      deadline: new Date(today.getTime() + 7 * 86400000), // 7天内处理
      lossReason: `《劳动合同法》第82条：未签书面合同，每月支付2倍工资。${months}个月×月薪${salary.toFixed(0)}元`,
    }
  }

  // 合同到期未续签：经济补偿 N × 月薪
  // 《劳动合同法》第47条：N≤12个月，月薪≤社平3倍
  if (title.includes('到期') && title.includes('未续签')) {
    const loss = salary * n // n已封顶12
    return {
      estimatedLoss: loss,
      lossRange: [loss, loss * 2], // 可能被认定为违法解除 2N
      deadline: new Date(today.getTime() + 3 * 86400000), // 3天内处理
      lossReason: `《劳动合同法》第47条：经济补偿N×月薪。N=${n}个月×月薪${salary.toFixed(0)}元，违法解除可能2N`,
    }
  }

  // 合同即将到期：提前准备
  if (title.includes('即将到期')) {
    const daysMatch = title.match(/(\d+)天/)
    const days = daysMatch ? parseInt(daysMatch[1]) : 30
    return {
      estimatedLoss: 0,
      lossRange: [0, salary * n],
      deadline: new Date(today.getTime() + days * 86400000),
      lossReason: `到期未续签可能需支付经济补偿，最高N=${n}个月×月薪${salary.toFixed(0)}元`,
    }
  }

  // 试用期超限：工资差额
  if (title.includes('试用期') && title.includes('不合法')) {
    const loss = salary * 0.2 * 1 // 试用期工资差额约20%
    return {
      estimatedLoss: loss,
      lossRange: [loss, loss * 3],
      deadline: new Date(today.getTime() + 14 * 86400000), // 14天内处理
      lossReason: `试用期工资差额约20%：月薪${salary.toFixed(0)}元×20%`,
    }
  }

  // 三期/工伤/医疗期：违法解除风险 2N
  // 《劳动合同法》第87条：违法解除按第47条标准的2倍赔偿，2N≤24个月
  if (title.includes('孕期') || title.includes('哺乳期') || title.includes('工伤') || title.includes('医疗期')) {
    const loss = salary * n * 2 // n已封顶12，故2N≤24
    return {
      estimatedLoss: loss,
      lossRange: [loss, loss * 2],
      deadline: new Date(today.getTime() + 1 * 86400000), // 1天内处理
      lossReason: `《劳动合同法》第87条：违法解除按2N赔偿。2N=${(n * 2).toFixed(1)}个月×月薪${salary.toFixed(0)}元`,
    }
  }

  // 月度任务：无直接损失但可能有滞纳金
  if (title.includes('社保') || title.includes('公积金')) {
    return {
      estimatedLoss: 500,
      lossRange: [100, 2000],
      deadline: new Date(today.getTime() + 3 * 86400000),
      lossReason: '逾期缴纳社保/公积金将产生滞纳金，每日万分之五',
    }
  }

  if (title.includes('工资') || title.includes('个税')) {
    return {
      estimatedLoss: 1000,
      lossRange: [500, 5000],
      deadline: new Date(today.getTime() + 1 * 86400000),
      lossReason: '逾期发放工资/申报个税可能面临行政处罚及赔偿，《劳动法》第91条',
    }
  }

  // 退休提醒：未及时办理退休可能导致多缴社保公积金
  if (title.includes('退休')) {
    const daysMatch = title.match(/(\d+)天/)
    const days = daysMatch ? parseInt(daysMatch[1]) : 0
    return {
      estimatedLoss: days > 0 ? salary * (days / 30) : salary,
      lossRange: [500, salary * 6],
      deadline: new Date(today.getTime() + (days > 0 ? days : 7) * 86400000),
      lossReason: `未及时办理退休可能导致多缴社保公积金，约${(days / 30).toFixed(1)}个月×月薪${salary.toFixed(0)}元`,
    }
  }

  // 默认
  return {
    estimatedLoss: 0,
    lossRange: [0, 0],
  }
}

/**
 * 计算风险优先级
 */
function calcPriority(
  level: RiskLevel,
  estimatedLoss: number,
  deadline?: Date,
): 'URGENT' | 'HIGH' | 'MEDIUM' | 'LOW' {
  const today = new Date()
  today.setHours(0, 0, 0, 0)
  const daysLeft = deadline ? daysBetween(deadline, today) : 999

  if (level === 'HIGH' && (daysLeft <= 3 || estimatedLoss >= 50000)) return 'URGENT'
  if (level === 'HIGH') return 'HIGH'
  if (level === 'MEDIUM' && estimatedLoss >= 10000) return 'HIGH'
  if (level === 'MEDIUM') return 'MEDIUM'
  return 'LOW'
}

/**
 * 格式化时长：不足30天用"X天"，超过30天用"X个月Y天"
 */
function formatDuration(days: number): string {
  if (days < 30) return `${days}天`
  const months = Math.floor(days / 30)
  const remainDays = days % 30
  return remainDays === 0 ? `${months}个月` : `${months}个月${remainDays}天`
}

export async function detectContractRisks(orgId: string) {
  const today = new Date()
  today.setHours(0, 0, 0, 0)
  const employees = await prisma.employee.findMany({
    where: { orgId, status: 'ACTIVE', hireDate: { lte: today } },
    include: { contracts: { orderBy: { createdAt: 'desc' } } },
  })

  const risks: { employeeId: string; type: RiskType; level: RiskLevel; title: string; description: string; actionUrl: string }[] = []

  for (const emp of employees) {
    const latestContract = emp.contracts[0]

    if (!latestContract || latestContract.contractType === 'UNSIGNED') {
      const days = daysBetween(new Date(), emp.hireDate)
      if (days > 365) {
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT',
          level: 'HIGH',
          title: `${emp.name}入职${formatDuration(days)}未签合同，已视为无固定期限`,
          description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)}，超过1年未签订书面合同，法律上已视为无固定期限劳动合同。`,
          actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
        })
      } else if (days > 30) {
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT',
          level: 'HIGH',
          title: `${emp.name}入职${formatDuration(days)}未签合同`,
          description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)}，超过30天未签订书面合同，需尽快补签。`,
          actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
        })
      } else {
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT',
          level: 'LOW',
          title: `${emp.name}入职${formatDuration(days)}，尚未签合同`,
          description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)}，30天内需签订书面合同。`,
          actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
        })
      }
      continue
    }

    if (latestContract.endDate) {
      const daysToExpire = daysBetween(latestContract.endDate, new Date())
      if (daysToExpire < 0) {
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT',
          level: 'HIGH',
          title: `${emp.name}的合同已到期${Math.abs(daysToExpire)}天未续签`,
          description: `合同到期日 ${latestContract.endDate.toISOString().slice(0, 10)}，已过期未续签。`,
          actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
        })
      } else if (daysToExpire <= 30) {
        // 30 天内到期：HIGH，需立即处理
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT',
          level: 'HIGH',
          title: `${emp.name}的合同即将到期（${daysToExpire}天）`,
          description: `合同到期日 ${latestContract.endDate.toISOString().slice(0, 10)}，30天内到期需立即准备续签或终止。`,
          actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
        })
      } else if (daysToExpire <= 60) {
        // 31-60 天到期：MEDIUM，提前预警给 HR 反应时间
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT',
          level: 'MEDIUM',
          title: `${emp.name}的合同将于${daysToExpire}天后到期`,
          description: `合同到期日 ${latestContract.endDate.toISOString().slice(0, 10)}，建议提前准备续签或终止。`,
          actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
        })
      }
    }

    if (latestContract.probationMonths > 0) {
      const contractMonths = latestContract.endDate
        ? Math.ceil(daysBetween(latestContract.endDate, latestContract.startDate) / 30.44)
        : 36
      let maxProbation = 0
      if (contractMonths >= 36) maxProbation = 6
      else if (contractMonths >= 12) maxProbation = 2
      else if (contractMonths >= 3) maxProbation = 1

      if (latestContract.probationMonths > maxProbation) {
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT',
          level: 'MEDIUM',
          title: `${emp.name}试用期${latestContract.probationMonths}个月可能不合法`,
          description: `${contractMonths}个月合同试用期最多${maxProbation}个月，当前${latestContract.probationMonths}个月超出法定上限。`,
          actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
        })
      }
    }
  }

  return risks
}

// 预入职检查：入职日期已到但未签合同 → 待办
export async function detectOnboardingRisks(orgId: string) {
  const today = new Date()
  today.setHours(0, 0, 0, 0)
  const employees = await prisma.employee.findMany({
    where: { orgId, status: 'ACTIVE', hireDate: { lte: today } },
    include: {
      contracts: { orderBy: { createdAt: 'desc' }, take: 1 },
      terminations: { where: { terminationDate: { lte: today }, status: 'COMPLETED' }, take: 1 },
    },
  })

  const risks: { employeeId: string; type: RiskType; level: RiskLevel; title: string; description: string; actionUrl: string }[] = []

  for (const emp of employees) {
    // 已离职的跳过
    if (emp.terminations.length > 0) continue

    const latestContract = emp.contracts[0]
    const hasSignedContract = latestContract && latestContract.contractType !== 'UNSIGNED'

    if (!hasSignedContract) {
      const daysSinceHire = daysBetween(today, emp.hireDate)
      risks.push({
        employeeId: emp.id,
        type: 'ONBOARDING',
        level: daysSinceHire > 30 ? 'HIGH' : 'MEDIUM',
        title: `${emp.name}入职手续未完成${daysSinceHire > 30 ? `（已超${formatDuration(Math.round(daysSinceHire))}）` : ''}`,
        description: `入职日期 ${emp.hireDate.toISOString().slice(0, 10)}，尚未签订劳动合同，请尽快完成入职手续。`,
        actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
      })
    }
  }

  return risks
}

export async function detectTerminationRisks(orgId: string) {
  // 三期/医疗期/工伤员工的解聘限制为被动检查，在 termination.service.ts 中已实现
  // 不再作为主动风险推送到工作台
  return []
}

/**
 * 检测特殊状态相关风险（三期/工伤/医疗期员工合同到期不得终止）
 */
async function detectSpecialStatusRisks(orgId: string) {
  const today = new Date()
  today.setHours(0, 0, 0, 0)

  // 查找所有进行中的特殊状态记录
  const specialStatuses = await (prisma as any).employeeSpecialStatus.findMany({
    where: { orgId, status: 'ACTIVE' },
    include: {
      employee: {
        select: { id: true, name: true, status: true, contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
      },
    },
  })

  const risks: { employeeId: string; type: RiskType; level: RiskLevel; title: string; description: string; actionUrl: string; deadline?: Date }[] = []

  for (const ss of specialStatuses) {
    const emp = ss.employee
    if (!emp || emp.status !== 'ACTIVE') continue
    const latestContract = emp.contracts[0]
    if (!latestContract || !latestContract.endDate) continue

    const daysToExpire = daysBetween(latestContract.endDate, today)
    const typeLabel = ss.type === 'PREGNANCY' ? '三期' : ss.type === 'WORK_INJURY' ? '工伤' : ss.type === 'MEDICAL_PERIOD' ? '医疗期' : '特殊状态'

    // 合同即将到期但处于特殊状态期间，不得终止
    if (daysToExpire >= 0 && daysToExpire <= 60) {
      risks.push({
        employeeId: emp.id,
        type: 'CONTRACT' as RiskType,
        level: 'HIGH' as RiskLevel,
        title: `${emp.name}处于${typeLabel}期间，合同${daysToExpire}天后到期，依法不得终止`,
        description: `${typeLabel}员工在合同到期时，用人单位不得依照《劳动合同法》第四十条、第四十一条终止合同，需顺延至相应情形消失。`,
        actionUrl: `/special-status?type=${ss.type}`,
        deadline: latestContract.endDate,
      })
    }

    // 工伤认定超期提醒（受伤30天内未认定）
    if (ss.type === 'WORK_INJURY' && ss.injuryDate && !ss.certificationDate) {
      const daysSinceInjury = daysBetween(today, new Date(ss.injuryDate))
      if (daysSinceInjury > 30) {
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT' as RiskType,
          level: 'HIGH' as RiskLevel,
          title: `${emp.name}工伤已${daysSinceInjury}天未完成认定，超30天限期`,
          description: `用人单位应自事故伤害发生之日起30日内提出工伤认定申请，逾期将影响工伤待遇。`,
          actionUrl: `/special-status?type=WORK_INJURY`,
        })
      }
    }

    // 医疗期即将到期
    if (ss.type === 'MEDICAL_PERIOD' && ss.medicalPeriodEnd) {
      const daysToMedicalEnd = daysBetween(new Date(ss.medicalPeriodEnd), today)
      if (daysToMedicalEnd >= 0 && daysToMedicalEnd <= 30) {
        risks.push({
          employeeId: emp.id,
          type: 'CONTRACT' as RiskType,
          level: daysToMedicalEnd <= 7 ? 'HIGH' as RiskLevel : 'MEDIUM' as RiskLevel,
          title: `${emp.name}医疗期${daysToMedicalEnd}天后到期，需提前处理`,
          description: `医疗期届满后，员工仍需治疗的，用人单位应按规定处理。医疗期满不能从事原工作的，可依法解除合同但需支付经济补偿。`,
          actionUrl: `/special-status?type=MEDICAL_PERIOD`,
        })
      }
    }
  }

  return risks
}

export async function detectMonthlyTasks(orgId: string) {
  const setting = await prisma.notificationSetting.findUnique({ where: { orgId } })
  if (!setting) return []

  const now = new Date()
  const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
  const today = now.getDate()

  // 工资和个税仍用全局通知设置
  const globalTasks = [
    { day: setting.payrollDay, title: `${currentMonth}月 发放工资`, desc: `每月${setting.payrollDay}日前完成工资发放`, url: '/money?tab=batch' },
    { day: setting.taxDay, title: `${currentMonth}月 申报个税`, desc: `每月${setting.taxDay}日前完成个税申报`, url: '/money?tab=batch' },
  ]

  const risks: { employeeId: null; type: RiskType; level: RiskLevel; title: string; description: string; actionUrl: string; deadline?: Date }[] = []

  for (const task of globalTasks) {
    if (today >= task.day) {
      const deadline = new Date(now.getFullYear(), now.getMonth(), task.day)
      risks.push({
        employeeId: null,
        type: 'MONTHLY',
        level: today > task.day + 3 ? 'HIGH' : 'MEDIUM',
        title: task.title,
        description: task.desc,
        actionUrl: task.url,
        deadline,
      })
    }
  }

  // 社保和公积金按账户 paymentDay 生成提醒，未设置时回退到组织 socialInsCutoffDay
  const org = await prisma.organization.findUnique({ where: { id: orgId }, select: { socialInsCutoffDay: true } })
  const defaultCutoffDay = org?.socialInsCutoffDay || 15
  const accounts = await prisma.socialAccount.findMany({
    where: { orgId, status: 'ACTIVE' },
  })
  for (const acc of accounts) {
    const day = acc.paymentDay || defaultCutoffDay
    if (today >= day) {
      const deadline = new Date(now.getFullYear(), now.getMonth(), day)
      const typeLabel = acc.type === 'SOCIAL' ? '社保' : '公积金'
      risks.push({
        employeeId: null,
        type: 'MONTHLY',
        level: today > day + 3 ? 'HIGH' : 'MEDIUM',
        title: `${currentMonth}月 缴纳${typeLabel}（${acc.name}）`,
        description: `账户「${acc.name}」每月${day}日前完成${typeLabel}缴纳`,
        actionUrl: '/social?tab=monthly',
        deadline,
      })
    }
  }

  // 工资条生成提醒：当月有已归档批次时提醒生成工资条
  const archivedBatches = await prisma.payrollBatch.count({
    where: { orgId, month: currentMonth, status: 'ARCHIVED' },
  })
  if (archivedBatches > 0) {
    risks.push({
      employeeId: null,
      type: 'SALARY',
      level: 'MEDIUM',
      title: `${currentMonth}月 生成工资条`,
      description: `本月有 ${archivedBatches} 个已归档工资批次，请前往工资条管理汇总生成工资条`,
      actionUrl: '/money',
    })
  }

  return risks
}

/**
 * 退休提醒：检测即将退休的员工（距退休180天内）
 */
export async function detectRetirementRisks(orgId: string) {
  const employees = await prisma.employee.findMany({
    where: { orgId, status: 'ACTIVE', birthDate: { not: null } },
    include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
  })

  const risks: { employeeId: string; type: RiskType; level: RiskLevel; title: string; description: string; actionUrl: string }[] = []

  for (const emp of employees) {
    // 只对最新合同为正式劳动合同的员工进行退休检测
    const latestContractType = emp.contracts[0]?.contractType
    if (!latestContractType || !['FIXED', 'UNFIXED'].includes(latestContractType)) continue
    if (!emp.birthDate) continue
    const gender = emp.gender || '男'
    const fwt = emp.femaleWorkerType
    const bd = new Date(emp.birthDate)
    const { retireDate } = calcIndividualRetireAge(bd, gender, fwt)
    const daysLeft = calcRetirementDaysLeft(bd, gender, fwt) ?? 0

    if (daysLeft <= 180 && daysLeft > 0) {
      risks.push({
        employeeId: emp.id,
        type: 'RETIREMENT',
        level: daysLeft <= 30 ? 'HIGH' : 'MEDIUM',
        title: `${emp.name}距退休仅剩${daysLeft}天`,
        description: `${gender === '女' ? (fwt === 'WORKER' ? '女工人' : '女干部') : '男'}，出生日期 ${emp.birthDate.toISOString().slice(0, 10)}，预计退休日期 ${retireDate.toISOString().slice(0, 10)}。请提前准备退休手续。`,
        actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
      })
    } else if (daysLeft <= 0) {
      risks.push({
        employeeId: emp.id,
        type: 'RETIREMENT',
        level: 'HIGH',
        title: `${emp.name}已达退休年龄`,
        description: `${gender === '女' ? (fwt === 'WORKER' ? '女工人' : '女干部') : '男'}，出生日期 ${emp.birthDate.toISOString().slice(0, 10)}，已超过退休日期 ${retireDate.toISOString().slice(0, 10)}。请尽快办理退休手续。`,
        actionUrl: `/roster?employee=${encodeURIComponent(emp.name)}`,
      })
    }
  }

  return risks
}

export async function runRiskDetection(orgId: string) {
  // 清理重复 PENDING 风险：同一 employeeId:type 只保留最新一条 PENDING
  const pendingRisks = await prisma.riskItem.findMany({
    where: { orgId, status: 'PENDING', employeeId: { not: null } },
    select: { id: true, employeeId: true, type: true, createdAt: true },
    orderBy: { createdAt: 'desc' },
  })
  const seenKeys = new Set<string>()
  const duplicateIds: string[] = []
  for (const r of pendingRisks) {
    const key = `${r.employeeId}:${r.type}`
    if (seenKeys.has(key)) {
      duplicateIds.push(r.id)
    } else {
      seenKeys.add(key)
    }
  }
  if (duplicateIds.length > 0) {
    await prisma.riskItem.updateMany({
      where: { id: { in: duplicateIds } },
      data: { status: 'RESOLVED', resolvedAt: new Date() },
    })
  }

  // 非月度风险去重：检查所有状态（含 RESOLVED/IGNORED），避免已处理的风险被重新创建
  // 按 employeeId:type 归并，不依赖 actionUrl（actionUrl 可能因天数变化而不同）
  const existingRisks = await prisma.riskItem.findMany({
    where: { orgId, status: { in: ['PENDING', 'RESOLVED', 'IGNORED'] } },
    select: { id: true, employeeId: true, type: true, title: true, status: true, actionUrl: true },
  })
  const existingKeys = new Set(existingRisks.map((r: typeof existingRisks[number]) => `${r.employeeId}:${r.type}`))
  // PENDING 风险的 employeeId:type → record 映射，用于更新标题
  const pendingRiskMap = new Map<string, typeof existingRisks[number]>()
  for (const r of existingRisks) {
    if (r.status === 'PENDING' && r.employeeId) {
      pendingRiskMap.set(`${r.employeeId}:${r.type}`, r)
    }
  }

  // 当月任务去重：检查所有状态（含 RESOLVED/IGNORED），避免已完成的当月任务被重新创建
  const currentMonth = `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}`
  const monthlyExisting = await prisma.riskItem.findMany({
    where: { orgId, title: { startsWith: `${currentMonth}月` } },
    select: { employeeId: true, title: true },
  })
  const monthlyKeys = new Set(monthlyExisting.map((r: typeof monthlyExisting[number]) => `${r.employeeId}:${r.title}`))

  const contractRisks = await detectContractRisks(orgId)
  const terminationRisks = await detectTerminationRisks(orgId)
  const onboardingRisks = await detectOnboardingRisks(orgId)
  const monthlyTasks = await detectMonthlyTasks(orgId)
  const specialStatusRisks = await detectSpecialStatusRisks(orgId)
  const retirementRisks = await detectRetirementRisks(orgId)

  // 去重：contractRisks 已覆盖"未签合同"，onboardingRisks 中相同员工不再重复生成
  const contractEmployeeIds = new Set(contractRisks.map(r => r.employeeId))
  const filteredOnboardingRisks = onboardingRisks.filter(r => !contractEmployeeIds.has(r.employeeId))

  // 获取所有相关员工数据用于风险量化
  const allEmployeeIds = [...contractRisks, ...terminationRisks, ...filteredOnboardingRisks, ...specialStatusRisks, ...retirementRisks]
    .map(r => r.employeeId)
    .filter(Boolean) as string[]
  const employees = allEmployeeIds.length > 0
    ? await prisma.employee.findMany({ where: { id: { in: allEmployeeIds } } })
    : []
  const empMap = new Map(employees.map(e => [e.id, e]))

  // 月度任务用 monthlyKeys 去重，其他任务用 existingKeys 去重
  const nonMonthlyRisks = [...contractRisks, ...terminationRisks, ...filteredOnboardingRisks, ...specialStatusRisks, ...retirementRisks]
  const toCreate = [
    ...nonMonthlyRisks.filter((r) => !existingKeys.has(`${r.employeeId}:${r.type}`)),
    ...monthlyTasks.filter((r) => !monthlyKeys.has(`${r.employeeId}:${r.title}`)),
  ]

  // 更新已存在的 PENDING 风险：标题/量化信息/actionUrl 可能因天数变化或代码更新而变化
  const toUpdate: { id: string; title: string; description: string; actionUrl: string; estimatedLoss: number; lossRange: [number, number]; deadline?: Date }[] = []
  for (const r of nonMonthlyRisks) {
    const key = `${r.employeeId}:${r.type}`
    const existing = pendingRiskMap.get(key)
    if (existing && (existing.title !== r.title || existing.actionUrl !== r.actionUrl)) {
      const emp = r.employeeId ? empMap.get(r.employeeId) : null
      const cost = estimateRiskCost(r, emp)
      toUpdate.push({
        id: existing.id,
        title: r.title,
        description: r.description,
        actionUrl: r.actionUrl,
        estimatedLoss: cost.estimatedLoss,
        lossRange: cost.lossRange,
        deadline: cost.deadline,
      })
    }
  }
  for (const u of toUpdate) {
    await prisma.riskItem.update({
      where: { id: u.id },
      data: {
        title: u.title,
        description: u.description,
        actionUrl: u.actionUrl,
        estimatedLoss: u.estimatedLoss,
        lossRange: u.lossRange,
        deadline: u.deadline,
      },
    })
  }

  // 更新 MONTHLY 类型 PENDING 记录的 actionUrl（标题不变但 actionUrl 可能因代码更新而变化）
  const monthlyPending = existingRisks.filter((r: typeof existingRisks[number]) => r.status === 'PENDING' && r.type === 'MONTHLY')
  const monthlyActionUrlMap = new Map(monthlyTasks.map((r) => [r.title, r.actionUrl]))
  for (const r of monthlyPending) {
    const newUrl = monthlyActionUrlMap.get(r.title)
    if (newUrl && r.actionUrl !== newUrl) {
      await prisma.riskItem.update({
        where: { id: r.id },
        data: { actionUrl: newUrl },
      })
    }
  }

  if (toCreate.length > 0) {
    // 为每个风险计算量化信息
    const createData = toCreate.map((r) => {
      const emp = r.employeeId ? empMap.get(r.employeeId) : null
      const cost = estimateRiskCost(r, emp)
      return {
        orgId,
        employeeId: r.employeeId,
        type: r.type,
        level: r.level,
        title: r.title,
        description: r.description,
        actionUrl: r.actionUrl,
        estimatedLoss: cost.estimatedLoss,
        lossRange: cost.lossRange,
        deadline: (r as any).deadline || cost.deadline,
      }
    })
    await prisma.riskItem.createMany({ data: createData })
  }

  // 自动消除：PENDING 风险在最新检测结果中不再出现，说明问题已自然解决
  // 例如：员工补签了合同，则旧的"未签合同"风险应自动标记为 RESOLVED
  const newRiskKeys = new Set(nonMonthlyRisks.map((r) => `${r.employeeId}:${r.type}`))
  const toResolve: string[] = []
  for (const r of existingRisks) {
    if (r.status === 'PENDING' && r.employeeId) {
      const key = `${r.employeeId}:${r.type}`
      if (!newRiskKeys.has(key)) {
        toResolve.push(r.id)
      }
    }
  }
  if (toResolve.length > 0) {
    await prisma.riskItem.updateMany({
      where: { id: { in: toResolve } },
      data: { status: 'RESOLVED', resolvedAt: new Date() },
    })
  }

  return toCreate.length
}

export async function getDashboardData(orgId: string) {
  await runRiskDetection(orgId)

  const now = new Date()
  const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
  const monthStart = new Date(now.getFullYear(), now.getMonth(), 1)
  const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59)
  const yearStart = new Date(now.getFullYear(), 0, 1)
  const yearEnd = new Date(now.getFullYear(), 11, 31, 23, 59, 59)

  const [
    employeeCount, _highRisks, _pendingRisks, riskItems, resolvedItems,
    overtimeRecords, payslips, batchEntries, socialConfig, housingConfig,
    monthContracts, monthTerminations, monthDisciplinary, monthAttendance,
    monthSeverancePay,
    yearBatchEntries, yearOvertimeRecords, yearSeverancePay,
  ] = await Promise.all([
    prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
    prisma.riskItem.count({ where: { orgId, status: 'PENDING', level: 'HIGH', type: { in: ['CONTRACT', 'TERMINATION'] } } }),
    prisma.riskItem.count({ where: { orgId, status: 'PENDING' } }),
    prisma.riskItem.findMany({
      where: { orgId, status: 'PENDING' },
      include: { employee: { select: { id: true, name: true, department: true, idCardHash: true, idCardNumber: true } } },
      orderBy: [{ level: 'asc' }, { estimatedLoss: 'desc' }],
    }),
    prisma.riskItem.findMany({
      where: { orgId, status: 'RESOLVED' },
      include: { employee: true },
      orderBy: { resolvedAt: 'desc' },
      take: 10,
    }),
    prisma.overtimeRecord.findMany({
      where: { orgId, month: currentMonth },
      select: { totalPay: true, weekdayHours: true, weekendHours: true, holidayHours: true },
    }),
    prisma.payslip.findMany({
      where: { orgId, month: currentMonth },
      select: { baseSalary: true, overtimePay: true, allowance: true, deduction: true, totalPay: true, confirmedAt: true },
    }),
    // 已归档批次的条目（用于总览汇总）
    prisma.batchEntry.findMany({
      where: { orgId, batch: { month: currentMonth, status: 'ARCHIVED' } },
      select: { baseSalary: true, overtimePay: true, allowance: true, deduction: true, bonus: true, totalPay: true, socialEmp: true, socialOrg: true, housingEmp: true, housingOrg: true, tax: true, netPay: true, employeeId: true },
    }),
    // 社保/公积金配置：优先查 SocialYearStandard（新架构），回退到旧表
    prisma.socialYearStandard.findFirst({ where: { orgId, isCurrent: true, account: { type: 'SOCIAL' } } }).then(async r => r as any || (await prisma.socialInsuranceConfig.findFirst({ where: { orgId, isCurrent: true } }) as any)),
    prisma.socialYearStandard.findFirst({ where: { orgId, isCurrent: true, account: { type: 'HOUSING' } } }).then(async r => r as any || (await prisma.housingFundConfig.findFirst({ where: { orgId, isCurrent: true } }) as any)),
    prisma.laborContract.count({
      where: { orgId, createdAt: { gte: monthStart, lte: monthEnd } },
    }),
    prisma.terminationRecord.count({
      where: { orgId, createdAt: { gte: monthStart, lte: monthEnd }, status: { notIn: ['CANCELLED', 'DRAFT'] } },
    }),
    prisma.disciplinaryRecord.count({
      where: { orgId, violationDate: { gte: monthStart, lte: monthEnd } },
    }),
    prisma.attendanceRecord.count({
      where: { orgId, date: { gte: monthStart, lte: monthEnd } },
    }),
    prisma.terminationRecord.aggregate({
      where: { orgId, createdAt: { gte: monthStart, lte: monthEnd }, status: { notIn: ['CANCELLED', 'DRAFT'] } },
      _sum: { compensation: true },
    }),
    // 年度累计：已归档批次条目
    prisma.batchEntry.findMany({
      where: { orgId, batch: { month: { startsWith: `${now.getFullYear()}-` }, status: 'ARCHIVED' } },
      select: { baseSalary: true, overtimePay: true, allowance: true, deduction: true, bonus: true, totalPay: true, socialEmp: true, socialOrg: true, housingEmp: true, housingOrg: true, tax: true, netPay: true, employeeId: true },
    }),
    // 年度累计：加班记录
    prisma.overtimeRecord.findMany({
      where: { orgId, month: { startsWith: `${now.getFullYear()}-` } },
      select: { totalPay: true, weekdayHours: true, weekendHours: true, holidayHours: true },
    }),
    // 年度累计：补偿金
    prisma.terminationRecord.aggregate({
      where: { orgId, createdAt: { gte: yearStart, lte: yearEnd }, status: { notIn: ['CANCELLED', 'DRAFT'] } },
      _sum: { compensation: true },
    }),
  ])

  const monthlyOvertimePay = overtimeRecords.reduce((sum: number, r: typeof overtimeRecords[number]) => sum + r.totalPay, 0)

  // 本月薪税汇总：优先从已归档批次汇总，无归档批次则用工资条数据
  const archivedEntries = batchEntries
  const useArchivedData = archivedEntries.length > 0

  let totalBaseSalary: number, totalOvertimePay: number, totalAllowance: number, totalDeduction: number, totalPay: number
  let totalSocialOrg: number, totalSocialEmp: number, totalHousingOrg: number, totalHousingEmp: number, totalTax: number, totalNetPay: number
  let payslipCount: number, confirmedPayslips: number

  if (useArchivedData) {
    // 从已归档批次条目汇总（同一员工多批次的金额累加）
    const empMap = new Map<string, any>()
    for (const e of archivedEntries) {
      const ex = empMap.get(e.employeeId) || { baseSalary: 0, overtimePay: 0, allowance: 0, deduction: 0, bonus: 0, totalPay: 0, socialEmp: 0, socialOrg: 0, housingEmp: 0, housingOrg: 0, tax: 0, netPay: 0 }
      ex.baseSalary += e.baseSalary
      ex.overtimePay += e.overtimePay
      ex.allowance += e.allowance
      ex.deduction += e.deduction
      ex.bonus += e.bonus
      ex.totalPay += e.totalPay
      ex.socialEmp += e.socialEmp
      ex.socialOrg += e.socialOrg
      ex.housingEmp += e.housingEmp
      ex.housingOrg += e.housingOrg
      ex.tax += e.tax
      ex.netPay += e.netPay
      empMap.set(e.employeeId, ex)
    }
    const summary = Array.from(empMap.values())
    totalBaseSalary = summary.reduce((s, e) => s + e.baseSalary, 0)
    totalOvertimePay = summary.reduce((s, e) => s + e.overtimePay, 0)
    totalAllowance = summary.reduce((s, e) => s + e.allowance, 0)
    totalDeduction = summary.reduce((s, e) => s + e.deduction, 0)
    totalPay = summary.reduce((s, e) => s + e.totalPay, 0)
    totalSocialOrg = summary.reduce((s, e) => s + e.socialOrg, 0)
    totalSocialEmp = summary.reduce((s, e) => s + e.socialEmp, 0)
    totalHousingOrg = summary.reduce((s, e) => s + e.housingOrg, 0)
    totalHousingEmp = summary.reduce((s, e) => s + e.housingEmp, 0)
    totalTax = summary.reduce((s, e) => s + e.tax, 0)
    totalNetPay = summary.reduce((s, e) => s + e.netPay, 0)
    payslipCount = summary.length
    confirmedPayslips = payslips.filter((p: typeof payslips[number]) => p.confirmedAt).length
  } else {
    // fallback：从工资条表汇总
    totalBaseSalary = payslips.reduce((s: number, p: typeof payslips[number]) => s + p.baseSalary, 0)
    totalOvertimePay = payslips.reduce((s: number, p: typeof payslips[number]) => s + p.overtimePay, 0)
    totalAllowance = payslips.reduce((s: number, p: typeof payslips[number]) => s + p.allowance, 0)
    totalDeduction = payslips.reduce((s: number, p: typeof payslips[number]) => s + p.deduction, 0)
    totalPay = payslips.reduce((s: number, p: typeof payslips[number]) => s + p.totalPay, 0)
    totalSocialOrg = 0
    totalSocialEmp = 0
    totalHousingOrg = 0
    totalHousingEmp = 0
    totalTax = 0
    totalNetPay = 0
    payslipCount = payslips.length
    confirmedPayslips = payslips.filter((p: typeof payslips[number]) => p.confirmedAt).length
  }

  // 社保公积金：优先用归档批次的实际计算值，否则估算
  let socialOrgTotal = 0
  let socialEmpTotal = 0
  let housingOrgTotal = 0
  let housingEmpTotal = 0
  if (useArchivedData) {
    socialOrgTotal = totalSocialOrg
    socialEmpTotal = totalSocialEmp
    housingOrgTotal = totalHousingOrg
    housingEmpTotal = totalHousingEmp
  } else if (socialConfig && employeeCount > 0 && payslips.length > 0) {
    // 用平均工资作为估算基数（仅当有工资条数据时才估算）
    const avgBase = employeeCount > 0 ? Math.max(socialConfig.baseMin, Math.min(socialConfig.baseMax, totalBaseSalary / Math.max(employeeCount, 1))) : socialConfig.baseMin
    socialOrgTotal = avgBase * (socialConfig.pensionOrg + socialConfig.medicalOrg + socialConfig.unemploymentOrg + socialConfig.injuryOrg + socialConfig.maternityOrg) / 100 * employeeCount
    socialEmpTotal = avgBase * (socialConfig.pensionEmp + socialConfig.medicalEmp + socialConfig.unemploymentEmp) / 100 * employeeCount
    housingOrgTotal = avgBase * (housingConfig?.housingOrg ?? 0) / 100 * employeeCount
    housingEmpTotal = avgBase * (housingConfig?.housingEmp ?? 0) / 100 * employeeCount
  }

  // 个税：优先用归档批次的实际计算值，否则估算
  let estimatedTax = 0
  if (useArchivedData) {
    estimatedTax = totalTax
  } else {
    const taxableIncome = Math.max(0, totalPay - 5000 * payslips.length - socialEmpTotal - housingEmpTotal)
    if (taxableIncome <= 3000) estimatedTax = taxableIncome * 0.03
    else if (taxableIncome <= 12000) estimatedTax = 3000 * 0.03 + (taxableIncome - 3000) * 0.1
    else if (taxableIncome <= 25000) estimatedTax = 3000 * 0.03 + 9000 * 0.1 + (taxableIncome - 12000) * 0.2
    else if (taxableIncome <= 35000) estimatedTax = 3000 * 0.03 + 9000 * 0.1 + 13000 * 0.2 + (taxableIncome - 25000) * 0.25
    else if (taxableIncome <= 55000) estimatedTax = 3000 * 0.03 + 9000 * 0.1 + 13000 * 0.2 + 10000 * 0.25 + (taxableIncome - 35000) * 0.3
    else if (taxableIncome <= 80000) estimatedTax = 3000 * 0.03 + 9000 * 0.1 + 13000 * 0.2 + 10000 * 0.25 + 20000 * 0.3 + (taxableIncome - 55000) * 0.35
    else estimatedTax = 3000 * 0.03 + 9000 * 0.1 + 13000 * 0.2 + 10000 * 0.25 + 20000 * 0.3 + 25000 * 0.35 + (taxableIncome - 80000) * 0.45
  }

  const payrollSummary = {
    month: currentMonth,
    employeeCount,
    payslipCount,
    confirmedPayslips,
    unconfirmedPayslips: payslipCount - confirmedPayslips,
    baseSalary: totalBaseSalary,
    overtimePay: totalOvertimePay,
    allowance: totalAllowance,
    deduction: totalDeduction,
    totalPay,
    socialOrg: socialOrgTotal,
    socialEmp: socialEmpTotal,
    housingOrg: housingOrgTotal,
    housingEmp: housingEmpTotal,
    estimatedTax,
    severancePay: monthSeverancePay._sum.compensation || 0,
    // 企业总成本 = 工资总额 + 企业社保 + 企业公积金 + 加班费 + 经济补偿金
    orgTotalCost: totalPay + socialOrgTotal + housingOrgTotal + monthlyOvertimePay + (monthSeverancePay._sum.compensation || 0),
    // 员工实发 = 工资总额 - 个人社保 - 个人公积金 - 个税
    empNetPay: useArchivedData ? totalNetPay : totalPay - socialEmpTotal - housingEmpTotal - estimatedTax,
  }

  // 本月工作动态
  const monthlyActivities = {
    month: currentMonth,
    newContracts: monthContracts,
    terminations: monthTerminations,
    disciplinaryActions: monthDisciplinary,
    attendanceRecords: monthAttendance,
    overtimeHours: overtimeRecords.reduce((s: number, r: typeof overtimeRecords[number]) => s + r.weekdayHours + r.weekendHours + r.holidayHours, 0),
    overtimePay: monthlyOvertimePay,
  }

  // 年度累计人力资源成本 = 已归档批次汇总 + 当前月数据（当前月可能未归档）
  const yearArchivedPay = yearBatchEntries.reduce((s: number, e: typeof yearBatchEntries[number]) => s + e.totalPay, 0)
  const yearArchivedSocialOrg = yearBatchEntries.reduce((s: number, e: typeof yearBatchEntries[number]) => s + e.socialOrg, 0)
  const yearArchivedHousingOrg = yearBatchEntries.reduce((s: number, e: typeof yearBatchEntries[number]) => s + e.housingOrg, 0)
  const yearOvertimePay = yearOvertimeRecords.reduce((s: number, r: typeof yearOvertimeRecords[number]) => s + r.totalPay, 0)
  const yearSeverance = yearSeverancePay._sum.compensation || 0

  // 当前月是否已包含在归档批次中
  const currentMonthArchived = batchEntries.length > 0
  const yearTotalPay = yearArchivedPay + (currentMonthArchived ? 0 : totalPay)
  const yearSocialOrg = yearArchivedSocialOrg + (currentMonthArchived ? 0 : socialOrgTotal)
  const yearHousingOrg = yearArchivedHousingOrg + (currentMonthArchived ? 0 : housingOrgTotal)

  const yearCostSummary = {
    year: now.getFullYear().toString(),
    totalPay: yearTotalPay,
    socialOrg: yearSocialOrg,
    housingOrg: yearHousingOrg,
    overtimePay: yearOvertimePay,
    severancePay: yearSeverance,
    orgTotalCost: yearTotalPay + yearSocialOrg + yearHousingOrg + yearOvertimePay + yearSeverance,
  }

  // 为所有风险项计算优先级和量化信息
  const todosWithCost = riskItems.map((r: typeof riskItems[number]) => {
    const estimatedLoss = r.estimatedLoss || 0
    const priority = calcPriority(r.level, estimatedLoss, r.deadline || undefined)
    const daysUntilDeadline = r.deadline ? daysBetween(r.deadline, new Date()) : null
    // 动态计算损失理由
    const costInfo = estimateRiskCost({ type: r.type, level: r.level, title: r.title, description: r.description }, null)
    return {
      id: r.id,
      type: r.type as 'CONTRACT' | 'SALARY' | 'TERMINATION' | 'MONTHLY' | 'ONBOARDING' | 'RETIREMENT',
      level: r.level.toLowerCase() as 'high' | 'medium' | 'low',
      priority,
      title: r.title,
      description: r.description,
      actionUrl: r.actionUrl || '/',
      estimatedLoss,
      lossRange: r.lossRange as [number, number] | null,
      lossReason: costInfo.lossReason || null,
      deadline: r.deadline?.toISOString().slice(0, 10) || null,
      daysUntilDeadline,
      employeeName: r.employee?.name || null,
      employeeDepartment: r.employee?.department || null,
      employeeIdCardHash: r.employee?.idCardHash || null,
      employeeIdCardNumber: r.employee?.idCardNumber ? (() => { try { return r.employee.idCardNumber.includes(':') ? decrypt(r.employee.idCardNumber) : r.employee.idCardNumber } catch { return null } })() : null,
    }
  })

  // 按员工 + 风险类别去重：同一员工同一类别只保留 estimatedLoss 最大的一条
  function getTodoRiskCategoryKey(title: string): string {
    if (title.includes('未签合同') && title.includes('无固定期限')) return 'CONTRACT_UNSIGNED_OVER_YEAR'
    if (title.includes('未签合同') && title.includes('个月')) return 'CONTRACT_UNSIGNED'
    if (title.includes('到期') && title.includes('未续签')) return 'CONTRACT_EXPIRED'
    if (title.includes('即将到期')) return 'CONTRACT_EXPIRING'
    if (title.includes('试用期')) return 'CONTRACT_PROBATION'
    if (title.includes('入职手续未完成')) return 'ONBOARDING_INCOMPLETE'
    if (title.includes('孕期') || title.includes('哺乳期')) return 'SPECIAL_PREGNANCY'
    if (title.includes('工伤')) return 'SPECIAL_INJURY'
    if (title.includes('医疗期')) return 'SPECIAL_MEDICAL'
    if (title.includes('社保')) return 'MONTHLY_SOCIAL'
    if (title.includes('公积金')) return 'MONTHLY_HOUSING'
    if (title.includes('工资')) return 'MONTHLY_PAYROLL'
    if (title.includes('个税')) return 'MONTHLY_TAX'
    if (title.includes('退休')) return 'RETIREMENT'
    return title.replace(/\d+/g, '').trim()
  }

  const dedupedTodoMap = new Map<string, typeof todosWithCost[number]>()
  for (const t of todosWithCost) {
    const personKey = t.employeeIdCardHash || t.employeeName || t.id
    const catKey = getTodoRiskCategoryKey(t.title || '')
    const dedupKey = `${personKey}:${catKey}`
    const existing = dedupedTodoMap.get(dedupKey)
    // 去重保留优先级更高（estimatedLoss 更大或 deadline 更近）的一条
    if (!existing || t.estimatedLoss > existing.estimatedLoss) {
      dedupedTodoMap.set(dedupKey, t)
    }
  }
  const dedupedTodos = Array.from(dedupedTodoMap.values())

  // 去重后的待办按优先级排序：URGENT > HIGH > MEDIUM > LOW
  const priorityOrder = { URGENT: 0, HIGH: 1, MEDIUM: 2, LOW: 3 }
  dedupedTodos.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority])

  // 风险分布：使用去重后的数据，与待办列表一致
  const riskDistribution = {
    contract: dedupedTodos.filter((t) => t.type === 'CONTRACT').length,
    salary: dedupedTodos.filter((t) => t.type === 'SALARY').length,
    termination: dedupedTodos.filter((t) => t.type === 'TERMINATION').length,
    retirement: dedupedTodos.filter((t) => t.type === 'RETIREMENT').length,
    monthly: dedupedTodos.filter((t) => t.type === 'MONTHLY').length,
  }

  // 按优先级排序：URGENT > HIGH > MEDIUM > LOW（priorityOrder 已在上方声明）
  todosWithCost.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority])

  const topRisks = todosWithCost
    .filter((t) => t.priority === 'URGENT' || t.priority === 'HIGH')
    .slice(0, 5)
    .map((t) => ({
      id: t.id,
      type: t.type as string,
      level: t.level,
      priority: t.priority,
      title: t.title,
      description: t.description,
      estimatedLoss: t.estimatedLoss,
      deadline: t.deadline,
      daysUntilDeadline: t.daysUntilDeadline,
      actionUrl: t.actionUrl,
    }))

  // 紧急风险横幅：最高优先级且预估损失>0
  const urgentRisk = dedupedTodos.find((t) => t.priority === 'URGENT' && t.estimatedLoss > 0) || null

  // 按优先级排序去重后的 todos
  dedupedTodos.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority])
  const todos = dedupedTodos

  const resolvedTodos = resolvedItems.map((r: typeof resolvedItems[number]) => ({
    id: r.id,
    type: r.type as 'CONTRACT' | 'SALARY' | 'TERMINATION' | 'MONTHLY' | 'ONBOARDING' | 'RETIREMENT',
    level: r.level.toLowerCase() as 'high' | 'medium' | 'low',
    title: r.title,
    description: r.description,
    actionUrl: r.actionUrl || '/',
    resolvedAt: r.resolvedAt?.toISOString() || null,
  }))

  const hour = new Date().getHours()
  const greeting = hour < 12
    ? `早上好！今天有 ${todos.length} 件事需要处理`
    : hour < 18
      ? `下午好！今天有 ${todos.length} 件事需要处理`
      : `晚上好！今天有 ${todos.length} 件事需要处理`

  // 风险趋势：本月新增 vs 上月
  const lastMonthStart = new Date(now.getFullYear(), now.getMonth() - 1, 1)
  const lastMonthEnd = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59)
  const lastMonthRiskCount = await prisma.riskItem.count({
    where: { orgId, createdAt: { gte: lastMonthStart, lte: lastMonthEnd } },
  })
  const thisMonthRiskCount = await prisma.riskItem.count({
    where: { orgId, createdAt: { gte: monthStart, lte: monthEnd } },
  })
  const riskTrend = {
    thisMonth: thisMonthRiskCount,
    lastMonth: lastMonthRiskCount,
    change: thisMonthRiskCount - lastMonthRiskCount,
  }

  return {
    greeting,
    stats: {
      employeeCount,
      highRiskCount: dedupedTodos.filter((t) => t.level === 'high' && (t.type === 'CONTRACT' || t.type === 'TERMINATION' || t.type === 'RETIREMENT')).length,
      todoCount: todos.length,
      monthlyOvertimePay,
    },
    urgentRisk,
    todos,
    resolvedTodos,
    riskDistribution,
    riskTrend,
    topRisks,
    aiPrediction: null,
    payrollSummary,
    monthlyActivities,
    yearCostSummary,
  }
}

/**
 * HR 月度日历：聚合当月关键日期
 */
export async function getMonthlyCalendar(orgId: string, month: string) {
  const year = month.slice(0, 4)
  const monthNum = parseInt(month.slice(5, 7))
  const monthStart = new Date(parseInt(year), monthNum - 1, 1)
  const monthEnd = new Date(parseInt(year), monthNum, 0, 23, 59, 59)

  const events: Array<{
    date: string
    type: string
    title: string
    employeeName?: string
    employeeIdCardNumber?: string | null
    employeeId?: string
    actionUrl?: string
    priority: 'high' | 'medium' | 'low'
  }> = []

  // 1. 合同到期
  const expiringContracts = await prisma.laborContract.findMany({
    where: {
      orgId,
      endDate: { gte: monthStart, lte: monthEnd },
    },
    include: { employee: true },
  })
  for (const c of expiringContracts) {
    events.push({
      date: c.endDate!.toISOString().slice(0, 10),
      type: 'CONTRACT_EXPIRY',
      title: `${c.employee.name} 合同到期`,
      employeeName: c.employee.name,
      employeeIdCardNumber: (() => { try { const v = c.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
      employeeId: c.employeeId,
      actionUrl: '/contracts',
      priority: 'high',
    })
  }

  // 2. 试用期到期
  const probationExpiring = await prisma.laborContract.findMany({
    where: {
      orgId,
      probationMonths: { gt: 0 },
    },
    include: { employee: true },
  })
  for (const c of probationExpiring) {
    const probationEnd = new Date(c.startDate)
    probationEnd.setMonth(probationEnd.getMonth() + c.probationMonths)
    if (probationEnd >= monthStart && probationEnd <= monthEnd) {
      events.push({
        date: probationEnd.toISOString().slice(0, 10),
        type: 'PROBATION_END',
        title: `${c.employee.name} 试用期到期`,
        employeeName: c.employee.name,
        employeeIdCardNumber: (() => { try { const v = c.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
        employeeId: c.employeeId,
        actionUrl: '/roster',
        priority: 'medium',
      })
    }
  }

  // 3. 离职/解聘日期
  const terminations = await prisma.terminationRecord.findMany({
    where: {
      orgId,
      terminationDate: { gte: monthStart, lte: monthEnd },
      status: { notIn: ['CANCELLED'] },
    },
    include: { employee: true },
  })
  for (const t of terminations) {
    events.push({
      date: t.terminationDate.toISOString().slice(0, 10),
      type: 'TERMINATION',
      title: `${t.employee.name} 离职/解聘日`,
      employeeName: t.employee.name,
      employeeIdCardNumber: (() => { try { const v = t.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
      employeeId: t.employeeId,
      actionUrl: '/termination',
      priority: 'high',
    })
  }

  // 4. 入职周年
  const employees = await prisma.employee.findMany({
    where: {
      orgId,
      status: 'ACTIVE',
      hireDate: {
        gte: new Date(parseInt(year) - 50, monthNum - 1, 1),
        lte: new Date(parseInt(year) - 1, monthNum - 1, 0),
      },
    },
  })
  for (const emp of employees) {
    const hireAnniversary = new Date(parseInt(year), emp.hireDate.getMonth(), emp.hireDate.getDate())
    if (hireAnniversary >= monthStart && hireAnniversary <= monthEnd) {
      const years = parseInt(year) - emp.hireDate.getFullYear()
      events.push({
        date: hireAnniversary.toISOString().slice(0, 10),
        type: 'ANNIVERSARY',
        title: `${emp.name} 入职${years}周年`,
        employeeName: emp.name,
        employeeIdCardNumber: (() => { try { const v = emp.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
        employeeId: emp.id,
        actionUrl: '/roster',
        priority: 'low',
      })
    }
  }

  // 5. 风险截止日期（按 employeeId:type 去重，同一员工同一类型只保留最新一条）
  const riskDeadlines = await prisma.riskItem.findMany({
    where: {
      orgId,
      status: 'PENDING',
      deadline: { gte: monthStart, lte: monthEnd },
    },
    include: { employee: true },
    orderBy: { createdAt: 'desc' },
  })
  const seenRiskKeys = new Set<string>()
  for (const r of riskDeadlines) {
    const dedupKey = `${r.employeeId}:${r.type}`
    if (r.employeeId && seenRiskKeys.has(dedupKey)) continue
    if (r.employeeId) seenRiskKeys.add(dedupKey)
    events.push({
      date: r.deadline!.toISOString().slice(0, 10),
      type: 'RISK_DEADLINE',
      title: `${r.title} 处理截止日`,
      employeeName: r.employee?.name,
      employeeIdCardNumber: (() => { try { const v = r.employee?.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
      employeeId: r.employeeId || undefined,
      actionUrl: r.actionUrl || '/',
      priority: r.level === 'HIGH' ? 'high' : 'medium',
    })
  }

  // 6. 退休日期（仅最新合同为正式劳动合同的员工）
  const retiringEmployees = await prisma.employee.findMany({
    where: {
      orgId,
      status: 'ACTIVE',
      retirementDaysLeft: { gte: 0, lte: 365 },
    },
    include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
  })
  for (const emp of retiringEmployees) {
    // 只对最新合同为正式劳动合同的员工显示退休日期
    const latestContractType = emp.contracts[0]?.contractType
    if (!latestContractType || !['FIXED', 'UNFIXED'].includes(latestContractType)) continue
    if (emp.retirementDaysLeft !== null) {
      const retireDate = new Date()
      retireDate.setDate(retireDate.getDate() + emp.retirementDaysLeft)
      if (retireDate >= monthStart && retireDate <= monthEnd) {
        events.push({
          date: retireDate.toISOString().slice(0, 10),
          type: 'RETIREMENT',
          title: `${emp.name} 达到法定退休年龄`,
          employeeName: emp.name,
          employeeIdCardNumber: (() => { try { const v = emp.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
          employeeId: emp.id,
          actionUrl: '/roster',
          priority: 'high',
        })
      }
    }
  }

  // 7. 发薪日期
  const org = await prisma.organization.findUnique({
    where: { id: orgId },
    select: { payrollDays: true },
  })
  const payrollDays = Array.isArray(org?.payrollDays) ? org.payrollDays as number[] : []
  for (const day of payrollDays) {
    const dateStr = `${month}-${String(day).padStart(2, '0')}`
    const payrollDate = new Date(parseInt(year), monthNum - 1, day)
    if (payrollDate >= monthStart && payrollDate <= monthEnd) {
      events.push({
        date: dateStr,
        type: 'PAYROLL_DAY',
        title: `发薪日（每月${day}号）`,
        actionUrl: '/money',
        priority: 'medium',
      })
    }
  }

  // 8. 自定义日历事件
  const customEvents = await prisma.calendarEvent.findMany({
    where: {
      orgId,
      date: { gte: monthStart, lte: monthEnd },
    },
    include: { employee: { select: { name: true, idCardNumber: true } } },
  })
  for (const ev of customEvents) {
    events.push({
      date: ev.date.toISOString().slice(0, 10),
      type: ev.type,
      title: ev.title + (ev.employee ? ` — ${ev.employee.name}` : ''),
      employeeName: ev.employee?.name,
      employeeIdCardNumber: (() => { try { const v = ev.employee?.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
      actionUrl: '/dashboard',
      priority: ev.priority as 'high' | 'medium' | 'low',
    })
  }

  // 按日期排序
  events.sort((a, b) => a.date.localeCompare(b.date))

  return { month, events }
}

/**
 * 人力成本深度分析：同比/环比 + 归因
 */
export async function getCostAnalysis(orgId: string, month: string) {
  const year = parseInt(month.slice(0, 4))
  const monthNum = parseInt(month.slice(5, 7))

  // 当月数据

  // 上月（环比）
  const prevMonthStart = new Date(year, monthNum - 2, 1)
  const prevMonthStr = `${prevMonthStart.getFullYear()}-${String(prevMonthStart.getMonth() + 1).padStart(2, '0')}`

  // 去年同月（同比）
  const lastYearMonthStart = new Date(year - 1, monthNum - 1, 1)
  const lastYearMonthStr = `${lastYearMonthStart.getFullYear()}-${String(lastYearMonthStart.getMonth() + 1).padStart(2, '0')}`

  // 获取各月归档批次汇总（按员工去重，与 getDashboardData 口径一致）
  const getMonthCost = async (m: string) => {
    const entries = await prisma.batchEntry.findMany({
      where: { orgId, batch: { month: m, status: 'ARCHIVED' } },
      select: { employeeId: true, totalPay: true, socialOrg: true, housingOrg: true, tax: true },
    })
    // 同一员工多批次只取一条（取最后一条，金额通常已合并）
    const empMap = new Map<string, { totalPay: number; socialOrg: number; housingOrg: number; tax: number }>()
    for (const e of entries) {
      const ex = empMap.get(e.employeeId)
      if (!ex) {
        empMap.set(e.employeeId, { totalPay: e.totalPay, socialOrg: e.socialOrg, housingOrg: e.housingOrg, tax: e.tax })
      } else {
        // 同一员工多批次：累加金额
        ex.totalPay += e.totalPay
        ex.socialOrg += e.socialOrg
        ex.housingOrg += e.housingOrg
        ex.tax += e.tax
      }
    }
    const deduped = Array.from(empMap.values())
    return {
      totalPay: deduped.reduce((s, e) => s + e.totalPay, 0),
      totalSocialOrg: deduped.reduce((s, e) => s + e.socialOrg, 0),
      totalHousingOrg: deduped.reduce((s, e) => s + e.housingOrg, 0),
      totalTax: deduped.reduce((s, e) => s + e.tax, 0),
      employeeCount: deduped.length,
    }
  }

  const current = await getMonthCost(month)
  const prev = await getMonthCost(prevMonthStr)
  const lastYear = await getMonthCost(lastYearMonthStr)

  const currentTotal = current.totalPay + current.totalSocialOrg + current.totalHousingOrg
  const prevTotal = prev.totalPay + prev.totalSocialOrg + prev.totalHousingOrg
  const lastYearTotal = lastYear.totalPay + lastYear.totalSocialOrg + lastYear.totalHousingOrg

  // 环比变化
  const momChange = prevTotal > 0 ? ((currentTotal - prevTotal) / prevTotal) * 100 : 0
  // 同比变化
  const yoyChange = lastYearTotal > 0 ? ((currentTotal - lastYearTotal) / lastYearTotal) * 100 : 0

  // 人均成本
  const currentPerCapita = current.employeeCount > 0 ? currentTotal / current.employeeCount : 0
  const prevPerCapita = prev.employeeCount > 0 ? prevTotal / prev.employeeCount : 0
  const lastYearPerCapita = lastYear.employeeCount > 0 ? lastYearTotal / lastYear.employeeCount : 0

  // 归因分析
  const factors: Array<{ factor: string; impact: number; description: string }> = []

  // 1. 人数变化影响
  const headcountChange = current.employeeCount - prev.employeeCount
  if (headcountChange !== 0 && prevPerCapita > 0) {
    factors.push({
      factor: '人数变化',
      impact: headcountChange * prevPerCapita,
      description: `员工人数${headcountChange > 0 ? '增加' : '减少'} ${Math.abs(headcountChange)} 人，影响成本 ¥${(headcountChange * prevPerCapita).toFixed(2)}`,
    })
  }

  // 2. 薪酬水平变化
  if (prevPerCapita > 0 && currentPerCapita > 0) {
    const perCapitaChange = currentPerCapita - prevPerCapita
    if (Math.abs(perCapitaChange) > 1) {
      factors.push({
        factor: '人均薪酬变化',
        impact: perCapitaChange * current.employeeCount,
        description: `人均成本${perCapitaChange > 0 ? '上升' : '下降'} ¥${Math.abs(perCapitaChange).toFixed(2)}，影响总成本 ¥${(perCapitaChange * current.employeeCount).toFixed(2)}`,
      })
    }
  }

  // 3. 社保公积金变化
  const socialChange = (current.totalSocialOrg + current.totalHousingOrg) - (prev.totalSocialOrg + prev.totalHousingOrg)
  if (Math.abs(socialChange) > 1) {
    factors.push({
      factor: '社保公积金变化',
      impact: socialChange,
      description: `社保公积金${socialChange > 0 ? '增加' : '减少'} ¥${Math.abs(socialChange).toFixed(2)}`,
    })
  }

  // 按部门拆分成本（按员工去重）
  const deptEntries = await prisma.batchEntry.findMany({
    where: {
      orgId,
      batch: { month, status: 'ARCHIVED' },
    },
    include: { employee: { select: { department: true } } },
  })
  // 先按员工去重，同一员工多批次累加金额
  const deptEmpMap = new Map<string, { dept: string; totalPay: number; socialOrg: number; housingOrg: number }>()
  for (const e of deptEntries) {
    const dept = e.employee?.department || '未分配'
    const ex = deptEmpMap.get(e.employeeId)
    if (!ex) {
      deptEmpMap.set(e.employeeId, { dept, totalPay: e.totalPay, socialOrg: e.socialOrg, housingOrg: e.housingOrg })
    } else {
      ex.totalPay += e.totalPay
      ex.socialOrg += e.socialOrg
      ex.housingOrg += e.housingOrg
    }
  }
  const deptMap: Record<string, { totalPay: number; socialOrg: number; housingOrg: number; headcount: number }> = {}
  for (const [, v] of deptEmpMap) {
    if (!deptMap[v.dept]) deptMap[v.dept] = { totalPay: 0, socialOrg: 0, housingOrg: 0, headcount: 0 }
    deptMap[v.dept].totalPay += v.totalPay
    deptMap[v.dept].socialOrg += v.socialOrg
    deptMap[v.dept].housingOrg += v.housingOrg
    deptMap[v.dept].headcount += 1
  }
  const departmentCost = Object.entries(deptMap)
    .map(([dept, v]) => ({
      department: dept,
      totalCost: v.totalPay + v.socialOrg + v.housingOrg,
      totalPay: v.totalPay,
      socialOrg: v.socialOrg,
      housingOrg: v.housingOrg,
      headcount: v.headcount,
      perCapita: v.headcount > 0 ? (v.totalPay + v.socialOrg + v.housingOrg) / v.headcount : 0,
    }))
    .sort((a, b) => b.totalCost - a.totalCost)

  return {
    month,
    current: {
      totalCost: currentTotal,
      totalPay: current.totalPay,
      socialOrg: current.totalSocialOrg,
      housingOrg: current.totalHousingOrg,
      tax: current.totalTax,
      employeeCount: current.employeeCount,
      perCapita: currentPerCapita,
    },
    monthOnMonth: {
      prevMonth: prevMonthStr,
      prevTotal: prevTotal,
      prevPerCapita: prevPerCapita,
      change: currentTotal - prevTotal,
      changePercent: momChange,
    },
    yearOnYear: {
      lastYearMonth: lastYearMonthStr,
      lastYearTotal: lastYearTotal,
      lastYearPerCapita: lastYearPerCapita,
      change: currentTotal - lastYearTotal,
      changePercent: yoyChange,
    },
    factors,
    departmentCost,
  }
}

/**
 * 合规健康度评分 + AI 建议卡片流
 * 5 维度：合同管理 / 规章制度 / 考勤工时 / 薪酬工资 / 社保公积金
 * 评分 0-100，越高越健康
 */
export async function getComplianceScore(orgId: string) {
  const now = new Date()

  const [
    totalEmployees,
    unsignedContracts,
    pendingSignContracts,
    expiringContracts,
    pendingRisks,
    highRisks,
    resolvedRisks,
    policiesWithoutPublish,
    attendanceUnconfirmed,
    unconfirmedPayslips,
    totalPayslips,
    socialConfig,
    housingConfig,
    lastMonthRisks,
  ] = await Promise.all([
    prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
    // 未签合同：完全无合同记录的在职员工
    prisma.employee.count({ where: { orgId, status: 'ACTIVE', contracts: { none: {} } } }),
    // 待签署：有合同记录但 signDate 为 null（合同已创建但未完成签署）
    prisma.employee.count({
      where: {
        orgId,
        status: 'ACTIVE',
        contracts: { some: { signDate: null } },
      },
    }),
    prisma.laborContract.count({
      where: {
        orgId,
        employee: { status: 'ACTIVE' },
        endDate: { gte: now, lte: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000) },
      },
    }),
    prisma.riskItem.count({ where: { orgId, status: 'PENDING' } }),
    prisma.riskItem.count({ where: { orgId, status: 'PENDING', level: 'HIGH' } }),
    prisma.riskItem.count({ where: { orgId, status: 'RESOLVED' } }),
    prisma.policyDocument.count({ where: { orgId, status: { not: 'PUBLISHED' } } }),
    prisma.attendanceConfirmation.count({ where: { orgId, status: 'PENDING' } }),
    prisma.payslip.count({ where: { orgId, confirmedAt: null } }),
    prisma.payslip.count({ where: { orgId } }),
    // 社保/公积金配置：优先查 SocialYearStandard（新架构），回退到旧表
    prisma.socialYearStandard.findFirst({ where: { orgId, isCurrent: true, account: { type: 'SOCIAL' } } }).then(async r => r ? true : !!(await prisma.socialInsuranceConfig.findFirst({ where: { orgId, isCurrent: true } }))) as Promise<boolean>,
    prisma.socialYearStandard.findFirst({ where: { orgId, isCurrent: true, account: { type: 'HOUSING' } } }).then(async r => r ? true : !!(await prisma.housingFundConfig.findFirst({ where: { orgId, isCurrent: true } }))) as Promise<boolean>,
    prisma.riskItem.count({
      where: {
        orgId,
        createdAt: {
          gte: new Date(now.getFullYear(), now.getMonth() - 1, 1),
          lte: new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59),
        },
      },
    }),
  ])

  // 5 维度评分计算（未签合同 + 待签署都扣分）
  const contractScore = Math.max(0, 100 - unsignedContracts * 15 - pendingSignContracts * 10 - expiringContracts * 5)
  const policyScore = Math.max(0, 100 - policiesWithoutPublish * 12)
  const attendanceScore = Math.max(0, 100 - attendanceUnconfirmed * 8)
  const salaryScore = totalPayslips > 0
    ? Math.max(0, 100 - Math.round((unconfirmedPayslips / totalPayslips) * 100))
    : 100
  const socialScore = (socialConfig ? 50 : 0) + (housingConfig ? 50 : 0)
  // 社保公积金待办：当月未完成的缴纳任务
  const currentMonthStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
  const socialTodoCount = await prisma.riskItem.count({
    where: { orgId, status: 'PENDING', type: 'MONTHLY', title: { startsWith: `${currentMonthStr}月 缴纳` } },
  })

  const dimensions = [
    { key: 'contract', name: '合同管理', score: contractScore, todoCount: unsignedContracts + pendingSignContracts + expiringContracts },
    { key: 'policy', name: '规章制度', score: policyScore, todoCount: policiesWithoutPublish },
    { key: 'attendance', name: '考勤工时', score: attendanceScore, todoCount: attendanceUnconfirmed },
    { key: 'salary', name: '薪酬工资', score: salaryScore, todoCount: unconfirmedPayslips },
    { key: 'social', name: '社保公积金', score: socialScore, todoCount: socialTodoCount },
  ]

  const overallScore = Math.round(
    dimensions.reduce((sum, d) => sum + d.score, 0) / dimensions.length,
  )

  const level = overallScore >= 85 ? 'safe' : overallScore >= 60 ? 'warning' : 'danger'
  const levelLabel = overallScore >= 85 ? '健康' : overallScore >= 60 ? '中等风险' : '高风险'

  // AI 建议卡片流：4 类分级
  const suggestions: Array<{
    type: 'danger' | 'warning' | 'value' | 'knowledge'
    title: string
    description: string
    actionUrl: string
    actionLabel: string
    estimatedLoss?: number
  }> = []

  // 高风险卡片
  if (highRisks > 0) {
    const topRisk = await prisma.riskItem.findFirst({
      where: { orgId, status: 'PENDING', level: 'HIGH' },
      include: { employee: true },
      orderBy: { estimatedLoss: 'desc' },
    })
    if (topRisk) {
      suggestions.push({
        type: 'danger',
        title: topRisk.title,
        description: topRisk.description,
        actionUrl: topRisk.actionUrl || '/roster',
        actionLabel: '立即处理',
        estimatedLoss: topRisk.estimatedLoss || 0,
      })
    }
  }

  // 中风险卡片
  if (policiesWithoutPublish > 0) {
    suggestions.push({
      type: 'warning',
      title: `${policiesWithoutPublish} 项制度未完成民主程序`,
      description: '未公示的制度在仲裁中可能无效，建议尽快完成民主程序',
      actionUrl: '/policies',
      actionLabel: '去处理',
    })
  }
  if (expiringContracts > 0) {
    suggestions.push({
      type: 'warning',
      title: `${expiringContracts} 份合同即将到期`,
      description: '30 天内到期的合同需及时续签或终止，否则可能面临双倍工资风险',
      actionUrl: '/roster',
      actionLabel: '查看合同',
    })
  }

  // 价值卡片
  if (resolvedRisks > 0) {
    const totalSaved = await prisma.riskItem.aggregate({
      where: { orgId, status: 'RESOLVED', estimatedLoss: { gt: 0 } },
      _sum: { estimatedLoss: true },
    })
    suggestions.push({
      type: 'value',
      title: `已规避 ${fmtMoney(totalSaved._sum.estimatedLoss || 0)} 潜在损失`,
      description: `累计处理 ${resolvedRisks} 项风险，有效降低仲裁败诉概率`,
      actionUrl: '/audit',
      actionLabel: '查看记录',
    })
  }

  // 知识卡片
  if (lastMonthRisks > 0) {
    suggestions.push({
      type: 'knowledge',
      title: `上月新增 ${lastMonthRisks} 项风险`,
      description: '关注风险趋势变化，及时调整管理策略',
      actionUrl: '/',
      actionLabel: '查看趋势',
    })
  }

  return {
    overallScore,
    level,
    levelLabel,
    dimensions,
    suggestions,
    stats: {
      totalEmployees,
      pendingRisks,
      highRisks,
      resolvedRisks,
    },
  }
}

function fmtMoney(n: number): string {
  if (n >= 10000) return `¥${(n / 10000).toFixed(1)}万`
  return `¥${n.toLocaleString('zh-CN')}`
}

/**
 * 用工体检诊断：6 维度深度诊断 + 历史报告
 * 维度：合同管理 / 薪酬社保 / 考勤加班 / 规章制度 / 解聘合规 / 证据链
 */
export async function getHealthCheck(orgId: string) {
  const now = new Date()
  const year = now.getFullYear()

  const [
    totalEmployees,
    unsignedContracts,
    pendingSignContracts,
    expiringContracts,
    policiesWithoutPublish,
    totalPolicies,
    socialConfig,
    housingConfig,
    employeesNoSocial,
    overtimeExcessive,
    totalTerminations,
    completedTerminations,
    terminationsWithChecklist,
    disciplinaryRecords,
    attendanceRecords,
    trainingRecords,
    unconfirmedPayslips,
    totalPayslips,
  ] = await Promise.all([
    prisma.employee.count({ where: { orgId, status: 'ACTIVE' } }),
    // 未签合同：完全无合同记录的在职员工
    prisma.employee.count({ where: { orgId, status: 'ACTIVE', contracts: { none: {} } } }),
    // 待签署：有合同记录但 signDate 为 null（合同已创建但未完成签署）
    prisma.employee.count({
      where: {
        orgId,
        status: 'ACTIVE',
        contracts: { some: { signDate: null } },
      },
    }),
    prisma.laborContract.count({
      where: {
        orgId,
        employee: { status: 'ACTIVE' },
        endDate: { gte: now, lte: new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000) },
      },
    }),
    // democracyProgress 是 JSONB，用 status 字段判断未完成
    prisma.policyDocument.count({ where: { orgId, status: { not: 'PUBLISHED' } } }),
    prisma.policyDocument.count({ where: { orgId } }),
    // 社保/公积金配置：优先查 SocialYearStandard（新架构），回退到旧表
    prisma.socialYearStandard.findFirst({ where: { orgId, isCurrent: true, account: { type: 'SOCIAL' } } }).then(async r => r ? true : !!(await prisma.socialInsuranceConfig.findFirst({ where: { orgId, isCurrent: true } }))) as Promise<boolean>,
    prisma.socialYearStandard.findFirst({ where: { orgId, isCurrent: true, account: { type: 'HOUSING' } } }).then(async r => r ? true : !!(await prisma.housingFundConfig.findFirst({ where: { orgId, isCurrent: true } }))) as Promise<boolean>,
    // 社保记录：计算有正式劳动合同但无社保记录的在职员工数（实习/劳务/外包等不缴社保）
    Promise.all([
      prisma.employee.findMany({
        where: { orgId, status: 'ACTIVE', contracts: { some: { contractType: { in: ['FIXED', 'UNFIXED'] } } } },
        select: { id: true },
      }),
      prisma.employeeSocialInsRecord.groupBy({ by: ['employeeId'], where: { orgId, endMonth: null }, _count: true }).then(r => new Set(r.map(x => x.employeeId))),
    ]).then(([emps, socialEmpIds]) => emps.filter(e => !socialEmpIds.has(e.id)).length),
    prisma.overtimeRecord.count({ where: { orgId, weekdayHours: { gt: 36 } } }),
    // 解聘合规
    prisma.terminationRecord.count({ where: { orgId } }),
    prisma.terminationRecord.count({ where: { orgId, status: 'COMPLETED' } }),
    prisma.terminationRecord.count({ where: { orgId, status: 'COMPLETED', checklist: { not: {} } } }),
    // 证据链
    prisma.disciplinaryRecord.count({ where: { orgId } }),
    prisma.attendanceRecord.count({ where: { orgId } }),
    prisma.trainingRecord.count({ where: { orgId } }),
    // 薪酬
    prisma.payslip.count({ where: { orgId, confirmedAt: null } }),
    prisma.payslip.count({ where: { orgId } }),
  ])

  // 6 维度诊断（与评分标准说明一致）
  const dimensions = [
    {
      key: 'contract',
      name: '合同管理',
      score: Math.max(0, 100 - unsignedContracts * 15 - pendingSignContracts * 10 - expiringContracts * 5),
      todoCount: unsignedContracts + pendingSignContracts + expiringContracts,
      findings: [
        unsignedContracts > 0 ? `${unsignedContracts} 名员工未签书面合同，存在双倍工资风险` : '全员已有合同记录',
        pendingSignContracts > 0 ? `${pendingSignContracts} 份合同待签署（已创建未完成签署）` : '',
        expiringContracts > 0 ? `${expiringContracts} 份合同 30 天内到期，需及时续签` : '无即将到期合同',
      ].filter(Boolean),
      recommendations: [
        unsignedContracts > 0 ? '立即补签书面合同，超过1个月未签需支付双倍工资' : '',
        pendingSignContracts > 0 ? '尽快完成待签署合同的签署流程，未签署合同在仲裁中可能无效' : '',
        expiringContracts > 0 ? '提前30天发送续签意向书，保留协商证据' : '',
      ].filter(Boolean),
    },
    {
      key: 'salary',
      name: '薪酬工资',
      score: totalPayslips > 0
        ? Math.max(0, 100 - Math.round((unconfirmedPayslips / totalPayslips) * 100))
        : 100,
      todoCount: unconfirmedPayslips,
      findings: [
        totalPayslips > 0
          ? `${unconfirmedPayslips}/${totalPayslips} 张工资条未确认`
          : '暂无工资条记录',
      ].filter(Boolean),
      recommendations: [
        unconfirmedPayslips > 0 ? '提醒员工及时确认工资条，保留薪酬合规证据' : '',
      ].filter(Boolean),
    },
    {
      key: 'social',
      name: '社保公积金',
      score: Math.round(
        (socialConfig ? 50 : 0) +
        (housingConfig ? 30 : 0) +
        (employeesNoSocial === 0 ? 20 : Math.max(0, 20 - employeesNoSocial * 2))
      ),
      todoCount: await prisma.riskItem.count({
        where: { orgId, status: 'PENDING', type: 'MONTHLY', title: { startsWith: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}月 缴纳` } },
      }),
      findings: [
        socialConfig ? '社保配置已设置' : '未配置社保比例，建议尽快设置',
        housingConfig ? '公积金配置已设置' : '未配置公积金比例',
        employeesNoSocial > 0 ? `${employeesNoSocial} 名员工社保记录异常` : '社保缴纳记录正常',
      ].filter(Boolean),
      recommendations: [
        !socialConfig ? '前往社保管理页面设置企业/个人缴纳比例' : '',
        !housingConfig ? '根据当地政策配置公积金比例' : '',
      ].filter(Boolean),
    },
    {
      key: 'attendance',
      name: '考勤加班',
      score: Math.max(0, 100 - overtimeExcessive * 10),
      todoCount: overtimeExcessive,
      findings: [
        overtimeExcessive > 0 ? `${overtimeExcessive} 条加班记录月超时36小时，存在违法风险` : '加班时长合规',
        attendanceRecords > 0 ? `已记录 ${attendanceRecords} 条考勤数据` : '暂无考勤记录，建议建立考勤制度',
      ].filter(Boolean),
      recommendations: [
        overtimeExcessive > 0 ? '月加班不得超过36小时，建议调休或减少加班安排' : '',
        attendanceRecords === 0 ? '建立考勤记录制度，保留出勤证据' : '',
      ].filter(Boolean),
    },
    {
      key: 'policy',
      name: '规章制度',
      score: totalPolicies > 0
        ? Math.max(0, 100 - policiesWithoutPublish * 12)
        : 60,
      todoCount: policiesWithoutPublish,
      findings: [
        totalPolicies === 0 ? '尚未上传任何规章制度，建议建立基础制度体系' : `已管理 ${totalPolicies} 项制度`,
        policiesWithoutPublish > 0 ? `${policiesWithoutPublish} 项制度未完成民主程序，仲裁中可能无效` : '所有制度已完成民主公示',
      ].filter(Boolean),
      recommendations: [
        totalPolicies === 0 ? '建议至少建立员工手册、考勤制度、奖惩制度三项基础制度' : '',
        policiesWithoutPublish > 0 ? '尽快完成民主程序四步：起草→讨论→协商→公示' : '',
      ].filter(Boolean),
    },
    {
      key: 'termination',
      name: '解聘合规',
      score: totalTerminations > 0
        ? Math.round(
            (completedTerminations / totalTerminations) * 60 +
            (terminationsWithChecklist / Math.max(1, completedTerminations)) * 40
          )
        : 100,
      todoCount: totalTerminations - completedTerminations,
      findings: [
        totalTerminations > 0
          ? `共 ${totalTerminations} 起解聘，${completedTerminations} 起已完成流程`
          : '暂无解聘记录',
        totalTerminations > 0 && completedTerminations < totalTerminations
          ? `${totalTerminations - completedTerminations} 起解聘流程未完成，存在程序瑕疵风险`
          : '',
        terminationsWithChecklist > 0
          ? `${terminationsWithChecklist} 起解聘已保存合规检查清单`
          : totalTerminations > 0 ? '建议完善解聘合规检查清单' : '',
      ].filter(Boolean),
      recommendations: [
        completedTerminations < totalTerminations ? '完成所有解聘流程审批，确保程序合法' : '',
        totalTerminations > 0 ? '保留解聘协议、补偿计算依据等关键证据' : '',
      ].filter(Boolean),
    },
    {
      key: 'evidence',
      name: '证据链',
      score: totalEmployees > 0
        ? Math.min(100, Math.round(
            ((disciplinaryRecords > 0 ? 1 : 0) * 20) +
            ((attendanceRecords > 0 ? 1 : 0) * 30) +
            ((trainingRecords > 0 ? 1 : 0) * 10) +
            (totalPolicies > 0 ? 20 : 0) +
            (totalTerminations > 0 ? 10 : 10) +
            (totalPayslips > 0 ? 10 : 0)
          ))
        : 100,
      findings: [
        disciplinaryRecords > 0 ? `违纪记录 ${disciplinaryRecords} 条` : '无违纪记录',
        attendanceRecords > 0 ? `考勤记录 ${attendanceRecords} 条` : '无考勤记录',
        trainingRecords > 0 ? `培训记录 ${trainingRecords} 条` : '无培训记录',
        totalEmployees > 0
          ? `覆盖 ${totalEmployees} 名在管员工的证据留存`
          : '暂无在管员工',
      ].filter(Boolean),
      recommendations: [
        disciplinaryRecords === 0 ? '对违纪行为及时记录并保留证据' : '',
        attendanceRecords === 0 ? '建立考勤记录制度，保留出勤证据' : '',
        trainingRecords === 0 ? '记录员工培训情况，保留签到表' : '',
      ].filter(Boolean),
    },
  ]

  const totalScore = Math.round(dimensions.reduce((sum, d) => sum + d.score, 0) / dimensions.length)
  const level = totalScore >= 85 ? 'safe' : totalScore >= 60 ? 'warning' : 'danger'

  const summary = level === 'safe'
    ? `整体用工合规状况良好（${totalScore}分），建议保持定期巡检`
    : level === 'warning'
      ? `整体用工存在中等风险（${totalScore}分），建议优先处理低分维度问题`
      : `整体用工存在较高风险（${totalScore}分），建议立即整改低分维度`

  return {
    year,
    totalScore,
    level,
    dimensions,
    summary,
  }
}

/**
 * 保存体检诊断报告
 */
export async function saveHealthCheckReport(orgId: string, userId: string) {
  const result = await getHealthCheck(orgId)

  const report = await (prisma as any).healthCheckReport.create({
    data: {
      orgId,
      year: result.year,
      totalScore: result.totalScore,
      level: result.level,
      dimensions: result.dimensions as any,
      summary: result.summary,
      createdBy: userId,
    },
  })

  return report
}

/**
 * 获取历史诊断报告列表
 */
export async function getHealthCheckHistory(orgId: string) {
  const reports = await (prisma as any).healthCheckReport.findMany({
    where: { orgId },
    orderBy: { createdAt: 'desc' },
    take: 10,
  })

  return (reports as any[]).map((r: any) => ({
    id: r.id,
    year: r.year,
    totalScore: r.totalScore,
    level: r.level,
    summary: r.summary,
    createdAt: r.createdAt.toISOString(),
  }))
}

/**
 * 年度价值报告：量化系统为企业创造的价值
 */
export async function getAnnualValueReport(orgId: string, year: number) {
  const yearStart = new Date(year, 0, 1)
  const yearEnd = new Date(year, 11, 31, 23, 59, 59)

  const [
    risksResolved,
    _lossAvoidedAgg,
    aiConversations,
    aiReviews,
    contractsSigned,
    payrollBatches,
    terminations,
    employeesManaged,
    auditActions,
    policiesPublished,
    monthlyRisks,
    employeeRiskDetails,
    pendingRisks,
  ] = await Promise.all([
    prisma.riskItem.count({
      where: {
        orgId, status: 'RESOLVED',
        resolvedBy: { not: null },
        resolvedAt: { gte: yearStart, lte: yearEnd },
        employeeId: { not: null },
      },
    }),
    prisma.riskItem.aggregate({
      where: {
        orgId, status: 'RESOLVED', estimatedLoss: { gt: 0 },
        resolvedBy: { not: null },
        resolvedAt: { gte: yearStart, lte: yearEnd },
        employeeId: { not: null },
      },
      _sum: { estimatedLoss: true },
    }),
    prisma.aIConversation.count({
      where: { orgId, createdAt: { gte: yearStart, lte: yearEnd } },
    }),
    prisma.aIReviewRecord.count({
      where: { orgId, createdAt: { gte: yearStart, lte: yearEnd } },
    }),
    prisma.laborContract.count({
      where: {
        orgId, signDate: { gte: yearStart, lte: yearEnd },
      },
    }),
    prisma.payrollBatch.count({
      where: { orgId, createdAt: { gte: yearStart, lte: yearEnd } },
    }),
    prisma.terminationRecord.count({
      where: { orgId, createdAt: { gte: yearStart, lte: yearEnd } },
    }),
    prisma.employee.count({
      where: {
        orgId,
        OR: [
          { hireDate: { gte: yearStart, lte: yearEnd } },
          { status: 'ACTIVE' },
        ],
      },
    }),
    prisma.auditLog.count({
      where: { orgId, createdAt: { gte: yearStart, lte: yearEnd } },
    }),
    prisma.policyDocument.count({
      where: {
        orgId, status: 'PUBLISHED',
        publishedAt: { gte: yearStart, lte: yearEnd },
      },
    }),
    Promise.all(
      Array.from({ length: 12 }, (_, m) =>
        prisma.riskItem.count({
          where: {
            orgId,
            createdAt: {
              gte: new Date(year, m, 1),
              lte: new Date(year, m + 1, 0, 23, 59, 59),
            },
          },
        }).then((count) => ({ month: m + 1, count })),
      ),
    ),
    // 按员工维度的人工解决风险明细（排除系统自动关闭的，只算用户真正处理过的）
    prisma.riskItem.findMany({
      where: {
        orgId, status: 'RESOLVED',
        resolvedBy: { not: null },
        resolvedAt: { gte: yearStart, lte: yearEnd },
        employeeId: { not: null },
      },
      include: {
        employee: {
          select: { id: true, name: true, department: true, monthlySalary: true, hireDate: true, idCardHash: true },
        },
      },
      orderBy: { estimatedLoss: 'desc' },
    }),
    // 未解决的风险列表（PENDING状态），排除无关联员工的月度任务，按预估损失降序，最多100条
    prisma.riskItem.findMany({
      where: {
        orgId, status: 'PENDING',
        estimatedLoss: { gt: 0 },
        employeeId: { not: null },
      },
      include: {
        employee: {
          select: { id: true, name: true, department: true, idCardHash: true },
        },
      },
      orderBy: { estimatedLoss: 'desc' },
      take: 100,
    }),
  ])

  // 按证件号码去重（同一人可能有多条 Employee 记录），无证件号码时回退到 employeeId
  // 同时按风险类型去重（同一风险被重复创建解决多次，只取 estimatedLoss 最大的一条）
  const personBreakdown: Record<string, {
    personKey: string
    name: string
    department: string
    riskCount: number
    lossAvoided: number
    adjustedLoss: number
    details: Map<string, { title: string; estimatedLoss: number; legalBasis: string; resolutionMethod: string }>
  }> = {}

  // 辅助：根据风险标题推断规避方式
  function getResolutionMethod(title: string): string {
    if (title.includes('未签合同') && title.includes('无固定期限')) return '补签书面合同'
    if (title.includes('未签合同')) return '签订书面合同'
    if (title.includes('到期') && title.includes('未续签')) return '续签合同'
    if (title.includes('即将到期')) return '续签或终止合同'
    if (title.includes('试用期') && title.includes('不合法')) return '调整试用期条款'
    if (title.includes('入职手续未完成')) return '完成入职手续'
    if (title.includes('孕期') || title.includes('哺乳期')) return '调整岗位/安排哺乳期待遇'
    if (title.includes('工伤')) return '申报工伤/安排适当工作'
    if (title.includes('医疗期')) return '保障医疗期待遇'
    if (title.includes('社保')) return '补缴社保'
    if (title.includes('公积金')) return '补缴公积金'
    if (title.includes('工资') || title.includes('个税')) return '发放工资/申报个税'
    return '人工处理'
  }

  // 辅助：根据风险标题推断法律依据
  function getLegalBasis(title: string): string {
    if (title.includes('未签合同')) return '第82条 双倍工资≤11个月'
    if (title.includes('到期') && title.includes('未续签')) return '第47条 经济补偿N≤12个月'
    if (title.includes('孕期') || title.includes('哺乳期') || title.includes('工伤') || title.includes('医疗期')) return '第87条 违法解除2N≤24个月'
    if (title.includes('试用期')) return '第20条 试用期工资差额'
    if (title.includes('社保') || title.includes('公积金')) return '滞纳金 每日0.05%'
    if (title.includes('工资') || title.includes('个税')) return '拖欠工资/个税'
    return '—'
  }

  // 辅助：根据风险标题归并到风险类别 key（去掉天数等动态部分）
  function getRiskCategoryKey(title: string): string {
    if (title.includes('未签合同') && title.includes('无固定期限')) return 'CONTRACT_UNSIGNED_OVER_YEAR'
    if (title.includes('未签合同') && title.includes('个月')) return 'CONTRACT_UNSIGNED'
    if (title.includes('到期') && title.includes('未续签')) return 'CONTRACT_EXPIRED'
    if (title.includes('即将到期')) return 'CONTRACT_EXPIRING'
    if (title.includes('试用期')) return 'CONTRACT_PROBATION'
    if (title.includes('入职手续未完成')) return 'ONBOARDING_INCOMPLETE'
    if (title.includes('孕期') || title.includes('哺乳期')) return 'SPECIAL_PREGNANCY'
    if (title.includes('工伤')) return 'SPECIAL_INJURY'
    if (title.includes('医疗期')) return 'SPECIAL_MEDICAL'
    if (title.includes('社保')) return 'MONTHLY_SOCIAL'
    if (title.includes('公积金')) return 'MONTHLY_HOUSING'
    if (title.includes('工资')) return 'MONTHLY_PAYROLL'
    if (title.includes('个税')) return 'MONTHLY_TAX'
    if (title.includes('退休')) return 'RETIREMENT'
    return title.replace(/\d+/g, '').trim()
  }

  for (const r of employeeRiskDetails) {
    // 去重优先级：证件号码 > 姓名回退到姓名，避免同一人多条 Employee 记录被重复计算
    const personKey = r.employee?.idCardHash || r.employee?.name || r.employeeId || '_unknown'
    if (!personBreakdown[personKey]) {
      personBreakdown[personKey] = {
        personKey,
        name: r.employee?.name || '未知员工',
        department: r.employee?.department || '-',
        riskCount: 0,
        lossAvoided: 0,
        adjustedLoss: 0,
        details: new Map(),
      }
    }
    const person = personBreakdown[personKey]

    // 按风险类别去重：同一类别只保留 estimatedLoss 最大的一条
    const catKey = getRiskCategoryKey(r.title || '')
    const existing = person.details.get(catKey)
    if (!existing || (r.estimatedLoss || 0) > existing.estimatedLoss) {
      person.details.set(catKey, {
        title: r.title,
        estimatedLoss: r.estimatedLoss || 0,
        legalBasis: getLegalBasis(r.title || ''),
        resolutionMethod: getResolutionMethod(r.title || ''),
      })
    }
  }

  // 汇总去重后的员工明细
  const employeeDetails = Object.values(personBreakdown)
    .map((p) => {
      const details = Array.from(p.details.values()).sort((a, b) => b.estimatedLoss - a.estimatedLoss)
      const lossAvoided = details.reduce((sum, d) => sum + d.estimatedLoss, 0)
      return {
        employeeId: p.personKey,
        name: p.name,
        department: p.department,
        riskCount: details.length,
        lossAvoided,
        adjustedLoss: lossAvoided,
        details,
      }
    })
    .sort((a, b) => b.adjustedLoss - a.adjustedLoss)

  // 用去重后的 lossAvoided 替换原始汇总值
  const dedupedLossAvoided = employeeDetails.reduce((sum, e) => sum + e.lossAvoided, 0)
  const lossAvoided = dedupedLossAvoided

  // 月度事件时间轴
  const timeline = monthlyRisks.map((mr) => ({
    month: mr.month,
    event: mr.count > 0 ? `发现 ${mr.count} 项风险` : '平稳运行',
    value: mr.count,
  }))

  // 价值计算
  // 1. 规避损失 = 已解决风险的预估损失之和（系统已发现并解决，全部潜在损失即为规避价值）
  // 2. 节约工时 = AI 查询 * 0.5h + 合同审查 * 1h + 算薪批次 * 2h + 制度公示 * 1h
  // 3. 节约成本 = 节约工时 * 100 元/h（HR 平均时薪）
  const adjustedLossAvoided = lossAvoided
  const aiTimeSaved = aiConversations * 0.5 + aiReviews * 1.0
  const payrollTimeSaved = payrollBatches * 2.0
  const policyTimeSaved = policiesPublished * 1.0
  const contractTimeSaved = contractsSigned * 0.5
  const timeSaved = Math.round(aiTimeSaved + payrollTimeSaved + policyTimeSaved + contractTimeSaved)
  const costSaved = timeSaved * 100

  // 总价值 = 规避损失 + 节约成本
  const totalValue = adjustedLossAvoided + costSaved

  const metrics = {
    risksResolved,
    lossAvoided,
    aiQueries: aiConversations,
    contractsReviewed: aiReviews,
    contractsSigned,
    payrollProcessed: payrollBatches,
    terminations,
    employeesManaged,
    auditActions,
    policiesPublished,
  }

  // 未解决风险按员工去重+风险类别去重（同一类别只取 estimatedLoss 最大的一条）
  const pendingBreakdown: Record<string, {
    name: string
    department: string
    riskCount: number
    totalLoss: number
    details: Map<string, { title: string; estimatedLoss: number; legalBasis: string }>
  }> = {}
  for (const r of pendingRisks) {
    const personKey = r.employee?.idCardHash || r.employee?.name || r.employeeId || '_unknown'
    if (!pendingBreakdown[personKey]) {
      pendingBreakdown[personKey] = {
        name: r.employee?.name || '未知员工',
        department: r.employee?.department || '-',
        riskCount: 0,
        totalLoss: 0,
        details: new Map(),
      }
    }
    const catKey = getRiskCategoryKey(r.title)
    const loss = r.estimatedLoss || 0
    const existing = pendingBreakdown[personKey].details.get(catKey)
    if (!existing || loss > existing.estimatedLoss) {
      pendingBreakdown[personKey].details.set(catKey, {
        title: r.title,
        estimatedLoss: loss,
        legalBasis: getLegalBasis(r.title),
      })
    }
  }
  const pendingRiskDetails = Object.values(pendingBreakdown)
    .map(p => {
      const details = Array.from(p.details.values()).sort((a, b) => b.estimatedLoss - a.estimatedLoss)
      return {
        name: p.name,
        department: p.department,
        riskCount: details.length,
        totalLoss: details.reduce((sum, d) => sum + d.estimatedLoss, 0),
        details,
      }
    })
    .sort((a, b) => b.totalLoss - a.totalLoss)

  const summary = `${year} 年规避潜在损失 ${fmtMoney(lossAvoided)}，节约 HR 工时 ${timeSaved} 小时，仍有 ${pendingRiskDetails.length} 人待处理风险`

  return {
    year,
    totalValue,
    metrics,
    timeline,
    costSaved,
    timeSaved,
    lossAvoided,
    adjustedLossAvoided,
    employeeDetails,
    pendingRiskDetails,
    summary,
  }
}

/**
 * 保存年度价值报告
 */
export async function saveAnnualValueReport(orgId: string, userId: string, year: number) {
  const result = await getAnnualValueReport(orgId, year)

  const report = await (prisma as any).annualValueReport.create({
    data: {
      orgId,
      year: result.year,
      totalValue: result.totalValue,
      metrics: result.metrics as any,
      timeline: result.timeline as any,
      costSaved: result.costSaved,
      timeSaved: result.timeSaved,
      summary: result.summary,
      createdBy: userId,
    },
  })

  return report
}

/**
 * 获取历史年度价值报告
 */
export async function getAnnualValueReportHistory(orgId: string) {
  const reports = await (prisma as any).annualValueReport.findMany({
    where: { orgId },
    orderBy: { year: 'desc' },
    take: 10,
  })

  return (reports as any[]).map((r: any) => ({
    id: r.id,
    year: r.year,
    totalValue: r.totalValue,
    roi: r.roi,
    summary: r.summary,
    createdAt: r.createdAt.toISOString(),
  }))
}
