import { Router, Response, NextFunction } from 'express'
import prisma from '../lib/prisma'
import { authMiddleware, AuthRequest } from '../middleware/auth'
import { z } from 'zod'
import { decrypt } from '../lib/crypto'
import {
  getTemplate,
  calcBatchEntry,
  getPayrollRiskWarnings,
  generatePayslipFromBatches,
  prePayrollCheck,
} from '../services/payroll.service'

// RFC 5987 编码中文文件名
function contentDisposition(filename: string): string {
  const encoded = encodeURIComponent(filename)
  return `attachment; filename="${encoded}"; filename*=UTF-8''${encoded}`
}

const router = Router()
router.use(authMiddleware)

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

// 获取薪酬模版
router.get('/template', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const items = await getTemplate(req.user!.orgId)
    res.json({ success: true, data: items })
  } catch (err) {
    next(err)
  }
})

// 更新薪酬模版项
const updateTemplateItemSchema = z.object({
  name: z.string().min(1).optional(),
  formula: z.string().nullable().optional(),
  order: z.number().int().optional(),
  isEditable: z.boolean().optional(),
})

router.put('/template/:id', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const data = updateTemplateItemSchema.parse(req.body)
    const item = await prisma.payslipItem.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
    })
    if (!item) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模版项不存在' } })

    const updateData: any = {}
    if (data.name !== undefined && !item.isDefault) updateData.name = data.name
    if (data.formula !== undefined) updateData.formula = data.formula
    if (data.order !== undefined) updateData.order = data.order
    if (data.isEditable !== undefined) updateData.isEditable = data.isEditable

    const updated = await prisma.payslipItem.update({ where: { id: req.params.id }, data: updateData })
    res.json({ success: true, data: updated })
  } catch (err) {
    next(err)
  }
})

// 新增薪酬模版项
const createTemplateItemSchema = z.object({
  name: z.string().min(1),
  code: z.string().min(1),
  type: z.enum(['INPUT', 'CALCULATED']),
  formula: z.string().nullable().optional(),
  order: z.number().int().default(99),
  isEditable: z.boolean().default(true),
})

router.post('/template', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const data = createTemplateItemSchema.parse(req.body)
    const item = await prisma.payslipItem.create({
      data: { ...data, orgId: req.user!.orgId, isDefault: false },
    })
    res.json({ success: true, data: item })
  } catch (err) {
    next(err)
  }
})

// 删除薪酬模版项（仅非预置项）
router.delete('/template/:id', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const item = await prisma.payslipItem.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
    })
    if (!item) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模版项不存在' } })
    if (item.isDefault) return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '预置项不可删除' } })

    await prisma.payslipItem.delete({ where: { id: req.params.id } })
    res.json({ success: true })
  } catch (err) {
    next(err)
  }
})

// ========== 发薪批次 ==========

// 检查本月是否已发薪
router.get('/batches/check', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { month } = req.query
    if (!month) return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '缺少 month 参数' } })

    const archivedBatches = await prisma.payrollBatch.count({
      where: { orgId: req.user!.orgId, month: String(month), status: 'ARCHIVED' },
    })
    const draftBatches = await prisma.payrollBatch.count({
      where: { orgId: req.user!.orgId, month: String(month), status: 'DRAFT' },
    })
    const publishedPayslips = await prisma.payslip.count({
      where: { orgId: req.user!.orgId, month: String(month), status: 'PUBLISHED' },
    })

    res.json({
      success: true,
      data: {
        hasArchivedBatch: archivedBatches > 0,
        archivedCount: archivedBatches,
        draftCount: draftBatches,
        payslipsPublished: publishedPayslips > 0,
      },
    })
  } catch (err) {
    next(err)
  }
})

// 获取可复制的归档批次列表
router.get('/batches/archived/list', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const orgId = req.user!.orgId
    const batches = await prisma.payrollBatch.findMany({
      where: { orgId, status: 'ARCHIVED' },
      orderBy: [{ month: 'desc' }, { batchNo: 'desc' }],
      select: { id: true, name: true, month: true, type: true, employeeCount: true, totalPay: true, totalNetPay: true },
      take: 20,
    })
    res.json({ success: true, data: batches })
  } catch (err) {
    next(err)
  }
})

