import prisma from '../lib/prisma'
import { evalFormula, topologicalSort } from './formula-engine'

// ========== 社保公积金账户辅助函数 ==========

/**
 * 通过员工获取适用的社保/公积金账户
 * 优先从员工所属根部门（level=0）关联的账户继承，回退到公司默认账户
 */
async function getEmployeeAccounts(orgId: string, employeeId: string) {
  const emp = await prisma.employee.findFirst({
    where: { id: employeeId, orgId },
    include: { dept: true },
  })
  if (!emp) return { socialAccount: null, housingAccount: null }

  // 向上找到 level=0 的根部门
  let currentDept: any = emp.dept
  while (currentDept && currentDept.level > 0 && currentDept.parentId) {
    currentDept = await prisma.department.findUnique({ where: { id: currentDept.parentId } })
  }
  const rootDeptId = currentDept?.id || null

  let socialAccount: any = null
  let housingAccount: any = null
  if (rootDeptId) {
    const rootDept = await prisma.department.findUnique({
      where: { id: rootDeptId },
      include: { socialAccount: true, housingAccount: true },
    })
    socialAccount = rootDept?.socialAccount || null
    housingAccount = rootDept?.housingAccount || null
  }

  // 回退到公司默认账户
  if (!socialAccount) {
    socialAccount = await prisma.socialAccount.findFirst({ where: { orgId, type: 'SOCIAL', isDefault: true } })
  }
  if (!housingAccount) {
    housingAccount = await prisma.socialAccount.findFirst({ where: { orgId, type: 'HOUSING', isDefault: true } })
  }

  return { socialAccount, housingAccount }
}

/**
 * 通过账户获取指定月份的年度标准
 */
async function getStandardByAccountAndMonth(accountId: string, month: string) {
  const standard = await prisma.socialYearStandard.findFirst({
    where: {
      accountId,
      effectiveFrom: { lte: month },
      OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }],
    },
    orderBy: { effectiveFrom: 'desc' },
  })
  if (!standard) {
    // 回退到当前生效标准
    return prisma.socialYearStandard.findFirst({
      where: { accountId, isCurrent: true },
      orderBy: { effectiveFrom: 'desc' },
    })
  }
  return standard
}

// ========== 薪酬模版 ==========

const DEFAULT_ITEMS: { name: string; code: string; type: 'INPUT' | 'CALCULATED'; formula: string | null; calcType: string; dependencies: string; dataSource: string | null; order: number; isDefault: boolean; isEditable: boolean }[] = [
  { name: '基本工资', code: 'baseSalary', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 1, isDefault: true, isEditable: true },
  { name: '岗位工资', code: 'positionSalary', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 2, isDefault: true, isEditable: true },
  { name: '绩效工资', code: 'performanceSalary', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: 'performance', order: 3, isDefault: true, isEditable: true },
  { name: '工龄工资', code: 'senioritySalary', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 4, isDefault: true, isEditable: true },
  { name: '加班费', code: 'overtimePay', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: 'overtime', order: 5, isDefault: true, isEditable: true },
  { name: '交通补贴', code: 'transportAllowance', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 6, isDefault: true, isEditable: true },
  { name: '餐补', code: 'mealAllowance', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 7, isDefault: true, isEditable: true },
  { name: '住房补贴', code: 'housingAllowance', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 8, isDefault: true, isEditable: true },
  { name: '通讯补贴', code: 'communicationAllowance', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 9, isDefault: true, isEditable: true },
  { name: '津贴补贴', code: 'allowance', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 10, isDefault: true, isEditable: true },
  { name: '奖金', code: 'bonus', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: 'bonus', order: 11, isDefault: true, isEditable: true },
  { name: '扣款', code: 'deduction', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: 'deduction', order: 12, isDefault: true, isEditable: true },
  { name: '其他扣款', code: 'otherDeduction', type: 'INPUT', formula: null, calcType: 'FORMULA', dependencies: '[]', dataSource: null, order: 13, isDefault: true, isEditable: true },
  { name: '应发合计', code: 'totalPay', type: 'CALCULATED', formula: 'baseSalary + positionSalary + performanceSalary + senioritySalary + overtimePay + transportAllowance + mealAllowance + housingAllowance + communicationAllowance + allowance + bonus - deduction - otherDeduction', calcType: 'FORMULA', dependencies: '["baseSalary","positionSalary","performanceSalary","senioritySalary","overtimePay","transportAllowance","mealAllowance","housingAllowance","communicationAllowance","allowance","bonus","deduction","otherDeduction"]', dataSource: null, order: 14, isDefault: true, isEditable: false },
  { name: '个人社保', code: 'socialEmp', type: 'CALCULATED', formula: 'SOCIAL_EMP', calcType: 'SYSTEM', dependencies: '[]', dataSource: null, order: 15, isDefault: true, isEditable: false },
  { name: '个人公积金', code: 'housingEmp', type: 'CALCULATED', formula: 'HOUSING_EMP', calcType: 'SYSTEM', dependencies: '[]', dataSource: null, order: 16, isDefault: true, isEditable: false },
  { name: '个人所得税', code: 'tax', type: 'CALCULATED', formula: 'TAX', calcType: 'SYSTEM', dependencies: '["totalPay","socialEmp","housingEmp"]', dataSource: null, order: 17, isDefault: true, isEditable: false },
  { name: '实发工资', code: 'netPay', type: 'CALCULATED', formula: 'totalPay - socialEmp - housingEmp - tax', calcType: 'FORMULA', dependencies: '["totalPay","socialEmp","housingEmp","tax"]', dataSource: null, order: 18, isDefault: true, isEditable: false },
]

export async function ensureDefaultTemplate(orgId: string) {
  const existing = await prisma.payslipItem.count({ where: { orgId } })
  if (existing === 0) {
    await prisma.payslipItem.createMany({
      data: DEFAULT_ITEMS.map(item => ({ ...item, orgId })),
    })
  } else {
    // 迁移：为已有记录补充 calcType、dependencies、dataSource 字段，并修正 overtimePay 类型
    for (const item of DEFAULT_ITEMS) {
      const existing = await prisma.payslipItem.findFirst({ where: { orgId, code: item.code } })
      if (!existing) continue
      const updates: any = {}
      // 补充 SYSTEM calcType
      if ((!existing.calcType || existing.calcType === 'FORMULA') && item.calcType === 'SYSTEM') {
        updates.calcType = 'SYSTEM'
        updates.dependencies = item.dependencies
      }
      // 补充 dataSource
      if (!existing.dataSource && item.dataSource) {
        updates.dataSource = item.dataSource
      }
      // 修正 overtimePay：从 CALCULATED 改为 INPUT
      if (item.code === 'overtimePay' && existing.type === 'CALCULATED') {
        updates.type = 'INPUT'
        updates.formula = null
        updates.calcType = 'FORMULA'
        updates.dependencies = '[]'
        updates.isEditable = true
      }
      if (Object.keys(updates).length > 0) {
        await prisma.payslipItem.update({ where: { id: existing.id }, data: updates })
      }
    }
  }
}