// 获取批次列表
router.get('/batches', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { month, monthFrom, monthTo, status, type } = req.query
    const batches = await prisma.payrollBatch.findMany({
      where: {
        orgId: req.user!.orgId,
        ...(month ? { month: String(month) } : {}),
        ...(monthFrom ? { month: { gte: String(monthFrom) } } : {}),
        ...(monthTo ? { month: { lte: String(monthTo) } } : {}),
        ...(status ? { status: String(status) as any } : {}),
        ...(type ? { type: String(type) as any } : {}),
      },
      orderBy: [{ month: 'desc' }, { batchNo: 'asc' }],
    })
    res.json({ success: true, data: batches })
  } catch (err) {
    next(err)
  }
})

// 获取批次详情
router.get('/batches/:id', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const batch = await prisma.payrollBatch.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
      include: {
        entries: {
          include: {
            employee: { select: { id: true, name: true, department: true, status: true, bankAccount: true, bankName: true } },
          },
          orderBy: { employee: { name: 'asc' } },
        },
      },
    })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    res.json({ success: true, data: batch })
  } catch (err) {
    next(err)
  }
})

// 重命名批次
router.put('/batches/:id/name', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { name } = req.body
    if (!name || typeof name !== 'string' || name.trim().length === 0) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '批次名称不能为空' } })
    }
    const batch = await prisma.payrollBatch.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
    })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status === 'ARCHIVED') {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '已归档批次不可重命名' } })
    }
    const updated = await prisma.payrollBatch.update({
      where: { id: req.params.id },
      data: { name: name.trim() },
    })
    res.json({ success: true, data: { id: updated.id, name: updated.name } })
  } catch (err) {
    next(err)
  }
})

// 创建批次
const createBatchSchema = z.object({
  month: z.string().regex(/^\d{4}-\d{2}$/),
  type: z.enum(['REGULAR', 'TERMINATION', 'BONUS', 'SEVERANCE']).default('REGULAR'),
  mode: z.enum(['copy_last', 'blank_employees', 'blank_all', 'copy_batch', 'custom']).default('copy_last'),
  sourceBatchId: z.string().optional(),
  employeeIds: z.array(z.string()).optional(),
  name: z.string().optional(),
  remark: z.string().optional(),
})

router.post('/batches', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { month, type, mode, sourceBatchId, employeeIds, name, remark } = createBatchSchema.parse(req.body)
    const orgId = req.user!.orgId

    // 查询当月最大批次号，避免删除后 count 不准导致唯一键冲突
    const lastBatch = await prisma.payrollBatch.findFirst({
      where: { orgId, month },
      orderBy: { batchNo: 'desc' },
      select: { batchNo: true },
    })
    const batchNo = (lastBatch?.batchNo || 0) + 1

    // 获取在职员工 + 本月离职员工
    const monthStart = new Date(`${month}-01`)
    const monthEnd = new Date(monthStart.getFullYear(), monthStart.getMonth() + 1, 0, 23, 59, 59)

    // 获取上月发薪数据
    const prevMonth = new Date(monthStart.getFullYear(), monthStart.getMonth() - 1, 1)
    const prevMonthStr = `${prevMonth.getFullYear()}-${String(prevMonth.getMonth() + 1).padStart(2, '0')}`

    const batchName = name || `${month} 第${batchNo}批 ${type === 'BONUS' ? '奖金' : type === 'TERMINATION' ? '离职结算' : type === 'SEVERANCE' ? '补偿金' : '发薪'}`

    // 根据模式确定员工列表和数据来源
    let employees: any[] = []
    let sourceEntries: any[] | null = null

    if (mode === 'blank_all') {
      // 全空白：不拉入员工
      employees = []
    } else if (mode === 'custom' && employeeIds && employeeIds.length > 0) {
      // 自定义选择：仅包含指定员工
      employees = await prisma.employee.findMany({
        where: { id: { in: employeeIds }, orgId },
        include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
      })
    } else if (mode === 'copy_batch' && sourceBatchId) {
      // 复制指定批次：从源批次复制条目
      const sourceBatch = await prisma.payrollBatch.findFirst({
        where: { id: sourceBatchId, orgId, status: 'ARCHIVED' },
        include: { entries: true },
      })
      if (!sourceBatch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '源批次不存在或未归档' } })
      sourceEntries = sourceBatch.entries
      // 提取员工 ID，后续按此创建条目
      const employeeIds = sourceEntries.map(e => e.employeeId)
      employees = await prisma.employee.findMany({
        where: { id: { in: employeeIds }, orgId },
        include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
      })
    } else {
      // copy_last 或 blank_employees：拉入员工
      if (type === 'TERMINATION' || type === 'SEVERANCE') {
        const terminations = await prisma.terminationRecord.findMany({
          where: { orgId, terminationDate: { gte: monthStart, lte: monthEnd } },
          include: { employee: { include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } } } },
        })
        employees = terminations.map(t => t.employee)
      } else {
        employees = await prisma.employee.findMany({
          where: {
            orgId,
            OR: [
              { status: 'ACTIVE' },
              { status: 'RESIGNED', updatedAt: { gte: monthStart, lte: monthEnd } },
            ],
          },
          include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
        })
      }
    }

    // 创建批次
    const batch = await prisma.payrollBatch.create({
      data: {
        orgId,
        month,
        batchNo,
        name: batchName,
        type,
        remark,
        createdBy: req.user!.id,
        employeeCount: employees.length,
      },
    })

    // 创建批次条目
    const entries: any[] = []
    const failedEmployees: { employeeId: string; name: string; error: string }[] = []
    for (const emp of employees) {
      let baseSalary = 0
      let overtimePay = 0
      let allowance = 0
      let deduction = 0
      let bonus = 0

      if (mode === 'copy_batch' && sourceEntries) {
        // 复制指定批次：从源条目复制数据
        const srcEntry = sourceEntries.find(e => e.employeeId === emp.id)
        if (srcEntry) {
          baseSalary = srcEntry.baseSalary
          overtimePay = srcEntry.overtimePay
          allowance = srcEntry.allowance
          deduction = srcEntry.deduction
          bonus = srcEntry.bonus
        }
      } else if (mode === 'copy_last') {
        // 复制上月：从上月工资条复制
        const prevPayslip = await prisma.payslip.findUnique({
          where: { employeeId_month: { employeeId: emp.id, month: prevMonthStr } },
        })
        const overtime = await prisma.overtimeRecord.findUnique({
          where: { employeeId_month: { employeeId: emp.id, month } },
        })

        if (emp.contracts?.[0]?.probationSalary && new Date(emp.contracts[0].startDate) > new Date(Date.now() - 365 * 24 * 60 * 60 * 1000)) {
          baseSalary = emp.contracts[0].probationSalary
        } else if (emp.monthlySalary) {
          try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
        }
        if (prevPayslip) baseSalary = prevPayslip.baseSalary
        overtimePay = overtime?.totalPay || 0
        allowance = prevPayslip?.allowance || 0
        deduction = prevPayslip?.deduction || 0
      }
      // blank_employees 和 blank_all: 所有金额默认 0
      // blank_employees 模式下尝试从员工记录获取基本工资
      if (mode === 'blank_employees' && emp.monthlySalary) {
        try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
      }

      // calcBatchEntry 内部会按员工检查当月已归档批次是否已扣社保，已扣则跳过
      let calcResult: any
      try {
        calcResult = await calcBatchEntry(orgId, emp.id, month, { baseSalary, overtimePay, allowance, deduction, bonus }, type)
      } catch (calcErr: any) {
        // 单个员工计算失败不阻塞整个批次，记录错误并使用零值
        failedEmployees.push({ employeeId: emp.id, name: emp.name, error: calcErr?.message || '计算失败' })
        calcResult = { socialEmp: 0, socialOrg: 0, housingEmp: 0, housingOrg: 0, tax: 0, totalPay: baseSalary + overtimePay + allowance + bonus - deduction, netPay: baseSalary + overtimePay + allowance + bonus - deduction }
      }

      // 风险提示
      const riskWarnings = await getPayrollRiskWarnings(orgId, emp.id)

      const entry = await prisma.batchEntry.create({
        data: {
          batchId: batch.id,
          orgId,
          employeeId: emp.id,
          baseSalary,
          overtimePay,
          allowance,
          deduction,
          bonus,
          socialEmp: calcResult.socialEmp,
          socialOrg: calcResult.socialOrg,
          housingEmp: calcResult.housingEmp,
          housingOrg: calcResult.housingOrg,
          tax: calcResult.tax,
          totalPay: calcResult.totalPay,
          netPay: calcResult.netPay,
          riskWarnings,
        },
      })
      entries.push(entry)
    }

    // 更新批次汇总
    const totals = entries.reduce((acc, e) => ({
      totalPay: acc.totalPay + e.totalPay,
      totalNetPay: acc.totalNetPay + e.netPay,
      totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
      totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
      totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
      totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
      totalTax: acc.totalTax + e.tax,
    }), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })

    const updatedBatch = await prisma.payrollBatch.update({
      where: { id: batch.id },
      data: {
        totalPay: Math.round(totals.totalPay * 100) / 100,
        totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
        totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
        totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
        totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
        totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
        totalTax: Math.round(totals.totalTax * 100) / 100,
      },
      include: { entries: { include: { employee: { select: { id: true, name: true, department: true, status: true } } } } },
    })

    res.json({ success: true, data: updatedBatch, failedEmployees: failedEmployees.length > 0 ? failedEmployees : undefined })
  } catch (err) {
    next(err)
  }
})