export async function getTemplate(orgId: string) {
  await ensureDefaultTemplate(orgId)
  return prisma.payslipItem.findMany({
    where: { orgId },
    orderBy: { order: 'asc' },
  })
}

// ========== 社保计算 ==========

export function calcSocialInsurance(base: number, config: any) {
  // 养老/失业/工伤保险基数
  const actualBase = Math.min(Math.max(base, config.baseMin), config.baseMax)
  // 医疗/生育保险基数（独立上下限，为 0 时 fallback 到统一基数）
  const medMin = config.medicalBaseMin && config.medicalBaseMin > 0 ? config.medicalBaseMin : config.baseMin
  const medMax = config.medicalBaseMax && config.medicalBaseMax > 0 ? config.medicalBaseMax : config.baseMax
  const medicalBase = Math.min(Math.max(base, medMin), medMax)
  let socialEmp = actualBase * (config.pensionEmp + config.unemploymentEmp) / 100 + medicalBase * config.medicalEmp / 100 + (config.medicalEmpExtra || 0)
  let socialOrg = actualBase * (config.pensionOrg + config.unemploymentOrg + config.injuryOrg) / 100 + medicalBase * (config.medicalOrg + config.maternityOrg) / 100 + (config.medicalOrgExtra || 0)
  // 附加险种（大病险/长护险等）
  const extraItems: any[] = []
  if (config.extraInsurances && Array.isArray(config.extraInsurances)) {
    for (const ins of config.extraInsurances as any[]) {
      const insBase = ins.baseType === 'medical' ? medicalBase : ins.baseType === 'fixed' ? 1 : actualBase
      if (ins.baseType === 'fixed' && ins.fixedAmount) {
        const orgAmt = ins.fixedAmount
        const empAmt = ins.empFixedAmount || 0
        socialOrg += orgAmt
        socialEmp += empAmt
        extraItems.push({ name: ins.name, orgAmount: orgAmt, empAmount: empAmt })
      } else {
        const orgAmt = insBase * (ins.orgRate || 0) / 100
        const empAmt = insBase * (ins.empRate || 0) / 100
        socialOrg += orgAmt
        socialEmp += empAmt
        extraItems.push({ name: ins.name, orgAmount: orgAmt, empAmount: empAmt })
      }
    }
  }
  return { actualBase, medicalBase, socialEmp, socialOrg, extraItems }
}

export function calcHousingFund(base: number, config: any) {
  // 公积金专用上下限（housingBaseMin/Max），为0时回退到社保的 baseMin/baseMax
  const min = (config.housingBaseMin && config.housingBaseMin > 0) ? config.housingBaseMin : config.baseMin
  const max = (config.housingBaseMax && config.housingBaseMax > 0) ? config.housingBaseMax : config.baseMax
  const actualBase = Math.min(Math.max(base, min), max)
  const housingEmp = actualBase * config.housingEmp / 100
  const housingOrg = actualBase * config.housingOrg / 100
  return { actualBase, housingEmp, housingOrg }
}

// ========== 累计预扣个税 ==========

export function calcTax(taxableIncome: number): number {
  if (taxableIncome <= 0) return 0
  let tax = 0
  if (taxableIncome <= 36000) tax = taxableIncome * 0.03
  else if (taxableIncome <= 144000) tax = taxableIncome * 0.10 - 2520
  else if (taxableIncome <= 300000) tax = taxableIncome * 0.20 - 16920
  else if (taxableIncome <= 420000) tax = taxableIncome * 0.25 - 31920
  else if (taxableIncome <= 660000) tax = taxableIncome * 0.30 - 52920
  else if (taxableIncome <= 960000) tax = taxableIncome * 0.35 - 85920
  else tax = taxableIncome * 0.45 - 181920
  return Math.max(0, Math.round(tax * 100) / 100)
}

/**
 * 累计预扣法计算当月个税
 * @param ytdTaxableIncome 当年累计应纳税所得额（含当月）
 * @param ytdTaxDeducted 当年累计已预扣税额
 * @returns 当月应预扣税额
 */
export function calcCumulativeTax(ytdTaxableIncome: number, ytdTaxDeducted: number): number {
  const ytdTax = calcTax(ytdTaxableIncome)
  const currentMonthTax = Math.max(0, ytdTax - ytdTaxDeducted)
  return Math.round(currentMonthTax * 100) / 100
}

/**
 * 年终奖单独计税
 * @param bonusAmount 奖金金额
 * @returns 应纳税额
 */
export function calcBonusTax(bonusAmount: number): number {
  if (bonusAmount <= 0) return 0
  const monthlyBonus = bonusAmount / 12
  let rate = 0.03
  let quickDeduction = 0
  if (monthlyBonus <= 3000) { rate = 0.03; quickDeduction = 0 }
  else if (monthlyBonus <= 12000) { rate = 0.10; quickDeduction = 210 }
  else if (monthlyBonus <= 25000) { rate = 0.20; quickDeduction = 1410 }
  else if (monthlyBonus <= 35000) { rate = 0.25; quickDeduction = 2660 }
  else if (monthlyBonus <= 55000) { rate = 0.30; quickDeduction = 4410 }
  else if (monthlyBonus <= 80000) { rate = 0.35; quickDeduction = 7160 }
  else { rate = 0.45; quickDeduction = 15160 }
  const tax = bonusAmount * rate - quickDeduction
  return Math.max(0, Math.round(tax * 100) / 100)
}

// ========== 系统计算器注册表 ==========

interface SystemCalcContext {
  orgId: string
  employeeId: string
  month: string
  batchType: string
  employee: any
  socialConfig: any
  housingConfig: any
  supplementaryHousingConfig: any
  socialBase: number
  housingBase: number
  isNoSocialContract: boolean
  options: any
  values: Record<string, number>
  // Side-effect outputs (org-side and supplementary amounts not in template)
  socialOrg: number
  housingOrg: number
  supplementaryHousingEmp: number
  supplementaryHousingOrg: number
  taxBreakdown: any
  // System values (before override)
  systemSocialEmp: number
  systemSocialOrg: number
  systemHousingEmp: number
  systemHousingOrg: number
  systemSuppHousingEmp: number
  systemSuppHousingOrg: number
  // Internal cache flag
  _socialCalculated: boolean
}

type SystemCalcFn = (ctx: SystemCalcContext) => Promise<number>