// 编辑批次条目（计算依据项 + 社保公积金手动覆盖）
const updateEntrySchema = z.object({
  baseSalary: z.number().min(0).optional(),
  overtimePay: z.number().min(0).optional(),
  allowance: z.number().min(0).optional(),
  deduction: z.number().min(0).optional(),
  bonus: z.number().min(0).optional(),
  socialEmp: z.number().min(0).optional(),
  socialOrg: z.number().min(0).optional(),
  housingEmp: z.number().min(0).optional(),
  housingOrg: z.number().min(0).optional(),
})

router.put('/batches/:batchId/entries/:employeeId', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId, employeeId } = req.params
    const data = updateEntrySchema.parse(req.body)
    const orgId = req.user!.orgId

    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status === 'ARCHIVED') return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '已归档批次不可编辑' } })

    const entry = await prisma.batchEntry.findUnique({
      where: { batchId_employeeId: { batchId, employeeId } },
    })
    if (!entry) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '条目不存在' } })

    // 合并输入项
    const inputs = {
      baseSalary: data.baseSalary ?? entry.baseSalary,
      overtimePay: data.overtimePay ?? entry.overtimePay,
      allowance: data.allowance ?? entry.allowance,
      deduction: data.deduction ?? entry.deduction,
      bonus: data.bonus ?? entry.bonus,
    }

    // 构建社保覆盖参数（如果请求中包含社保字段）
    const overrideSocial: any = {}
    if (data.socialEmp !== undefined) overrideSocial.socialEmp = data.socialEmp
    if (data.socialOrg !== undefined) overrideSocial.socialOrg = data.socialOrg
    if (data.housingEmp !== undefined) overrideSocial.housingEmp = data.housingEmp
    if (data.housingOrg !== undefined) overrideSocial.housingOrg = data.housingOrg

    // calcBatchEntry 内部会按员工检查当月已归档批次是否已扣社保，手动覆盖优先
    const options = Object.keys(overrideSocial).length > 0
      ? { overrideSocial }
      : undefined

    // 重新计算
    const calcResult = await calcBatchEntry(orgId, employeeId, batch.month, inputs, batch.type, options)

    const updated = await prisma.batchEntry.update({
      where: { id: entry.id },
      data: { ...inputs, ...calcResult },
    })

    // 更新批次汇总
    const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
    const totals = allEntries.reduce((acc, e) => ({
      totalPay: acc.totalPay + (e.id === entry.id ? calcResult.totalPay : e.totalPay),
      totalNetPay: acc.totalNetPay + (e.id === entry.id ? calcResult.netPay : e.netPay),
      totalSocialOrg: acc.totalSocialOrg + (e.id === entry.id ? calcResult.socialOrg : e.socialOrg),
      totalSocialEmp: acc.totalSocialEmp + (e.id === entry.id ? calcResult.socialEmp : e.socialEmp),
      totalHousingOrg: acc.totalHousingOrg + (e.id === entry.id ? calcResult.housingOrg : e.housingOrg),
      totalHousingEmp: acc.totalHousingEmp + (e.id === entry.id ? calcResult.housingEmp : e.housingEmp),
      totalTax: acc.totalTax + (e.id === entry.id ? calcResult.tax : e.tax),
    }), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })

    await prisma.payrollBatch.update({
      where: { id: batchId },
      data: {
        totalPay: Math.round(totals.totalPay * 100) / 100,
        totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
        totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
        totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
        totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
        totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
        totalTax: Math.round(totals.totalTax * 100) / 100,
      },
    })

    res.json({ success: true, data: updated, taxBreakdown: calcResult.taxBreakdown })
  } catch (err) {
    next(err)
  }
})

// 获取条目个税计算明细
router.get('/batches/:batchId/entries/:employeeId/tax-detail', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId, employeeId } = req.params
    const orgId = req.user!.orgId

    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })

    const entry = await prisma.batchEntry.findUnique({
      where: { batchId_employeeId: { batchId, employeeId } },
    })
    if (!entry) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '条目不存在' } })

    const inputs = {
      baseSalary: entry.baseSalary,
      overtimePay: entry.overtimePay,
      allowance: entry.allowance,
      deduction: entry.deduction,
      bonus: entry.bonus,
    }
    const calcResult = await calcBatchEntry(orgId, employeeId, batch.month, inputs, batch.type)
    const taxBreakdown = calcResult.taxBreakdown || {}
    // 加入社保公积金系统计算值 vs 实际值对比
    taxBreakdown.systemSocialEmp = calcResult.systemSocialEmp
    taxBreakdown.systemSocialOrg = calcResult.systemSocialOrg
    taxBreakdown.systemHousingEmp = calcResult.systemHousingEmp
    taxBreakdown.systemHousingOrg = calcResult.systemHousingOrg
    taxBreakdown.actualSocialEmp = entry.socialEmp
    taxBreakdown.actualSocialOrg = entry.socialOrg
    taxBreakdown.actualHousingEmp = entry.housingEmp
    taxBreakdown.actualHousingOrg = entry.housingOrg
    res.json({ success: true, data: taxBreakdown })
  } catch (err) {
    next(err)
  }
})

// 批次增加人员
router.post('/batches/:batchId/employees', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId } = req.params
    const { employeeIds } = req.body as { employeeIds: string[] }
    const orgId = req.user!.orgId

    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status === 'ARCHIVED') return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '已归档批次不可编辑' } })

    const results: any[] = []
    for (const employeeId of employeeIds) {
      // 检查是否已在批次中
      const existing = await prisma.batchEntry.findUnique({
        where: { batchId_employeeId: { batchId, employeeId } },
      })
      if (existing) continue

      const emp = await prisma.employee.findFirst({
        where: { id: employeeId, orgId },
        include: { contracts: { orderBy: { createdAt: 'desc' }, take: 1 } },
      })
      if (!emp) continue

      let baseSalary = 0
      if (emp.contracts?.[0]?.probationSalary && new Date(emp.contracts[0].startDate) > new Date(Date.now() - 365 * 24 * 60 * 60 * 1000)) {
        baseSalary = emp.contracts[0].probationSalary
      } else if (emp.monthlySalary) {
        try { baseSalary = Number(decrypt(emp.monthlySalary)) || 0 } catch { baseSalary = Number(emp.monthlySalary) || 0 }
      }

      const overtime = await prisma.overtimeRecord.findUnique({
        where: { employeeId_month: { employeeId, month: batch.month } },
      })
      const overtimePay = overtime?.totalPay || 0

      // calcBatchEntry 内部会按员工检查当月已归档批次是否已扣社保，已扣则跳过
      const calcResult = await calcBatchEntry(orgId, employeeId, batch.month, { baseSalary, overtimePay, allowance: 0, deduction: 0, bonus: 0 }, batch.type)
      const riskWarnings = await getPayrollRiskWarnings(orgId, employeeId)

      const entry = await prisma.batchEntry.create({
        data: {
          batchId, orgId, employeeId,
          baseSalary, overtimePay, allowance: 0, deduction: 0, bonus: 0,
          ...calcResult, riskWarnings,
        },
      })
      results.push(entry)
    }

    // 更新批次人数和汇总
    const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
    const totals = allEntries.reduce((acc, e) => ({
      totalPay: acc.totalPay + e.totalPay,
      totalNetPay: acc.totalNetPay + e.netPay,
      totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
      totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
      totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
      totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
      totalTax: acc.totalTax + e.tax,
    }), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })
    await prisma.payrollBatch.update({
      where: { id: batchId },
      data: {
        employeeCount: allEntries.length,
        totalPay: Math.round(totals.totalPay * 100) / 100,
        totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
        totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
        totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
        totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
        totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
        totalTax: Math.round(totals.totalTax * 100) / 100,
      },
    })

    res.json({ success: true, data: { added: results.length } })
  } catch (err) {
    next(err)
  }
})