/**
 * 社保+公积金联合计算（缓存结果，避免重复查询数据库）
 */
async function ensureSocialHousingCalculated(ctx: SystemCalcContext) {
  if (ctx._socialCalculated) return
  ctx._socialCalculated = true

  const { orgId, employeeId, month, batchType, socialConfig, housingConfig, supplementaryHousingConfig, socialBase, housingBase, isNoSocialContract, options } = ctx

  let socialEmp = 0, socialOrg = 0, housingEmp = 0, housingOrg = 0
  let suppHousingEmp = 0, suppHousingOrg = 0

  if (batchType !== 'BONUS' && batchType !== 'SEVERANCE' && !options?.skipSocial && !isNoSocialContract) {
    let fullSocialEmp = 0, fullSocialOrg = 0, fullHousingEmp = 0, fullHousingOrg = 0
    let fullSuppHousingEmp = 0, fullSuppHousingOrg = 0
    if (socialConfig) {
      const social = calcSocialInsurance(socialBase, socialConfig)
      fullSocialEmp = social.socialEmp
      fullSocialOrg = social.socialOrg
    }
    if (housingConfig) {
      const housing = calcHousingFund(housingBase, housingConfig)
      fullHousingEmp = housing.housingEmp
      fullHousingOrg = housing.housingOrg
    }
    if (supplementaryHousingConfig) {
      const suppHousing = calcHousingFund(housingBase, supplementaryHousingConfig)
      fullSuppHousingEmp = suppHousing.housingEmp
      fullSuppHousingOrg = suppHousing.housingOrg
    }

    const archivedEntries = await prisma.batchEntry.findMany({
      where: { orgId, employeeId, batch: { month, status: 'ARCHIVED', type: { in: ['REGULAR', 'TERMINATION'] } } },
      select: { socialEmp: true, socialOrg: true, housingEmp: true, housingOrg: true, supplementaryHousingEmp: true, supplementaryHousingOrg: true },
    })
    const deductedSocialEmp = archivedEntries.reduce((s, e) => s + e.socialEmp, 0)
    const deductedSocialOrg = archivedEntries.reduce((s, e) => s + e.socialOrg, 0)
    const deductedHousingEmp = archivedEntries.reduce((s, e) => s + e.housingEmp, 0)
    const deductedHousingOrg = archivedEntries.reduce((s, e) => s + e.housingOrg, 0)
    const deductedSuppHousingEmp = archivedEntries.reduce((s, e) => s + (e.supplementaryHousingEmp || 0), 0)
    const deductedSuppHousingOrg = archivedEntries.reduce((s, e) => s + (e.supplementaryHousingOrg || 0), 0)

    socialEmp = Math.max(0, fullSocialEmp - deductedSocialEmp)
    socialOrg = Math.max(0, fullSocialOrg - deductedSocialOrg)
    housingEmp = Math.max(0, fullHousingEmp - deductedHousingEmp)
    housingOrg = Math.max(0, fullHousingOrg - deductedHousingOrg)
    suppHousingEmp = Math.max(0, fullSuppHousingEmp - deductedSuppHousingEmp)
    suppHousingOrg = Math.max(0, fullSuppHousingOrg - deductedSuppHousingOrg)

    if (options?.prevDeferred) {
      socialEmp += options.prevDeferred.socialEmp || 0
      housingEmp += options.prevDeferred.housingEmp || 0
    }
  }

  ctx.values.socialEmp = socialEmp
  ctx.values.housingEmp = housingEmp
  ctx.socialOrg = socialOrg
  ctx.housingOrg = housingOrg
  ctx.supplementaryHousingEmp = suppHousingEmp
  ctx.supplementaryHousingOrg = suppHousingOrg
  ctx.systemSocialEmp = socialEmp
  ctx.systemSocialOrg = socialOrg
  ctx.systemHousingEmp = housingEmp
  ctx.systemHousingOrg = housingOrg
  ctx.systemSuppHousingEmp = suppHousingEmp
  ctx.systemSuppHousingOrg = suppHousingOrg
}

/**
 * 个税系统计算函数
 */
async function calcTaxSystem(ctx: SystemCalcContext): Promise<number> {
  const { orgId, employeeId, month, batchType, employee, values } = ctx
  const totalPay = values.totalPay || 0
  const socialEmp = values.socialEmp || 0
  const housingEmp = values.housingEmp || 0
  const suppHousingEmp = ctx.supplementaryHousingEmp

  if (batchType === 'BONUS') {
    const tax = calcBonusTax(values.bonus || 0)
    ctx.taxBreakdown = { method: '单独计税（年终奖）', bonus: values.bonus || 0, tax }
    return tax
  }

  const year = month.slice(0, 4)
  const archivedEntries = await prisma.batchEntry.findMany({
    where: { orgId, employeeId, batch: { month: { startsWith: year }, status: 'ARCHIVED' } },
    select: { totalPay: true, socialEmp: true, housingEmp: true, supplementaryHousingEmp: true, tax: true },
  })
  const ytdIncome = archivedEntries.reduce((s, e) => s + e.totalPay, 0) + totalPay
  const ytdSocialEmp = archivedEntries.reduce((s, e) => s + e.socialEmp, 0) + socialEmp
  const ytdHousingEmp = archivedEntries.reduce((s, e) => s + e.housingEmp + (e.supplementaryHousingEmp || 0), 0) + housingEmp + suppHousingEmp

  const deductionRecords = await prisma.specialDeductionRecord.findMany({
    where: { orgId, employeeId, month: { startsWith: year, lte: month } },
    select: { amount: true, month: true },
  })
  let ytdSpecialDeduction: number
  if (deductionRecords.length > 0) {
    ytdSpecialDeduction = deductionRecords.reduce((s, r) => s + r.amount, 0)
  } else {
    ytdSpecialDeduction = employee.specialDeduction * Number(month.slice(5, 7))
  }

  const ytdTaxDeducted = archivedEntries.reduce((s, e) => s + e.tax, 0)
  const deductionAmount = 5000 * Number(month.slice(5, 7))
  const ytdTaxableIncome = Math.max(0, ytdIncome - deductionAmount - ytdSocialEmp - ytdHousingEmp - ytdSpecialDeduction)
  const tax = calcCumulativeTax(ytdTaxableIncome, ytdTaxDeducted)
  ctx.taxBreakdown = {
    method: '累计预扣法',
    month: Number(month.slice(5, 7)),
    ytdIncome, deductionAmount, ytdSocialEmp, ytdHousingEmp, ytdSpecialDeduction,
    specialDeductionSource: deductionRecords.length > 0 ? '按月记录' : '便捷字段',
    specialDeductionRecords: deductionRecords.length,
    ytdTaxableIncome, ytdTaxDeducted, currentMonthTax: tax, archivedCount: archivedEntries.length,
  }
  return tax
}