// 批次移除人员
router.delete('/batches/:batchId/employees/:employeeId', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId, employeeId } = req.params
    const orgId = req.user!.orgId

    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status === 'ARCHIVED') return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '已归档批次不可编辑' } })

    await prisma.batchEntry.deleteMany({ where: { batchId, employeeId } })

    // 更新批次人数和汇总
    const allEntries = await prisma.batchEntry.findMany({ where: { batchId } })
    const totals = allEntries.reduce((acc, e) => ({
      totalPay: acc.totalPay + e.totalPay,
      totalNetPay: acc.totalNetPay + e.netPay,
      totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
      totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
      totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
      totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
      totalTax: acc.totalTax + e.tax,
    }), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })
    await prisma.payrollBatch.update({
      where: { id: batchId },
      data: {
        employeeCount: allEntries.length,
        totalPay: Math.round(totals.totalPay * 100) / 100,
        totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
        totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
        totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
        totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
        totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
        totalTax: Math.round(totals.totalTax * 100) / 100,
      },
    })

    res.json({ success: true })
  } catch (err) {
    next(err)
  }
})

// 删除批次（仅限草稿状态）
router.delete('/batches/:batchId', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId } = req.params
    const orgId = req.user!.orgId

    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status === 'ARCHIVED') return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '已归档批次不可删除' } })

    await prisma.batchEntry.deleteMany({ where: { batchId } })
    await prisma.payrollBatch.delete({ where: { id: batchId } })

    res.json({ success: true })
  } catch (err) {
    next(err)
  }
})

// 归档批次（归档前重算所有条目，确保累计个税/社保包含先前已归档批次的数据）
router.post('/batches/:batchId/archive', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId } = req.params
    const orgId = req.user!.orgId

    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status === 'ARCHIVED') return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '批次已归档' } })

    // 1. 重算本批次所有条目（此时 calcBatchEntry 会包含所有已归档批次的累计数据）
    const entries = await prisma.batchEntry.findMany({ where: { batchId } })
    const recalcErrors: string[] = []
    for (const entry of entries) {
      try {
        const inputs = {
          baseSalary: entry.baseSalary,
          overtimePay: entry.overtimePay,
          allowance: entry.allowance,
          deduction: entry.deduction,
          bonus: entry.bonus,
          positionSalary: entry.positionSalary || undefined,
          performanceSalary: entry.performanceSalary || undefined,
          senioritySalary: entry.senioritySalary || undefined,
          transportAllowance: entry.transportAllowance || undefined,
          mealAllowance: entry.mealAllowance || undefined,
          housingAllowance: entry.housingAllowance || undefined,
          communicationAllowance: entry.communicationAllowance || undefined,
          otherDeduction: entry.otherDeduction || undefined,
        }
        // 社保如被手动覆盖，保留覆盖值
        const overrideSocial: any = {}
        if (entry.socialEmp !== undefined) overrideSocial.socialEmp = entry.socialEmp
        if (entry.socialOrg !== undefined) overrideSocial.socialOrg = entry.socialOrg
        if (entry.housingEmp !== undefined) overrideSocial.housingEmp = entry.housingEmp
        if (entry.housingOrg !== undefined) overrideSocial.housingOrg = entry.housingOrg
        const options = Object.keys(overrideSocial).length > 0 ? { overrideSocial } : undefined

        const calcResult = await calcBatchEntry(orgId, entry.employeeId, batch.month, inputs, batch.type, options)
        await prisma.batchEntry.update({
          where: { id: entry.id },
          data: { ...calcResult },
        })
      } catch (e: any) {
        recalcErrors.push(`${entry.employeeId}: ${e?.message || '重算失败'}`)
      }
    }

    // 2. 更新批次汇总
    const recalcedEntries = await prisma.batchEntry.findMany({ where: { batchId } })
    const totals = recalcedEntries.reduce((acc, e) => ({
      totalPay: acc.totalPay + e.totalPay,
      totalNetPay: acc.totalNetPay + e.netPay,
      totalSocialOrg: acc.totalSocialOrg + e.socialOrg,
      totalSocialEmp: acc.totalSocialEmp + e.socialEmp,
      totalHousingOrg: acc.totalHousingOrg + e.housingOrg,
      totalHousingEmp: acc.totalHousingEmp + e.housingEmp,
      totalTax: acc.totalTax + e.tax,
    }), { totalPay: 0, totalNetPay: 0, totalSocialOrg: 0, totalSocialEmp: 0, totalHousingOrg: 0, totalHousingEmp: 0, totalTax: 0 })

    // 3. 标记为已归档
    await prisma.payrollBatch.update({
      where: { id: batchId },
      data: {
        status: 'ARCHIVED',
        archivedAt: new Date(),
        totalPay: Math.round(totals.totalPay * 100) / 100,
        totalNetPay: Math.round(totals.totalNetPay * 100) / 100,
        totalSocialOrg: Math.round(totals.totalSocialOrg * 100) / 100,
        totalSocialEmp: Math.round(totals.totalSocialEmp * 100) / 100,
        totalHousingOrg: Math.round(totals.totalHousingOrg * 100) / 100,
        totalHousingEmp: Math.round(totals.totalHousingEmp * 100) / 100,
        totalTax: Math.round(totals.totalTax * 100) / 100,
      },
    })

    res.json({ success: true, data: { archived: true, recalculated: entries.length, errors: recalcErrors.length > 0 ? recalcErrors : undefined } })
  } catch (err) {
    next(err)
  }
})

// 取消归档（只能取消最后一个归档批次，依次取消）
router.post('/batches/:batchId/unarchive', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId } = req.params
    const orgId = req.user!.orgId

    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status !== 'ARCHIVED') return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '仅已归档批次可取消归档' } })

    // 检查是否是最后一个归档批次（按 batchNo 倒序）
    const lastArchived = await prisma.payrollBatch.findFirst({
      where: { orgId, month: batch.month, status: 'ARCHIVED' },
      orderBy: { batchNo: 'desc' },
      select: { id: true },
    })
    if (lastArchived?.id !== batchId) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '只能依次取消归档，请先取消最后归档的批次' } })
    }

    // 取消归档
    await prisma.payrollBatch.update({
      where: { id: batchId },
      data: { status: 'DRAFT', archivedAt: null },
    })

    // 如果当月已无归档批次，删除对应工资条（取消个税累计）
    const remainingArchived = await prisma.payrollBatch.count({
      where: { orgId, month: batch.month, status: 'ARCHIVED' },
    })
    if (remainingArchived === 0) {
      // 查找该月所有批次的员工，删除其工资条
      const entries = await prisma.batchEntry.findMany({
        where: { orgId, batch: { month: batch.month } },
        select: { employeeId: true },
        distinct: ['employeeId'],
      })
      if (entries.length > 0) {
        await prisma.payslip.deleteMany({
          where: {
            orgId,
            employeeId: { in: entries.map(e => e.employeeId) },
            month: batch.month,
          },
        })
      }
    } else {
      // 还有归档批次，重新生成工资条（基于剩余归档批次汇总）
      await generatePayslipFromBatches(orgId, batch.month)
    }

    res.json({ success: true, data: { unarchived: true } })
  } catch (err) {
    next(err)
  }
})

// 从已归档批次汇总生成工资条
router.post('/payslips/generate', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { month } = req.body
    const orgId = req.user!.orgId

    if (!month || !/^\d{4}-\d{2}$/.test(month)) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '请提供有效的月份（YYYY-MM）' } })
    }

    // 检查是否有已归档批次
    const archivedBatches = await prisma.payrollBatch.count({
      where: { orgId, month, status: 'ARCHIVED' },
    })
    if (archivedBatches === 0) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '当月无已归档批次，无法生成工资条' } })
    }

    const result = await generatePayslipFromBatches(orgId, month)

    // 自动标记"生成工资条"待办为已完成
    await prisma.riskItem.updateMany({
      where: { orgId, status: 'PENDING', type: 'SALARY', title: { startsWith: `${month}月 生成工资条` } },
      data: { status: 'RESOLVED', resolvedAt: new Date(), resolvedBy: req.user!.id },
    })

    res.json({ success: true, data: { generated: result.generated } })
  } catch (err) {
    next(err)
  }
})