/**
 * 系统计算器注册表
 * key 对应 PayslipItem.formula 中的 SYSTEM 标识
 */
const systemCalculators: Record<string, SystemCalcFn> = {
  SOCIAL_EMP: async (ctx) => {
    await ensureSocialHousingCalculated(ctx)
    return ctx.values.socialEmp
  },
  HOUSING_EMP: async (ctx) => {
    await ensureSocialHousingCalculated(ctx)
    return ctx.values.housingEmp
  },
  TAX: calcTaxSystem,
}

// ========== 批次计算 ==========

export async function calcBatchEntry(
  orgId: string,
  employeeId: string,
  month: string,
  inputs: { baseSalary: number; overtimePay: number; allowance: number; deduction: number; bonus: number; positionSalary?: number; performanceSalary?: number; senioritySalary?: number; transportAllowance?: number; mealAllowance?: number; housingAllowance?: number; communicationAllowance?: number; otherDeduction?: number; [key: string]: any },
  batchType: string = 'REGULAR',
  options?: { skipSocial?: boolean; overrideSocial?: { socialEmp?: number; socialOrg?: number; housingEmp?: number; housingOrg?: number }; prevDeferred?: { socialEmp?: number; housingEmp?: number; minWage?: number } },
  extraItems?: Record<string, number> | null,
) {
  const employee = await prisma.employee.findFirst({
    where: { id: employeeId, orgId },
    include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
  })
  if (!employee) throw { code: 'NOT_FOUND', message: '员工不存在' }

  // 劳务协议/实习协议人员：不缴纳社保公积金，基数强制 0
  const latestContract = employee.contracts[0]
  const isNoSocialContract = latestContract && ['LABOR', 'INTERNSHIP', 'PARTTIME', 'OUTSOURCING', 'UNSIGNED'].includes(latestContract.contractType)

  // 通过员工账户查年度标准（新逻辑），回退到旧配置（兼容）
  const { socialAccount, housingAccount } = await getEmployeeAccounts(orgId, employeeId)
  let socialConfig: any = null
  let housingConfig: any = null
  let supplementaryHousingConfig: any = null

  if (socialAccount) {
    socialConfig = await getStandardByAccountAndMonth(socialAccount.id, month)
  }
  if (housingAccount) {
    housingConfig = await getStandardByAccountAndMonth(housingAccount.id, month)
  }
  // 补充公积金：员工单独关联的补充公积金账户
  if (employee.supplementaryHousingAccountId) {
    supplementaryHousingConfig = await getStandardByAccountAndMonth(employee.supplementaryHousingAccountId, month)
  }

  // 回退到旧配置表（兼容未迁移数据）
  const cityWhere = employee.city ? { orgId, city: employee.city } : { orgId }
  if (!socialConfig) {
    socialConfig = await prisma.socialInsuranceConfig.findFirst({
      where: { ...cityWhere, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
      orderBy: { effectiveFrom: 'desc' },
    })
  }
  if (!housingConfig) {
    housingConfig = await prisma.housingFundConfig.findFirst({
      where: { ...cityWhere, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
      orderBy: { effectiveFrom: 'desc' },
    })
  }

  // 社保基数：劳务协议/实习协议人员强制 0；否则优先用员工核定基数（含0），未设置时回退到基本工资
  const socialBase = isNoSocialContract ? 0 : (employee.socialInsBase != null ? employee.socialInsBase : inputs.baseSalary)
  const housingBase = isNoSocialContract ? 0 : (employee.housingFundBase != null ? employee.housingFundBase : inputs.baseSalary)

  // ── 模板驱动计算 ──
  const templateItems = await getTemplate(orgId)

  // 构建系统计算上下文
  const ctx: SystemCalcContext = {
    orgId, employeeId, month, batchType, employee,
    socialConfig, housingConfig, supplementaryHousingConfig,
    socialBase, housingBase, isNoSocialContract, options,
    values: {},
    socialOrg: 0, housingOrg: 0,
    supplementaryHousingEmp: 0, supplementaryHousingOrg: 0,
    taxBreakdown: null,
    systemSocialEmp: 0, systemSocialOrg: 0,
    systemHousingEmp: 0, systemHousingOrg: 0,
    systemSuppHousingEmp: 0, systemSuppHousingOrg: 0,
    _socialCalculated: false,
  }

  // 1. 初始化输入项：先从固定列 inputs 取，再从 extraItems 补充自定义项
  for (const item of templateItems) {
    if (item.type === 'INPUT') {
      ctx.values[item.code] = (inputs as any)[item.code] ?? (extraItems?.[item.code] ?? 0)
    }
  }

  // 2. 按拓扑排序计算所有项
  const sorted = topologicalSort(templateItems)
  for (const item of sorted) {
    if (item.type === 'INPUT') continue

    // 在计算 tax 前应用手动覆盖（覆盖社保值）
    if (item.code === 'tax' && options?.overrideSocial) {
      if (options.overrideSocial.socialEmp !== undefined) ctx.values.socialEmp = options.overrideSocial.socialEmp
      if (options.overrideSocial.housingEmp !== undefined) ctx.values.housingEmp = options.overrideSocial.housingEmp
      if (options.overrideSocial.socialOrg !== undefined) ctx.socialOrg = options.overrideSocial.socialOrg
      if (options.overrideSocial.housingOrg !== undefined) ctx.housingOrg = options.overrideSocial.housingOrg
    }

    if (item.calcType === 'SYSTEM' && item.formula) {
      const fn = systemCalculators[item.formula]
      if (fn) {
        ctx.values[item.code] = await fn(ctx)
      }
    } else if (item.formula) {
      ctx.values[item.code] = evalFormula(item.formula, ctx.values)
    }
  }

  // 3. 提取计算结果
  let socialEmp = ctx.values.socialEmp || 0
  let socialOrg = ctx.socialOrg
  let housingEmp = ctx.values.housingEmp || 0
  let housingOrg = ctx.housingOrg
  let suppHousingEmp = ctx.supplementaryHousingEmp
  let suppHousingOrg = ctx.supplementaryHousingOrg
  const systemSocialEmp = ctx.systemSocialEmp
  const systemSocialOrg = ctx.systemSocialOrg
  const systemHousingEmp = ctx.systemHousingEmp
  const systemHousingOrg = ctx.systemHousingOrg
  const systemSupplementaryHousingEmp = ctx.systemSuppHousingEmp
  const systemSupplementaryHousingOrg = ctx.systemSuppHousingOrg
  let tax = ctx.values.tax || 0
  const taxBreakdown = ctx.taxBreakdown
  let totalPay = ctx.values.totalPay || 0
  let netPay = ctx.values.netPay || 0

  // ── 最低工资保护 + 递延扣款逻辑 ──
  // 读取最低工资标准（从年度标准或旧配置表）
  let minWage = 0
  if (socialConfig && (socialConfig as any).minWage) {
    minWage = (socialConfig as any).minWage
  }

  // 上月递延的最低工资补齐差额（本批次需扣回）
  const prevDeferredMinWage = options?.prevDeferred?.minWage || 0

  // netPay 已由模板公式计算（公式中不含补充公积金和递延项），此处补扣
  netPay = netPay - suppHousingEmp - prevDeferredMinWage

  // 递延金额（本批次扣不动、递延到次月的部分）
  let deferredSocialEmp = 0
  let deferredHousingEmp = 0
  let deferredMinWage = 0
  let minWageApplied = 0  // 当月实际补齐到最低工资的金额

  // 最低工资保护：当月累计实发不得低于最低工资标准（仅 REGULAR / TERMINATION 批次）
  // 第二批次时需判断"当月累计实发"（已归档批次 netPay 之和 + 本批次 netPay）是否低于 minWage
  if (minWage > 0 && batchType !== 'BONUS' && batchType !== 'SEVERANCE') {
    // 查当月已归档批次的累计实发（同月已归档的 REGULAR/TERMINATION 批次）
    const archivedNetPayEntries = await prisma.batchEntry.findMany({
      where: {
        orgId,
        employeeId,
        batch: { month, status: 'ARCHIVED', type: { in: ['REGULAR', 'TERMINATION'] } },
      },
      select: { netPay: true },
    })
    const archivedNetPay = archivedNetPayEntries.reduce((s, e) => s + e.netPay, 0)

    // 当月累计实发 = 已归档批次实发 + 本批次实发
    const monthlyCumulativeNetPay = archivedNetPay + netPay

    // 仅当当月累计实发低于最低工资时才触发保护
    if (monthlyCumulativeNetPay < minWage) {
      // 需要补齐的金额 = 最低工资 - 当月累计实发
      // 但本批次最多补齐到本批次实发为正，且不超过 minWage - archivedNetPay
      const targetNetPay = Math.max(0, minWage - archivedNetPay)
      const shortfall = targetNetPay - netPay  // 需要补齐的金额（正数表示需要补齐）

      if (shortfall > 0) {
        // 优先递延社保个人部分（减少当月社保扣款）
        if (shortfall <= socialEmp) {
          // 只递延社保就够了
          deferredSocialEmp = shortfall
          socialEmp -= shortfall
          netPay = targetNetPay
          minWageApplied = shortfall
        } else if (shortfall <= socialEmp + housingEmp + suppHousingEmp) {
          // 递延全部社保 + 部分公积金（含补充）
          deferredSocialEmp = socialEmp
          socialEmp = 0
          const housingShortfall = shortfall - deferredSocialEmp
          // 先递延基本公积金，再递延补充公积金
          if (housingShortfall <= housingEmp) {
            deferredHousingEmp = housingShortfall
            housingEmp -= housingShortfall
          } else {
            deferredHousingEmp = housingEmp
            const suppShortfall = housingShortfall - housingEmp
            housingEmp = 0
            suppHousingEmp -= suppShortfall
          }
          netPay = targetNetPay
          minWageApplied = shortfall
        } else {
          // 递延全部社保 + 全部公积金（含补充），仍不足 → 差额作为最低工资补齐递延
          deferredSocialEmp = socialEmp
          deferredHousingEmp = housingEmp
          const remainingShortfall = shortfall - socialEmp - housingEmp - suppHousingEmp
          socialEmp = 0
          housingEmp = 0
          suppHousingEmp = 0
          deferredMinWage = remainingShortfall
          netPay = targetNetPay
          minWageApplied = shortfall
        }
      }
    }
  }

  // 提取非预置项的计算结果到 extraItems
  const presetCodes = new Set(DEFAULT_ITEMS.map(i => i.code))
  const resultExtraItems: Record<string, number> = {}
  for (const item of templateItems) {
    if (!presetCodes.has(item.code) && ctx.values[item.code] !== undefined) {
      resultExtraItems[item.code] = Math.round((ctx.values[item.code] || 0) * 100) / 100
    }
  }

  return {
    socialEmp: Math.round(socialEmp * 100) / 100,
    socialOrg: Math.round(socialOrg * 100) / 100,
    housingEmp: Math.round(housingEmp * 100) / 100,
    housingOrg: Math.round(housingOrg * 100) / 100,
    supplementaryHousingEmp: Math.round(suppHousingEmp * 100) / 100,
    supplementaryHousingOrg: Math.round(suppHousingOrg * 100) / 100,
    systemSocialEmp: Math.round(systemSocialEmp * 100) / 100,
    systemSocialOrg: Math.round(systemSocialOrg * 100) / 100,
    systemHousingEmp: Math.round(systemHousingEmp * 100) / 100,
    systemHousingOrg: Math.round(systemHousingOrg * 100) / 100,
    systemSupplementaryHousingEmp: Math.round(systemSupplementaryHousingEmp * 100) / 100,
    systemSupplementaryHousingOrg: Math.round(systemSupplementaryHousingOrg * 100) / 100,
    tax,
    taxBreakdown,
    totalPay: Math.round(totalPay * 100) / 100,
    netPay: Math.round(netPay * 100) / 100,
    // 最低工资保护 + 递延信息
    minWage,
    minWageApplied: Math.round(minWageApplied * 100) / 100,
    deferredSocialEmp: Math.round(deferredSocialEmp * 100) / 100,
    deferredHousingEmp: Math.round(deferredHousingEmp * 100) / 100,
    deferredMinWage: Math.round(deferredMinWage * 100) / 100,
    prevDeferredSocialEmp: options?.prevDeferred?.socialEmp || 0,
    prevDeferredHousingEmp: options?.prevDeferred?.housingEmp || 0,
    prevDeferredMinWage,
    extraItems: resultExtraItems,
  }
}

// ========== 风险提示 ==========

export async function getPayrollRiskWarnings(orgId: string, employeeId: string): Promise<string[]> {
  const warnings: string[] = []
  const employee = await prisma.employee.findFirst({
    where: { id: employeeId, orgId },
    include: {
      contracts: { orderBy: { createdAt: 'desc' }, take: 1 },
      terminations: { orderBy: { createdAt: 'desc' }, take: 1 },
    },
  })
  if (!employee) return warnings

  if (employee.status === 'RESIGNED') {
    warnings.push('该员工已离职，需进行离职结算')
  }
  if (!employee.contracts.length || employee.contracts[0].contractType === 'UNSIGNED') {
    warnings.push('未签订书面劳动合同')
  }
  if (employee.contracts.length) {
    const contract = employee.contracts[0]
    if (contract.endDate) {
      const daysToExpiry = Math.ceil((new Date(contract.endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24))
      if (daysToExpiry <= 30 && daysToExpiry > 0) {
        warnings.push(`合同将于 ${daysToExpiry} 天后到期`)
      }
    }
    if (contract.probationMonths > 0 && contract.startDate) {
      const probationEnd = new Date(contract.startDate)
      probationEnd.setMonth(probationEnd.getMonth() + contract.probationMonths)
      if (probationEnd > new Date()) {
        warnings.push('试用期员工，薪资可能不同')
      }
    }
  }
  if (!employee.socialInsBase) {
    warnings.push('未设置社保缴费基数')
  }
  if (!employee.housingFundBase) {
    warnings.push('未设置公积金缴费基数')
  }
  if (employee.terminations.some((t) => t.status !== 'CANCELLED')) {
    warnings.push('已有解聘记录，请注意结算')
  }

  return warnings
}

// ========== 工资条汇总生成 ==========

export async function generatePayslipFromBatches(orgId: string, month: string) {
  // 获取当月所有已归档批次
  const batches = await prisma.payrollBatch.findMany({
    where: { orgId, month, status: 'ARCHIVED' },
    include: { entries: true },
  })
  if (batches.length === 0) return { generated: 0 }

  // 按员工汇总
  const employeeMap = new Map<string, any>()
  for (const batch of batches) {
    for (const entry of batch.entries) {
      const existing = employeeMap.get(entry.employeeId) || {
        baseSalary: 0, overtimePay: 0, allowance: 0, deduction: 0, bonus: 0,
        positionSalary: 0, performanceSalary: 0, senioritySalary: 0,
        transportAllowance: 0, mealAllowance: 0, housingAllowance: 0, communicationAllowance: 0,
        otherDeduction: 0,
        socialEmp: 0, socialOrg: 0, housingEmp: 0, housingOrg: 0, tax: 0,
        totalPay: 0, netPay: 0,
      }
      existing.baseSalary += entry.baseSalary
      existing.overtimePay += entry.overtimePay
      existing.allowance += entry.allowance
      existing.deduction += entry.deduction
      existing.bonus += entry.bonus
      existing.positionSalary += entry.positionSalary || 0
      existing.performanceSalary += entry.performanceSalary || 0
      existing.senioritySalary += entry.senioritySalary || 0
      existing.transportAllowance += entry.transportAllowance || 0
      existing.mealAllowance += entry.mealAllowance || 0
      existing.housingAllowance += entry.housingAllowance || 0
      existing.communicationAllowance += entry.communicationAllowance || 0
      existing.otherDeduction += entry.otherDeduction || 0
      existing.socialEmp += entry.socialEmp
      existing.socialOrg += entry.socialOrg
      existing.housingEmp += entry.housingEmp
      existing.housingOrg += entry.housingOrg
      existing.tax += entry.tax
      existing.totalPay += entry.totalPay
      existing.netPay += entry.netPay
      employeeMap.set(entry.employeeId, existing)
    }
  }

  // 计算累计数据
  const year = month.slice(0, 4)
  const employeeIds = Array.from(employeeMap.keys())

  const allPrevPayslips = await prisma.payslip.findMany({
    where: { orgId, employeeId: { in: employeeIds }, month: { startsWith: year, lt: month } },
    select: { employeeId: true, totalPay: true, tax: true, socialEmp: true, housingEmp: true },
  })
  const prevMap = new Map<string, { totalPay: number; tax: number; socialEmp: number; housingEmp: number }>()
  for (const p of allPrevPayslips) {
    const existing = prevMap.get(p.employeeId) || { totalPay: 0, tax: 0, socialEmp: 0, housingEmp: 0 }
    existing.totalPay += p.totalPay
    existing.tax += p.tax
    existing.socialEmp += p.socialEmp
    existing.housingEmp += p.housingEmp
    prevMap.set(p.employeeId, existing)
  }

  let generated = 0
  for (const [employeeId, summary] of employeeMap) {
    const prev = prevMap.get(employeeId) || { totalPay: 0, tax: 0, socialEmp: 0, housingEmp: 0 }
    const ytdIncome = prev.totalPay + summary.totalPay
    const ytdTaxDeducted = prev.tax + summary.tax
    const ytdSocialEmp = prev.socialEmp + summary.socialEmp
    const ytdHousingEmp = prev.housingEmp + summary.housingEmp

    await prisma.payslip.upsert({
      where: { employeeId_month: { employeeId, month } },
      update: {
        baseSalary: Math.round(summary.baseSalary * 100) / 100,
        performanceSalary: Math.round(summary.performanceSalary * 100) / 100,
        overtimePay: Math.round(summary.overtimePay * 100) / 100,
        allowance: Math.round(summary.allowance * 100) / 100,
        deduction: Math.round(summary.deduction * 100) / 100,
        bonus: Math.round(summary.bonus * 100) / 100,
        totalPay: Math.round(summary.totalPay * 100) / 100,
        socialEmp: Math.round(summary.socialEmp * 100) / 100,
        housingEmp: Math.round(summary.housingEmp * 100) / 100,
        tax: Math.round(summary.tax * 100) / 100,
        netPay: Math.round(summary.netPay * 100) / 100,
        ytdIncome: Math.round(ytdIncome * 100) / 100,
        ytdTaxDeducted: Math.round(ytdTaxDeducted * 100) / 100,
        ytdSocialEmp: Math.round(ytdSocialEmp * 100) / 100,
        ytdHousingEmp: Math.round(ytdHousingEmp * 100) / 100,
        status: 'PUBLISHED',
        publishedAt: new Date(),
      },
      create: {
        orgId,
        employeeId,
        month,
        baseSalary: Math.round(summary.baseSalary * 100) / 100,
        performanceSalary: Math.round(summary.performanceSalary * 100) / 100,
        overtimePay: Math.round(summary.overtimePay * 100) / 100,
        allowance: Math.round(summary.allowance * 100) / 100,
        deduction: Math.round(summary.deduction * 100) / 100,
        bonus: Math.round(summary.bonus * 100) / 100,
        totalPay: Math.round(summary.totalPay * 100) / 100,
        socialEmp: Math.round(summary.socialEmp * 100) / 100,
        housingEmp: Math.round(summary.housingEmp * 100) / 100,
        tax: Math.round(summary.tax * 100) / 100,
        netPay: Math.round(summary.netPay * 100) / 100,
        ytdIncome: Math.round(ytdIncome * 100) / 100,
        ytdTaxDeducted: Math.round(ytdTaxDeducted * 100) / 100,
        ytdSocialEmp: Math.round(ytdSocialEmp * 100) / 100,
        ytdHousingEmp: Math.round(ytdHousingEmp * 100) / 100,
        status: 'PUBLISHED',
        publishedAt: new Date(),
      },
    })
    generated++
  }

  return { generated }
}

// ========== 算薪前 AI 校验 ==========

export interface PayrollCheckItem {
  code: string
  name: string
  status: 'PASS' | 'FAIL' | 'WARNING'
  message: string
  employeeId?: string
  employeeName?: string
  detail?: any
}

export interface PayrollCheckResult {
  checks: PayrollCheckItem[]
  passedCount: number
  failedCount: number
  warningCount: number
}

/**
 * 算薪前校验：检查 10+ 项异常，把错误拦截在发放前
 */
export async function prePayrollCheck(orgId: string, batchId: string): Promise<PayrollCheckResult> {
  const batch = await prisma.payrollBatch.findFirst({
    where: { id: batchId, orgId },
    include: { entries: { include: { employee: true } } },
  })
  if (!batch) {
    throw { code: 'NOT_FOUND', message: '工资批次不存在' }
  }

  const month = batch.month
  const entries = batch.entries
  const checks: PayrollCheckItem[] = []

  // 获取社保公积金配置
  const [socialConfig, housingConfig] = await Promise.all([
    prisma.socialInsuranceConfig.findFirst({
      where: { orgId, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
      orderBy: { effectiveFrom: 'desc' },
    }),
    prisma.housingFundConfig.findFirst({
      where: { orgId, effectiveFrom: { lte: month }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: month } }] },
      orderBy: { effectiveFrom: 'desc' },
    }),
  ])

  // 1. 社保基数是否在上下限范围内
  if (socialConfig) {
    for (const entry of entries) {
      const base = entry.employee.socialInsBase != null ? entry.employee.socialInsBase : entry.baseSalary
      if (base < socialConfig.baseMin || base > socialConfig.baseMax) {
        checks.push({
          code: 'SOCIAL_BASE_OUT_OF_RANGE',
          name: '社保基数超出范围',
          status: 'FAIL',
          message: `${entry.employee.name} 的社保基数 ¥${base} 不在范围内（${socialConfig.baseMin} ~ ${socialConfig.baseMax}）`,
          employeeId: entry.employeeId,
          employeeName: entry.employee.name,
          detail: { base, min: socialConfig.baseMin, max: socialConfig.baseMax },
        })
      }
    }
  }

  // 2. 公积金基数是否在上下限范围内
  if (housingConfig) {
    for (const entry of entries) {
      const base = entry.employee.housingFundBase != null ? entry.employee.housingFundBase : entry.baseSalary
      if (base < housingConfig.baseMin || base > housingConfig.baseMax) {
        checks.push({
          code: 'HOUSING_BASE_OUT_OF_RANGE',
          name: '公积金基数超出范围',
          status: 'FAIL',
          message: `${entry.employee.name} 的公积金基数 ¥${base} 不在范围内（${housingConfig.baseMin} ~ ${housingConfig.baseMax}）`,
          employeeId: entry.employeeId,
          employeeName: entry.employee.name,
          detail: { base, min: housingConfig.baseMin, max: housingConfig.baseMax },
        })
      }
    }
  }

  // 3. 加班时数是否超过法定上限（月 36 小时）
  for (const entry of entries) {
    const overtimeRecords = await prisma.overtimeRecord.findMany({
      where: { orgId, employeeId: entry.employeeId, month },
    })
    for (const ot of overtimeRecords) {
      const totalOT = ot.weekdayHours + ot.weekendHours + ot.holidayHours
      if (totalOT > 36) {
        checks.push({
          code: 'OVERTIME_EXCEED_LIMIT',
          name: '加班超法定上限',
          status: 'WARNING',
          message: `${entry.employee.name} 本月加班 ${totalOT} 小时，超过法定月上限 36 小时`,
          employeeId: entry.employeeId,
          employeeName: entry.employee.name,
          detail: { totalHours: totalOT, limit: 36 },
        })
      }
    }
  }

  // 4. 个税累计预扣跳档检测
  const year = month.slice(0, 4)
  for (const entry of entries) {
    const prevPayslips = await prisma.payslip.findMany({
      where: { orgId, employeeId: entry.employeeId, month: { startsWith: year, lt: month } },
      select: { tax: true, totalPay: true },
    })
    if (prevPayslips.length >= 2) {
      const avgTax = prevPayslips.reduce((s, p) => s + p.tax, 0) / prevPayslips.length
      if (entry.tax > avgTax * 3 && entry.tax > 1000) {
        checks.push({
          code: 'TAX_BRACKET_JUMP',
          name: '个税跳档警告',
          status: 'WARNING',
          message: `${entry.employee.name} 本月个税 ¥${entry.tax} 明显高于往月均值 ¥${avgTax.toFixed(0)}，可能存在累计预扣跳档`,
          employeeId: entry.employeeId,
          employeeName: entry.employee.name,
          detail: { currentTax: entry.tax, avgTax },
        })
      }
    }
  }

  // 5. 试用期工资是否低于合同工资 80%
  for (const entry of entries) {
    const latestContract = await prisma.laborContract.findFirst({
      where: { employeeId: entry.employeeId, orgId },
      orderBy: { createdAt: 'desc' },
    })
    if (latestContract && latestContract.probationMonths > 0 && latestContract.probationSalary > 0) {
      const probationEnd = new Date(latestContract.startDate)
      probationEnd.setMonth(probationEnd.getMonth() + latestContract.probationMonths)
      if (probationEnd > new Date() && entry.baseSalary < latestContract.probationSalary * 0.8) {
        checks.push({
          code: 'PROBATION_SALARY_TOO_LOW',
          name: '试用期工资低于法定下限',
          status: 'FAIL',
          message: `${entry.employee.name} 试用期工资 ¥${entry.baseSalary} 低于合同工资的 80%（¥${latestContract.probationSalary * 0.8}）`,
          employeeId: entry.employeeId,
          employeeName: entry.employee.name,
          detail: { actualSalary: entry.baseSalary, minSalary: latestContract.probationSalary * 0.8 },
        })
      }
    }
  }

  // 6. 离职员工是否多算了一个月
  for (const entry of entries) {
    if (entry.employee.status === 'RESIGNED') {
      const termination = await prisma.terminationRecord.findFirst({
        where: { employeeId: entry.employeeId, status: { notIn: ['CANCELLED', 'DRAFT'] } },
        orderBy: { terminationDate: 'desc' },
      })
      if (termination) {
        const termMonth = termination.terminationDate.toISOString().slice(0, 7)
        if (month > termMonth) {
          checks.push({
            code: 'RESIGNED_OVERPAY',
            name: '离职员工多算工资',
            status: 'FAIL',
            message: `${entry.employee.name} 已于 ${termination.terminationDate.toISOString().slice(0, 10)} 离职，但 ${month} 仍有工资记录`,
            employeeId: entry.employeeId,
            employeeName: entry.employee.name,
            detail: { terminationDate: termination.terminationDate, payrollMonth: month },
          })
        }
      }
    }
  }

  // 7. 新入职员工是否按实际入职日折算
  for (const entry of entries) {
    const hireDate = entry.employee.hireDate
    const hireMonth = hireDate.toISOString().slice(0, 7)
    if (hireMonth === month) {
      const daysInMonth = new Date(hireDate.getFullYear(), hireDate.getMonth() + 1, 0).getDate()
      const actualWorkDays = daysInMonth - hireDate.getDate() + 1
      // 如果基本工资等于整月工资，提示可能未折算
      const fullMonthSalary = Number(entry.employee.monthlySalary) || entry.baseSalary
      if (Math.abs(entry.baseSalary - fullMonthSalary) < 1 && actualWorkDays < daysInMonth) {
        checks.push({
          code: 'NEW_HIRE_NO_PRORATE',
          name: '新入职未折算工资',
          status: 'WARNING',
          message: `${entry.employee.name} 本月 ${hireDate.getDate()} 日入职，工资可能未按实际天数折算（实际工作 ${actualWorkDays} 天）`,
          employeeId: entry.employeeId,
          employeeName: entry.employee.name,
          detail: { hireDate: hireDate.toISOString().slice(0, 10), actualWorkDays, daysInMonth },
        })
      }
    }
  }

  // 8. 社保是否在入职 30 天内参保
  for (const entry of entries) {
    const hireDate = entry.employee.hireDate
    const socialStart = entry.employee.socialInsStartMonth
    if (socialStart) {
      const socialStartDate = new Date(socialStart + '-01')
      const daysDiff = Math.floor((socialStartDate.getTime() - hireDate.getTime()) / 86400000)
      if (daysDiff > 30) {
        checks.push({
          code: 'SOCIAL_INS_LATE_ENROLL',
          name: '社保参保延迟',
          status: 'WARNING',
          message: `${entry.employee.name} 入职 ${daysDiff} 天后才参保社保，超过 30 天法定期限`,
          employeeId: entry.employeeId,
          employeeName: entry.employee.name,
          detail: { hireDate: hireDate.toISOString().slice(0, 10), socialStart, daysDiff },
        })
      }
    }
  }

  // 9. 离职当月社保是否已停保
  for (const entry of entries) {
    if (entry.employee.status === 'RESIGNED' && entry.employee.socialInsEndMonth) {
      // 正常，已停保
    } else if (entry.employee.status === 'RESIGNED' && !entry.employee.socialInsEndMonth) {
      checks.push({
        code: 'SOCIAL_INS_NOT_STOPPED',
        name: '离职未停保',
        status: 'WARNING',
        message: `${entry.employee.name} 已离职但社保未停保，可能产生多缴费用`,
        employeeId: entry.employeeId,
        employeeName: entry.employee.name,
      })
    }
  }

  // 10. 基本工资为 0
  for (const entry of entries) {
    if (entry.baseSalary === 0 && batch.type === 'REGULAR') {
      checks.push({
        code: 'ZERO_BASE_SALARY',
        name: '基本工资为 0',
        status: 'WARNING',
        message: `${entry.employee.name} 的基本工资为 0，请确认是否正确`,
        employeeId: entry.employeeId,
        employeeName: entry.employee.name,
      })
    }
  }

  // 11. 实发工资低于最低工资标准（剔除加班费后比较）
  for (const entry of entries) {
    const minWage = (entry as any).minWage || 0
    if (minWage > 0 && batch.type !== 'BONUS' && batch.type !== 'SEVERANCE') {
      // 最低工资剔除项：加班费、高温/夜班津贴等不计入
      const comparablePay = entry.totalPay - entry.overtimePay
      if (comparablePay < minWage && entry.netPay < minWage) {
        const applied = (entry as any).minWageApplied || 0
        if (applied > 0) {
          // 已触发最低工资保护，检查递延情况
          const deferredTotal = ((entry as any).deferredSocialEmp || 0) + ((entry as any).deferredHousingEmp || 0) + ((entry as any).deferredMinWage || 0)
          checks.push({
            code: 'MIN_WAGE_DEFERRED',
            name: '最低工资保护已触发（递延扣款）',
            status: 'WARNING',
            message: `${entry.employee.name} 应发 ¥${comparablePay}（剔除加班费）低于最低工资 ¥${minWage}，已补齐 ¥${applied}，递延扣款 ¥${deferredTotal} 将在次月补扣`,
            employeeId: entry.employeeId,
            employeeName: entry.employee.name,
            detail: { comparablePay, minWage, applied, deferredTotal },
          })
        } else {
          checks.push({
            code: 'BELOW_MIN_WAGE',
            name: '实发低于最低工资标准',
            status: 'FAIL',
            message: `${entry.employee.name} 实发 ¥${entry.netPay} 低于当地最低工资标准 ¥${minWage}，请检查社保基数或启用最低工资保护`,
            employeeId: entry.employeeId,
            employeeName: entry.employee.name,
            detail: { netPay: entry.netPay, minWage, comparablePay },
          })
        }
      }
    }
  }

  // 12. 上月递延扣款待补扣
  for (const entry of entries) {
    const prevDeferred = ((entry as any).prevDeferredSocialEmp || 0) + ((entry as any).prevDeferredHousingEmp || 0) + ((entry as any).prevDeferredMinWage || 0)
    if (prevDeferred > 0) {
      checks.push({
        code: 'PREV_DEFERRED_RECOVERED',
        name: '上月递延扣款已补扣',
        status: 'WARNING',
        message: `${entry.employee.name} 本月补扣上月递延 ¥${prevDeferred}（社保 ¥${(entry as any).prevDeferredSocialEmp || 0} + 公积金 ¥${(entry as any).prevDeferredHousingEmp || 0} + 最低工资补齐 ¥${(entry as any).prevDeferredMinWage || 0}）`,
        employeeId: entry.employeeId,
        employeeName: entry.employee.name,
        detail: { prevDeferred },
      })
    }
  }

  // 汇总
  const passedCount = entries.length > 0 ? Math.max(0, entries.length - checks.filter(c => c.employeeId).length) : 0
  const failedCount = checks.filter(c => c.status === 'FAIL').length
  const warningCount = checks.filter(c => c.status === 'WARNING').length

  return { checks, passedCount, failedCount, warningCount }
}