// 银行代发文件导出（接口预留）
router.get('/batches/:batchId/export', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId } = req.params
    const orgId = req.user!.orgId
    const { format = 'csv' } = req.query

    const batch = await prisma.payrollBatch.findFirst({
      where: { id: batchId, orgId },
      include: {
        entries: {
          include: { employee: { select: { name: true, bankAccount: true, bankName: true } } },
        },
      },
    })
    if (!batch) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    if (batch.status !== 'ARCHIVED') return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '仅归档批次可导出' } })

    if (format === 'csv') {
      const header = '姓名,银行账号,开户行,实发金额\n'
      const rows = batch.entries.map(e => `${e.employee.name},${e.employee.bankAccount || ''},${e.employee.bankName || ''},${e.netPay}`).join('\n')
      res.setHeader('Content-Type', 'text/csv; charset=utf-8')
      res.setHeader('Content-Disposition', contentDisposition(`银行代发文件-${batch.month}-批次${batch.batchNo}.csv`))
      return res.send('\ufeff' + header + rows)
    }

    res.json({ success: true, data: batch })
  } catch (err) {
    next(err)
  }
})

// 算薪前 AI 校验
router.get('/batches/:batchId/pre-check', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const result = await prePayrollCheck(req.user!.orgId, req.params.batchId)
    res.json({ success: true, data: result })
  } catch (err: any) {
    if (err?.code === 'NOT_FOUND') {
      return res.status(404).json({ success: false, error: { code: err.code, message: err.message } })
    }
    next(err)
  }
})

// ========== 工资条发布 ==========

// 发布工资条（将批次内所有工资条标记为 PUBLISHED）
router.post('/batches/:batchId/publish', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId } = req.params
    const orgId = req.user!.orgId
    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    }
    // 查找该批次关联的所有工资条（通过 BatchEntry 关联的 employeeId + month）
    const entries = await prisma.batchEntry.findMany({
      where: { batchId, orgId },
      select: { employeeId: true },
    })
    const employeeIds = entries.map(e => e.employeeId)
    if (employeeIds.length === 0) {
      return res.status(400).json({ success: false, error: { code: 'EMPTY', message: '批次内无员工' } })
    }
    // 更新对应月份的工资条
    const result = await prisma.payslip.updateMany({
      where: { orgId, employeeId: { in: employeeIds }, month: batch.month },
      data: { publishStatus: 'PUBLISHED', publishedAt: new Date() },
    })
    res.json({ success: true, data: { published: result.count, month: batch.month } })
  } catch (err) {
    next(err)
  }
})

// 定时发送工资条
router.post('/batches/:batchId/schedule', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const { batchId } = req.params
    const { scheduledAt } = req.body
    if (!scheduledAt) {
      return res.status(400).json({ success: false, error: { code: 'MISSING_DATE', message: '请选择发送时间' } })
    }
    const orgId = req.user!.orgId
    const batch = await prisma.payrollBatch.findFirst({ where: { id: batchId, orgId } })
    if (!batch) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '批次不存在' } })
    }
    const entries = await prisma.batchEntry.findMany({
      where: { batchId, orgId },
      select: { employeeId: true },
    })
    const employeeIds = entries.map(e => e.employeeId)
    if (employeeIds.length === 0) {
      return res.status(400).json({ success: false, error: { code: 'EMPTY', message: '批次内无员工' } })
    }
    const result = await prisma.payslip.updateMany({
      where: { orgId, employeeId: { in: employeeIds }, month: batch.month },
      data: { publishStatus: 'SCHEDULED', scheduledAt: new Date(scheduledAt) },
    })
    res.json({ success: true, data: { scheduled: result.count, scheduledAt } })
  } catch (err) {
    next(err)
  }
})

// 定时发送记录
router.get('/schedule-records', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const records = await prisma.payslip.findMany({
      where: { orgId: req.user!.orgId, publishStatus: 'SCHEDULED' },
      include: { employee: { select: { name: true, department: true } } },
      orderBy: { scheduledAt: 'asc' },
    })
    res.json({ success: true, data: records })
  } catch (err) {
    next(err)
  }
})

// 取消定时发送
router.post('/schedule/:id/cancel', async (req: AuthRequest, res: Response, next: NextFunction) => {
  try {
    const payslip = await prisma.payslip.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId, publishStatus: 'SCHEDULED' },
    })
    if (!payslip) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '定时发送记录不存在' } })
    }
    const updated = await prisma.payslip.update({
      where: { id: payslip.id },
      data: { publishStatus: 'UNPUBLISHED', scheduledAt: null },
    })
    res.json({ success: true, data: updated })
  } catch (err) {
    next(err)
  }
})

export default router
