import { Router, Response } from 'express'
import { authMiddleware, AuthRequest } from '../middleware/auth'
import { auditLog } from '../middleware/auditLog'
import { createEvidence } from '../services/evidence.service'
import prisma from '../lib/prisma'
import { decrypt, encrypt } from '../lib/crypto'
import { getContractStatus } from '../services/contract.service'
import { calcSocialInsurance, calcHousingFund } from '../services/payroll.service'
import ExcelJS from 'exceljs'

const router = Router()

function safeDecrypt(encrypted: string): number {
  try {
    if (!encrypted || !encrypted.includes(':')) return Number(encrypted) || 0
    return Number(decrypt(encrypted))
  } catch {
    return Number(encrypted) || 0
  }
}

function safeDecryptStr(encrypted: string | null): string | null {
  if (!encrypted) return null
  try {
    if (!encrypted.includes(':')) return encrypted
    return decrypt(encrypted)
  } catch {
    return encrypted
  }
}

// ========== 花名册聚合 API ==========

// 获取部门列表（去重）
router.get('/departments', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const employees = await prisma.employee.findMany({
      where: { orgId: req.user!.orgId, status: 'ACTIVE' },
      select: { department: true },
      distinct: 'department',
    })
    const departments = employees.map((e) => e.department).filter(Boolean).sort()
    res.json({ success: true, data: departments })
  } catch (err) {
    next(err)
  }
})

/** 花名册可选职务列表（从在职员工中提取去重） */
router.get('/positions', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const employees = await prisma.employee.findMany({
      where: { orgId: req.user!.orgId, status: 'ACTIVE' },
      select: { position: true },
      distinct: 'position',
    })
    const positions = employees.map((e) => e.position).filter(Boolean).sort()
    res.json({ success: true, data: positions })
  } catch (err) {
    next(err)
  }
})

// 花名册列表（含汇总信息，支持分页和过滤）
router.get('/', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const page = parseInt(req.query.page as string) || 1
    const pageSize = Math.min(parseInt(req.query.pageSize as string) || 20, 200)
    const search = req.query.search as string
    const status = req.query.status as string  // ACTIVE | PRE_HIRE | RESIGNED
    const contractStatus = req.query.contractStatus as string  // active | expiring | expired | unsigned | etc.
    const department = req.query.department as string
    const position = req.query.position as string
    const skip = (page - 1) * pageSize

    // 使用本地日期午夜，避免时区问题导致当天入职被误判为预入职
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    // hireDate 可能以 UTC 午夜存储（如 new Date('2026-07-24')），在 UTC+8 下为 08:00
    // 将 today 前移一天作为比较基准，确保当天入职的员工不被误判为预入职
    const todayEnd = new Date(today)
    todayEnd.setDate(todayEnd.getDate() + 1)

    // 先查询满足 orgId 和搜索条件的员工
    const isIdCardSearch = search && /^\d{2,}$/.test(search)
    const whereBase: any = { orgId: req.user!.orgId }
    if (department) {
      whereBase.department = department
    }
    if (position) {
      whereBase.position = position
    }
    if (search && !isIdCardSearch) {
      whereBase.OR = [
        { name: { contains: search } },
        { department: { contains: search } },
      ]
    }

    // 状态过滤在 DB 层完成（contractStatus 需要后处理计算，仍需内存过滤）
    if (status === 'RESIGNED') {
      whereBase.status = 'RESIGNED'
    } else if (status === 'PRE_HIRE' || status === 'PRE_ONBOARD') {
      // 预入职：ACTIVE 状态但入职日期在未来（按入职日期自动判定）
      whereBase.status = 'ACTIVE'
      whereBase.hireDate = { gt: todayEnd }
    } else if (status === 'ACTIVE') {
      whereBase.status = 'ACTIVE'
      whereBase.hireDate = { lte: todayEnd }
    }

    // unsigned 合同状态可以在 DB 层过滤
    if (contractStatus === 'unsigned') {
      whereBase.contracts = { none: {} }
    }

    // 当有 contractStatus（非 unsigned）筛选或证件号码搜索时，需要先查全部再过滤后分页
    const needPostFilter = (!!contractStatus && contractStatus !== 'unsigned') || isIdCardSearch

    const [dbTotal, employees] = await Promise.all([
      prisma.employee.count({ where: whereBase }),
      prisma.employee.findMany({
        where: whereBase,
        orderBy: { createdAt: 'desc' },
        ...(needPostFilter ? {} : { skip, take: pageSize }),
        include: {
          contracts: { orderBy: { createdAt: 'desc' }, take: 1 },
          terminations: { orderBy: { terminationDate: 'desc' }, take: 1 },
          socialInsRecords: { orderBy: { startMonth: 'desc' }, take: 1 },
          _count: {
            select: {
              disciplinaryRecords: true,
              attendanceRecords: true,
              trainingRecords: true,
              performanceRecords: true,
              payslips: true,
              overtimeRecords: true,
            },
          },
        },
      }),
    ])

    // 获取社保和公积金配置（按城市缓存）
    const currentMonth = new Date().toISOString().slice(0, 7)
    const configCache = new Map<string, { social?: any; housing?: any }>()
    const getConfigsForCity = async (city?: string) => {
      const key = city || '_default'
      if (configCache.has(key)) return configCache.get(key)!
      const cityWhere = city ? { orgId: req.user!.orgId, city } : { orgId: req.user!.orgId }
      const [socialCfg, housingCfg] = await Promise.all([
        prisma.socialInsuranceConfig.findFirst({
          where: { ...cityWhere, effectiveFrom: { lte: currentMonth }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: currentMonth } }] },
          orderBy: { effectiveFrom: 'desc' },
        }),
        prisma.housingFundConfig.findFirst({
          where: { ...cityWhere, effectiveFrom: { lte: currentMonth }, OR: [{ effectiveTo: null }, { effectiveTo: { gte: currentMonth } }] },
          orderBy: { effectiveFrom: 'desc' },
        }),
      ])
      const result = { social: socialCfg, housing: housingCfg }
      configCache.set(key, result)
      return result
    }

    // 预加载所有涉及城市的配置
    const cities = [...new Set(employees.map((e) => e.city).filter(Boolean))] as string[]
    await Promise.all(cities.map((c) => getConfigsForCity(c)))

    // 计算动态状态和合同状态
    let result = employees.map((e) => {
      const latestContract = e.contracts[0] || null
      const contractInfo = latestContract
        ? getContractStatus({
            signDate: latestContract.signDate,
            startDate: latestContract.startDate,
            endDate: latestContract.endDate,
            contractType: latestContract.contractType,
            hireDate: e.hireDate,
            hasRecord: true,
          })
        : getContractStatus({
            signDate: null,
            startDate: e.hireDate,
            endDate: null,
            contractType: 'UNSIGNED',
            hireDate: e.hireDate,
            hasRecord: false,
          })
      const isResigned = e.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= today)
      const isPreHire = !isResigned && e.hireDate > todayEnd
      const dynamicStatus = isResigned ? 'RESIGNED' : (isPreHire ? 'PRE_HIRE' : 'ACTIVE')
      return {
        id: e.id,
        name: e.name,
        department: e.department,
        position: e.position,
        city: e.city,
        status: dynamicStatus,
        hasTermination: e.terminations.length > 0,
        latestTerminationDate: e.terminations[0]?.terminationDate || null,
        latestTerminationType: e.terminations[0]?.type || null,
        latestTerminationId: e.terminations[0]?.id || null,
        latestTerminationStatus: e.terminations[0]?.status || null,
        hireDate: e.hireDate,
        gender: e.gender,
        phone: e.phone,
        idCardNumber: safeDecryptStr(e.idCardNumber),
        monthlySalary: safeDecrypt(e.monthlySalary),
        baseSalary: e.baseSalary ? safeDecrypt(e.baseSalary) : null,
        performanceSalary: e.performanceSalary ? safeDecrypt(e.performanceSalary) : null,
        socialInsBase: (() => {
          const ct = latestContract?.contractType
          if (!ct || ['LABOR', 'INTERNSHIP', 'PARTTIME', 'OUTSOURCING', 'UNSIGNED'].includes(ct)) return null
          return e.socialInsBase
        })(),
        housingFundBase: (() => {
          const ct = latestContract?.contractType
          if (!ct || ['LABOR', 'INTERNSHIP', 'PARTTIME', 'OUTSOURCING', 'UNSIGNED'].includes(ct)) return null
          return e.housingFundBase
        })(),
        socialInsCalc: (() => {
          const contractType = latestContract?.contractType
          // 劳务协议、实习协议、兼职协议、业务外包等非劳动合同不缴纳社保
          if (!contractType || ['LABOR', 'INTERNSHIP', 'PARTTIME', 'OUTSOURCING', 'UNSIGNED'].includes(contractType)) return null
          const cfgs = configCache.get(e.city || '_default')
          if (!cfgs?.social || !e.socialInsBase) return null
          const r = calcSocialInsurance(e.socialInsBase, cfgs.social)
          return { socialEmp: r.socialEmp, socialOrg: r.socialOrg }
        })(),
        housingFundCalc: (() => {
          const contractType = latestContract?.contractType
          if (!contractType || ['LABOR', 'INTERNSHIP', 'PARTTIME', 'OUTSOURCING', 'UNSIGNED'].includes(contractType)) return null
          const cfgs = configCache.get(e.city || '_default')
          if (!cfgs?.housing || !e.housingFundBase) return null
          const r = calcHousingFund(e.housingFundBase, cfgs.housing)
          return { housingEmp: r.housingEmp, housingOrg: r.housingOrg }
        })(),
        isPregnant: e.isPregnant,
        isInMedicalPeriod: e.isInMedicalPeriod,
        isWorkInjured: e.isWorkInjured,
        latestContract,
        contractStatus: contractInfo.status,
        contractStatusText: contractInfo.statusText,
        riskLevel: contractInfo.riskLevel,
        socialInsuranceStatus: (() => {
          const sr = (e as any).socialInsRecords?.[0]
          // 劳务协议/实习协议：不缴纳社保，返回 null（前端显示"—"）
          const isNoSocialContract = latestContract && ['LABOR', 'INTERNSHIP'].includes(latestContract.contractType)
          if (isNoSocialContract) return null
          if (!sr) {
            // 在职且应缴社保但无社保记录 → 待办理
            return 'PENDING'
          }
          // endMonth 为 null 表示在保，否则已停保
          if (sr.endMonth) return 'SUSPENDED'
          return 'ACTIVE'
        })(),
        probationInfo: (() => {
          if (!latestContract || latestContract.probationMonths === 0) return null
          const probEnd = new Date(e.hireDate)
          probEnd.setMonth(probEnd.getMonth() + latestContract.probationMonths)
          const daysToConfirm = Math.ceil((probEnd.getTime() - today.getTime()) / 86400000)
          return {
            months: latestContract.probationMonths,
            endDate: probEnd.toISOString().slice(0, 10),
            daysToConfirm,
            isProbation: daysToConfirm > 0 && dynamicStatus === 'ACTIVE',
            isExpiring: daysToConfirm <= 7 && daysToConfirm > 0,
          }
        })(),
        counts: e._count,
      }
    })

    // 前端过滤：合同状态（非 unsigned 的需要后处理计算）
    if (contractStatus && contractStatus !== 'unsigned') {
      result = result.filter((e) => e.contractStatus === contractStatus)
    }

    // 证件号码后N位搜索：在内存中过滤（idCardNumber 已解密为明文）
    if (isIdCardSearch) {
      result = result.filter((e: any) => {
        if (!e.idCardNumber) return false
        return String(e.idCardNumber).endsWith(search!)
      })
    }

    // 计算过滤后的总数和分页
    const needMemoryPaging = needPostFilter || isIdCardSearch
    const filteredTotal = needMemoryPaging ? result.length : dbTotal
    if (needMemoryPaging) {
      result = result.slice(skip, skip + pageSize)
    }

    // 全局合同风险统计（基于 RiskItem，与风险中心同口径）
    const [globalExpiring, globalExpired, globalUnsigned] = await Promise.all([
      prisma.riskItem.count({ where: { orgId: req.user!.orgId, status: 'PENDING', type: 'CONTRACT', level: 'MEDIUM' } }),
      prisma.riskItem.count({ where: { orgId: req.user!.orgId, status: 'PENDING', type: 'CONTRACT', level: 'HIGH' } }),
      prisma.riskItem.count({ where: { orgId: req.user!.orgId, status: 'PENDING', type: 'ONBOARDING' } }),
    ])

    res.json({
      success: true,
      data: result,
      pagination: {
        page,
        pageSize,
        total: filteredTotal,
        totalPages: Math.ceil(filteredTotal / pageSize),
      },
      globalRiskStats: {
        expiring: globalExpiring,
        expired: globalExpired,
        unsigned: globalUnsigned,
      },
    })
  } catch (err) {
    next(err)
  }
})

// 员工完整档案（花名册详情）
router.get('/:id/profile', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const employee = await prisma.employee.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
      include: {
        contracts: { orderBy: { createdAt: 'desc' } },
        payslips: { orderBy: { month: 'desc' } },
        overtimeRecords: { orderBy: { month: 'desc' } },
        disciplinaryRecords: { orderBy: { violationDate: 'desc' } },
        attendanceRecords: { orderBy: { date: 'desc' }, take: 90 },
        trainingRecords: { orderBy: { trainingDate: 'desc' } },
        performanceRecords: { orderBy: { period: 'desc' } },
        terminations: { orderBy: { createdAt: 'desc' } },
        attachments: true,
        socialInsRecords: { orderBy: { startMonth: 'desc' }, include: { account: { select: { id: true, name: true, city: true } } } },
        housingFundRecords: { orderBy: { startMonth: 'desc' }, include: { account: { select: { id: true, name: true, city: true } } } },
        salaryChanges: { orderBy: { effectiveDate: 'desc' } },
        departmentRecords: { orderBy: { effectiveMonth: 'desc' } },
      },
    })
    if (!employee) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
    }

    // 查询该员工相关的月度办理记录（从快照中筛选该员工）
    const allProcesses = await prisma.socialMonthlyProcess.findMany({
      where: { orgId: req.user!.orgId },
      orderBy: { month: 'desc' },
    })
    const employeeId = req.params.id
    const monthlyProcessRecords: any[] = []
    for (const p of allProcesses) {
      const snap = p.snapshot as any
      // 从增减员快照中筛选
      const changes = snap.changes
      const active = snap.active
      const type = p.type
      let found = false
      let recordData: any = { month: p.month, type, processedAt: p.processedAt, status: p.status }
      if (changes?.additions) {
        const item = changes.additions.find((a: any) => a.employeeId === employeeId)
        if (item) { recordData.changeType = '新增'; recordData.detail = item.detail; recordData.base = item.base; recordData.city = item.city; found = true }
      }
      if (!found && changes?.reductions) {
        const item = changes.reductions.find((a: any) => a.employeeId === employeeId)
        if (item) { recordData.changeType = '减少'; recordData.detail = item.detail; recordData.base = item.base; recordData.city = item.city; found = true }
      }
      if (!found && active?.items) {
        const item = active.items.find((a: any) => a.employeeId === employeeId)
        if (item) { recordData.changeType = '正常在保'; recordData.detail = item.detail; recordData.base = item.base; recordData.city = item.city; found = true }
      }
      if (found) monthlyProcessRecords.push(recordData)
    }

    const { monthlySalary, baseSalary, performanceSalary, bankAccount, idCardNumber, ...rest } = employee
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    const dynamicStatus = employee.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= today) ? 'RESIGNED' : 'ACTIVE'
    res.json({
      success: true,
      data: {
        ...rest,
        status: dynamicStatus,
        monthlySalary: safeDecrypt(monthlySalary),
        baseSalary: baseSalary ? safeDecrypt(baseSalary) : null,
        performanceSalary: performanceSalary ? safeDecrypt(performanceSalary) : null,
        bankAccount: bankAccount ? safeDecrypt(bankAccount).toString() : null,
        idCardNumber: safeDecryptStr(idCardNumber),
        monthlyProcessRecords,
      },
    })
  } catch (err) {
    next(err)
  }
})

// 仲裁证据链导出
router.get('/:id/evidence-chain', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const employee = await prisma.employee.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
      include: {
        contracts: { orderBy: { createdAt: 'desc' } },
        payslips: { orderBy: { month: 'desc' } },
        overtimeRecords: { orderBy: { month: 'desc' } },
        disciplinaryRecords: { orderBy: { violationDate: 'desc' } },
        attendanceRecords: { orderBy: { date: 'desc' } },
        trainingRecords: { orderBy: { trainingDate: 'desc' } },
        performanceRecords: { orderBy: { period: 'desc' } },
        terminations: true,
      },
    })
    if (!employee) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
    }

    const evidence: any[] = []
    const empName = employee.name
    const empDept = employee.department
    const hireDate = employee.hireDate.toISOString().slice(0, 10)

    // 风险检测
    const risks: any[] = []
    const today = new Date()
    today.setHours(0, 0, 0, 0)

    // 1. 劳动关系证据
    evidence.push({
      category: '劳动关系',
      title: '入职登记',
      date: hireDate,
      description: `${empName}于${hireDate}入职${empDept}，建立劳动关系。`,
      evidenceType: 'EMPLOYMENT',
    })

    // 无合同风险检测
    if (employee.contracts.length === 0) {
      const daysSinceHire = Math.floor((today.getTime() - employee.hireDate.getTime()) / (86400000))
      if (daysSinceHire > 30) {
        risks.push({
          level: daysSinceHire > 365 ? 'DANGER' : 'HIGH',
          category: '劳动关系',
          title: '未签订书面劳动合同',
          description: `入职已${Math.round(daysSinceHire / 30)}个月仍未签订书面劳动合同，超过30天未签合同将面临双倍工资赔偿风险（《劳动合同法》第82条）。${daysSinceHire > 365 ? '已满一年未签合同，视为已订立无固定期限劳动合同。' : ''}`,
        })
      }
    }

    employee.contracts.forEach((c) => {
      const contractTypeText = ({ FIXED: '固定期限', UNFIXED: '无固定期限', LABOR: '劳务协议', INTERNSHIP: '实习协议', UNSIGNED: '未签订' } as Record<string, string>)[c.contractType] || '未签订'
      const isSigned = !!c.signDate
      evidence.push({
        category: '劳动关系',
        title: `合同（${contractTypeText}）`,
        date: c.signDate ? c.signDate.toISOString().slice(0, 10) : c.startDate.toISOString().slice(0, 10),
        description: `合同期限：${c.startDate.toISOString().slice(0, 10)} 至 ${c.endDate ? c.endDate.toISOString().slice(0, 10) : '无固定期限'}，试用期${c.probationMonths}个月，试用期工资¥${c.probationSalary}。${isSigned ? '' : '⚠ 该合同尚未签订。'}`,
        evidenceType: 'CONTRACT',
        signed: isSigned,
        riskLevel: !isSigned ? 'HIGH' : undefined,
      })

      // 合同未签字风险
      if (!isSigned) {
        risks.push({
          level: 'HIGH',
          category: '劳动关系',
          title: '合同未签字',
          description: `合同（${contractTypeText}）期限${c.startDate.toISOString().slice(0, 10)}至${c.endDate ? c.endDate.toISOString().slice(0, 10) : '无固定期限'}，尚未签订。未签合同在仲裁中无法证明劳动关系约定内容。`,
        })
      }

      // 合同过期风险
      if (c.endDate && c.endDate < today) {
        const expiredDays = Math.floor((today.getTime() - c.endDate.getTime()) / 86400000)
        risks.push({
          level: expiredDays > 30 ? 'HIGH' : 'MEDIUM',
          category: '劳动关系',
          title: '合同已过期',
          description: `合同已于${c.endDate.toISOString().slice(0, 10)}过期，过期${expiredDays}天。过期后继续用工满一个月未续签的，面临双倍工资风险。`,
        })
      } else if (c.endDate) {
        const daysToExpire = Math.floor((c.endDate.getTime() - today.getTime()) / 86400000)
        if (daysToExpire <= 30 && daysToExpire >= 0) {
          risks.push({
            level: 'MEDIUM',
            category: '劳动关系',
            title: '合同即将到期',
            description: `合同将于${c.endDate.toISOString().slice(0, 10)}到期，剩余${daysToExpire}天。请及时办理续签或终止手续。`,
          })
        }
      }

      // 试用期工资为0风险
      if (c.probationMonths > 0 && c.probationSalary === 0) {
        risks.push({
          level: 'MEDIUM',
          category: '劳动关系',
          title: '试用期工资为0',
          description: `合同约定试用期${c.probationMonths}个月但试用期工资为0，违反《劳动合同法》第20条（试用期工资不得低于本单位相同岗位最低档工资的80%或劳动合同约定工资的80%）。`,
        })
      }
    })

    // 无工资条风险检测（入职超过1个月但无工资条）
    const daysSinceHire = Math.floor((today.getTime() - employee.hireDate.getTime()) / 86400000)
    if (daysSinceHire > 30 && employee.payslips.length === 0) {
      risks.push({
        level: 'MEDIUM',
        category: '薪酬发放',
        title: '无工资条记录',
        description: `入职已${Math.round(daysSinceHire / 30)}个月但无任何工资条记录，在仲裁中难以证明已按时足额支付工资。建议尽快创建发薪批次并归档。`,
      })
    }

    // 2. 薪酬证据
    employee.payslips.forEach((p) => {
      evidence.push({
        category: '薪酬发放',
        title: `${p.month}月工资条`,
        date: p.month,
        description: `基本工资¥${p.baseSalary.toFixed(2)}，加班费¥${p.overtimePay.toFixed(2)}，津贴¥${p.allowance.toFixed(2)}，扣款¥${p.deduction.toFixed(2)}，应发合计¥${p.totalPay.toFixed(2)}。${p.confirmedAt ? '员工已确认。' : '员工未确认。'}`,
        evidenceType: 'PAYSLIP',
        confirmed: !!p.confirmedAt,
      })
    })
    employee.overtimeRecords.forEach((o) => {
      if (o.totalPay > 0) {
        evidence.push({
          category: '薪酬发放',
          title: `${o.month}月加班费记录`,
          date: o.month,
          description: `工作日加班${o.weekdayHours}h，休息日加班${o.weekendHours}h，节假日加班${o.holidayHours}h，加班费合计¥${o.totalPay.toFixed(2)}。`,
          evidenceType: 'OVERTIME',
        })
      }
    })

    // 3. 考勤证据
    const abnormalAttendance = employee.attendanceRecords.filter((a) => a.status !== 'NORMAL')
    abnormalAttendance.forEach((a) => {
      const statusMap: Record<string, string> = { LATE: '迟到', EARLY_LEAVE: '早退', ABSENT: '旷工', LEAVE: '请假', BUSINESS_TRIP: '出差' }
      evidence.push({
        category: '考勤记录',
        title: `${a.date.toISOString().slice(0, 10)} 考勤异常`,
        date: a.date.toISOString().slice(0, 10),
        description: `状态：${statusMap[a.status] || a.status}${a.lateMinutes ? `，迟到${a.lateMinutes}分钟` : ''}${a.earlyMinutes ? `，早退${a.earlyMinutes}分钟` : ''}。${a.remark || ''}`,
        evidenceType: 'ATTENDANCE',
      })
    })

    // 4. 违纪证据
    employee.disciplinaryRecords.forEach((d) => {
      const typeMap: Record<string, string> = { LATE: '迟到', ABSENT: '旷工', INSUBORDINATION: '不服从管理', MISCONDUCT: '违纪', VIOLATE_POLICY: '违反规章制度', OTHER: '其他' }
      const actionMap: Record<string, string> = { ORAL_WARNING: '口头警告', WRITTEN_WARNING: '书面警告', DEDUCTION: '扣款', DEMOTION: '降职', TERMINATION: '解除劳动合同' }
      evidence.push({
        category: '违纪处理',
        title: `${d.violationDate.toISOString().slice(0, 10)} ${typeMap[d.violationType] || d.violationType}`,
        date: d.violationDate.toISOString().slice(0, 10),
        description: `违纪事实：${d.description}。处理结果：${actionMap[d.action] || d.action}。${d.employeeAck ? `员工已签字确认（${d.ackDate ? d.ackDate.toISOString().slice(0, 10) : ''}）。` : '员工未签字。'}${d.witness ? `见证人：${d.witness}。` : ''}`,
        evidenceType: 'DISCIPLINARY',
        acknowledged: d.employeeAck,
      })
    })

    // 5. 培训签收证据
    employee.trainingRecords.forEach((t) => {
      const ackMap: Record<string, string> = { PENDING: '待签收', SIGNED: '已签收', REFUSED: '拒绝签收' }
      evidence.push({
        category: '培训签收',
        title: `${t.trainingDate.toISOString().slice(0, 10)} ${t.topic}`,
        date: t.trainingDate.toISOString().slice(0, 10),
        description: `培训主题：${t.topic}。时长：${t.duration}小时。${t.content ? `内容：${t.content}。` : ''}签收状态：${ackMap[t.ackStatus] || t.ackStatus}。`,
        evidenceType: 'TRAINING',
        acknowledged: t.ackStatus === 'SIGNED',
      })
    })

    // 6. 绩效证据
    employee.performanceRecords.forEach((p) => {
      const resultMap: Record<string, string> = { EXCELLENT: '优秀', QUALIFIED: '合格', NEED_IMPROVE: '需改进', UNQUALIFIED: '不胜任' }
      evidence.push({
        category: '绩效考核',
        title: `${p.period} 绩效考核`,
        date: p.period,
        description: `得分：${p.score}，等级：${p.grade}，结果：${resultMap[p.result] || p.result}。${p.summary ? `评语：${p.summary}。` : ''}${p.improvementPlan ? `改进计划：${p.improvementPlan}。` : ''}${p.employeeAck ? '员工已签字确认。' : '员工未签字。'}`,
        evidenceType: 'PERFORMANCE',
        acknowledged: p.employeeAck,
      })
    })

    // 7. 解聘证据（排除已撤销的记录）
    employee.terminations.filter((t) => t.status !== 'CANCELLED').forEach((t) => {
      const reasonMap: Record<string, string> = { NEGOTIATED: '协商一致', FAULT: '员工过错', NONFAULT: '非过错解除', LAYOFF: '经济性裁员', EXPIRED: '合同到期', RESIGNATION: '员工主动离职' }
      const typeLabel = t.type === 'RESIGNATION' && t.reason === 'NEGOTIATED'
        ? '协商一致离职'
        : t.type === 'RESIGNATION' ? '员工主动离职' : '公司解聘'
      const statusMap: Record<string, string> = { DRAFT: '草稿', PENDING_APPROVAL: '待审批', APPROVED: '已审批', EXECUTING: '执行中', COMPLETED: '已完成', REJECTED: '已驳回', CANCELLED: '已撤销' }
      evidence.push({
        category: '解聘记录',
        title: `${t.terminationDate.toISOString().slice(0, 10)} ${typeLabel}记录`,
        date: t.terminationDate.toISOString().slice(0, 10),
        description: `类型：${typeLabel}。原因：${reasonMap[t.reason] || t.reason}。经济补偿金：¥${t.compensation.toFixed(2)}。流程状态：${statusMap[t.status] || t.status}。${t.resignationReason ? `离职原因：${t.resignationReason}。` : ''}${t.remark || ''}`,
        evidenceType: 'TERMINATION',
        acknowledged: t.status === 'COMPLETED',
      })

      // 离职流程未完成风险
      if (t.status !== 'COMPLETED') {
        risks.push({
          level: 'HIGH',
          category: '解聘记录',
          title: '离职流程未完成',
          description: `${typeLabel}流程当前状态为「${statusMap[t.status] || t.status}」，尚未完成闭环。离职日期${t.terminationDate.toISOString().slice(0, 10)}，若未签署解除协议/离职确认书、未完成工作交接、未结清工资、未办理社保减员，在仲裁中无法证明离职的合法性与完整性，面临继续履行合同或违法解除赔偿（2N）风险。`,
        })
      }

      // 协商解除但补偿金为0
      if (t.reason === 'NEGOTIATED' && t.compensation === 0) {
        risks.push({
          level: 'MEDIUM',
          category: '解聘记录',
          title: '协商解除但补偿金为0',
          description: `解聘原因为协商解除但经济补偿金为0，若员工事后主张非自愿离职，企业需举证协商一致且员工自愿放弃补偿，否则可能被认定为单方违法解除，面临2N赔偿风险。`,
        })
      }
    })

    res.json({
      success: true,
      data: {
        employee: {
          name: empName,
          department: empDept,
          hireDate,
          status: employee.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= new Date()) ? 'RESIGNED' : 'ACTIVE',
          gender: employee.gender,
          phone: employee.phone,
          idCardNumber: safeDecryptStr(employee.idCardNumber),
        },
        evidence,
        risks,
        summary: {
          total: evidence.length,
          signed: evidence.filter((e) => e.acknowledged === true).length,
          unsigned: evidence.filter((e) => e.acknowledged === false).length,
          riskCount: risks.length,
        },
      },
    })
  } catch (err) {
    next(err)
  }
})

// 仲裁证据链 Excel 导出
router.get('/:id/evidence-chain/export', authMiddleware, async (req: AuthRequest, res: Response, next) => {
  try {
    const employee = await prisma.employee.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
      include: {
        contracts: { orderBy: { createdAt: 'desc' } },
        payslips: { orderBy: { month: 'desc' } },
        overtimeRecords: { orderBy: { month: 'desc' } },
        disciplinaryRecords: { orderBy: { violationDate: 'desc' } },
        attendanceRecords: { orderBy: { date: 'desc' } },
        trainingRecords: { orderBy: { trainingDate: 'desc' } },
        performanceRecords: { orderBy: { period: 'desc' } },
        terminations: { where: { status: { not: 'CANCELLED' } }, orderBy: { createdAt: 'desc' } },
      },
    })
    if (!employee) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
    }

    const empName = employee.name
    const empDept = employee.department
    const hireDate = employee.hireDate.toISOString().slice(0, 10)
    const today = new Date(); today.setHours(0, 0, 0, 0)

    const workbook = new ExcelJS.Workbook()

    // Sheet 1: 员工信息
    const wsInfo = workbook.addWorksheet('员工信息')
    wsInfo.columns = [
      { header: '项目', key: 'label', width: 16 },
      { header: '内容', key: 'value', width: 40 },
    ]
    wsInfo.getRow(1).font = { bold: true }
    const empStatus = employee.terminations.some((t) => t.status === 'COMPLETED' && t.terminationDate <= today) ? '离职' : '在职'
    wsInfo.addRows([
      { label: '姓名', value: empName },
      { label: '身份证号', value: safeDecryptStr(employee.idCardNumber) || '' },
      { label: '部门', value: empDept },
      { label: '入职日期', value: hireDate },
      { label: '状态', value: empStatus },
      { label: '导出时间', value: new Date().toLocaleString('zh-CN') },
    ])

    // Sheet 2: 证据清单
    const wsEv = workbook.addWorksheet('证据清单')
    wsEv.columns = [
      { header: '序号', key: 'no', width: 6 },
      { header: '类别', key: 'category', width: 12 },
      { header: '标题', key: 'title', width: 28 },
      { header: '日期', key: 'date', width: 12 },
      { header: '描述', key: 'description', width: 60 },
      { header: '签字状态', key: 'ack', width: 10 },
    ]
    wsEv.getRow(1).font = { bold: true }

    let evNo = 0
    // 劳动关系
    evNo++; wsEv.addRow({ no: evNo, category: '劳动关系', title: '入职登记', date: hireDate, description: `${empName}于${hireDate}入职${empDept}，建立劳动关系。`, ack: '' })
    employee.contracts.forEach((c) => {
      const typeText = ({ FIXED: '固定期限', UNFIXED: '无固定期限', LABOR: '劳务协议', INTERNSHIP: '实习协议', UNSIGNED: '未签订' } as Record<string, string>)[c.contractType] || '未签订'
      evNo++
      wsEv.addRow({
        no: evNo, category: '劳动关系', title: `合同（${typeText}）`,
        date: c.signDate ? c.signDate.toISOString().slice(0, 10) : c.startDate.toISOString().slice(0, 10),
        description: `合同期限：${c.startDate.toISOString().slice(0, 10)} 至 ${c.endDate ? c.endDate.toISOString().slice(0, 10) : '无固定期限'}，试用期${c.probationMonths}个月，试用期工资¥${c.probationSalary}。${c.signDate ? '' : '⚠ 该合同尚未签订。'}`,
        ack: c.signDate ? '已签字' : '未签字',
      })
    })

    // 薪酬发放
    employee.payslips.forEach((p) => {
      evNo++
      wsEv.addRow({
        no: evNo, category: '薪酬发放', title: `${p.month}月工资条`, date: p.month,
        description: `基本工资¥${p.baseSalary.toFixed(2)}，加班费¥${p.overtimePay.toFixed(2)}，津贴¥${p.allowance.toFixed(2)}，扣款¥${p.deduction.toFixed(2)}，应发合计¥${p.totalPay.toFixed(2)}。${p.confirmedAt ? '员工已确认。' : '员工未确认。'}`,
        ack: p.confirmedAt ? '已签字' : '未签字',
      })
    })
    employee.overtimeRecords.forEach((o) => {
      if (o.totalPay > 0) {
        evNo++
        wsEv.addRow({ no: evNo, category: '薪酬发放', title: `${o.month}月加班费记录`, date: o.month, description: `工作日加班${o.weekdayHours}h，休息日加班${o.weekendHours}h，节假日加班${o.holidayHours}h，加班费合计¥${o.totalPay.toFixed(2)}。`, ack: '' })
      }
    })

    // 考勤记录
    const statusMap: Record<string, string> = { LATE: '迟到', EARLY_LEAVE: '早退', ABSENT: '旷工', LEAVE: '请假', BUSINESS_TRIP: '出差' }
    employee.attendanceRecords.filter((a) => a.status !== 'NORMAL').forEach((a) => {
      evNo++
      wsEv.addRow({ no: evNo, category: '考勤记录', title: `${a.date.toISOString().slice(0, 10)} 考勤异常`, date: a.date.toISOString().slice(0, 10), description: `状态：${statusMap[a.status] || a.status}${a.lateMinutes ? `，迟到${a.lateMinutes}分钟` : ''}${a.earlyMinutes ? `，早退${a.earlyMinutes}分钟` : ''}。${a.remark || ''}`, ack: '' })
    })

    // 违纪处理
    const typeMap: Record<string, string> = { LATE: '迟到', ABSENT: '旷工', INSUBORDINATION: '不服从管理', MISCONDUCT: '违纪', VIOLATE_POLICY: '违反规章制度', OTHER: '其他' }
    const actionMap: Record<string, string> = { ORAL_WARNING: '口头警告', WRITTEN_WARNING: '书面警告', DEDUCTION: '扣款', DEMOTION: '降职', TERMINATION: '解除劳动合同' }
    employee.disciplinaryRecords.forEach((d) => {
      evNo++
      wsEv.addRow({ no: evNo, category: '违纪处理', title: `${d.violationDate.toISOString().slice(0, 10)} ${typeMap[d.violationType] || d.violationType}`, date: d.violationDate.toISOString().slice(0, 10), description: `违纪事实：${d.description}。处理结果：${actionMap[d.action] || d.action}。${d.employeeAck ? `员工已签字确认。` : '员工未签字。'}${d.witness ? `见证人：${d.witness}。` : ''}`, ack: d.employeeAck ? '已签字' : '未签字' })
    })

    // 培训签收
    const ackMap: Record<string, string> = { PENDING: '待签收', SIGNED: '已签收', REFUSED: '拒绝签收' }
    employee.trainingRecords.forEach((t) => {
      evNo++
      wsEv.addRow({ no: evNo, category: '培训签收', title: `${t.trainingDate.toISOString().slice(0, 10)} ${t.topic}`, date: t.trainingDate.toISOString().slice(0, 10), description: `培训主题：${t.topic}。时长：${t.duration}小时。${t.content ? `内容：${t.content}。` : ''}签收状态：${ackMap[t.ackStatus] || t.ackStatus}。`, ack: t.ackStatus === 'SIGNED' ? '已签字' : '未签字' })
    })

    // 绩效考核
    const resultMap: Record<string, string> = { EXCELLENT: '优秀', QUALIFIED: '合格', NEED_IMPROVE: '需改进', UNQUALIFIED: '不胜任' }
    employee.performanceRecords.forEach((p) => {
      evNo++
      wsEv.addRow({ no: evNo, category: '绩效考核', title: `${p.period} 绩效考核`, date: p.period, description: `得分：${p.score}，等级：${p.grade}，结果：${resultMap[p.result] || p.result}。${p.summary ? `评语：${p.summary}。` : ''}${p.improvementPlan ? `改进计划：${p.improvementPlan}。` : ''}${p.employeeAck ? '员工已签字确认。' : '员工未签字。'}`, ack: p.employeeAck ? '已签字' : '未签字' })
    })

    // 解聘记录
    const reasonMap: Record<string, string> = { NEGOTIATED: '协商一致', FAULT: '员工过错', NONFAULT: '非过错解除', LAYOFF: '经济性裁员', EXPIRED: '合同到期', RESIGNATION: '员工主动离职' }
    const termStatusMap: Record<string, string> = { DRAFT: '草稿', PENDING_APPROVAL: '待审批', APPROVED: '已审批', EXECUTING: '执行中', COMPLETED: '已完成', REJECTED: '已驳回', CANCELLED: '已撤销' }
    employee.terminations.forEach((t) => {
      const typeLabel = t.type === 'RESIGNATION' && t.reason === 'NEGOTIATED' ? '协商一致离职' : t.type === 'RESIGNATION' ? '员工主动离职' : '公司解聘'
      evNo++
      wsEv.addRow({ no: evNo, category: '解聘记录', title: `${t.terminationDate.toISOString().slice(0, 10)} ${typeLabel}记录`, date: t.terminationDate.toISOString().slice(0, 10), description: `类型：${typeLabel}。原因：${reasonMap[t.reason] || t.reason}。经济补偿金：¥${t.compensation.toFixed(2)}。流程状态：${termStatusMap[t.status] || t.status}。${t.resignationReason ? `离职原因：${t.resignationReason}。` : ''}${t.remark || ''}`, ack: t.status === 'COMPLETED' ? '已签字' : '未签字' })
    })

    // Sheet 3: 风险提醒
    const risks: any[] = []
    if (employee.contracts.length === 0) {
      const days = Math.floor((today.getTime() - employee.hireDate.getTime()) / 86400000)
      if (days > 30) risks.push({ level: days > 365 ? '危险' : '高', category: '劳动关系', title: '未签订书面劳动合同', description: `入职已${Math.round(days / 30)}个月仍未签订书面劳动合同，超过30天未签合同将面临双倍工资赔偿风险。${days > 365 ? '已满一年未签合同，视为已订立无固定期限劳动合同。' : ''}` })
    }
    employee.contracts.forEach((c) => {
      if (!c.signDate) risks.push({ level: '高', category: '劳动关系', title: '合同未签字', description: `合同期限${c.startDate.toISOString().slice(0, 10)}至${c.endDate ? c.endDate.toISOString().slice(0, 10) : '无固定期限'}，尚未签订。` })
      if (c.endDate && c.endDate < today) {
        const expiredDays = Math.floor((today.getTime() - c.endDate.getTime()) / 86400000)
        risks.push({ level: expiredDays > 30 ? '高' : '中', category: '劳动关系', title: '合同已过期', description: `合同已于${c.endDate.toISOString().slice(0, 10)}过期，过期${expiredDays}天。` })
      } else if (c.endDate) {
        const daysToExpire = Math.floor((c.endDate.getTime() - today.getTime()) / 86400000)
        if (daysToExpire <= 30 && daysToExpire >= 0) risks.push({ level: '中', category: '劳动关系', title: '合同即将到期', description: `合同将于${c.endDate.toISOString().slice(0, 10)}到期，剩余${daysToExpire}天。` })
      }
      if (c.probationMonths > 0 && c.probationSalary === 0) risks.push({ level: '中', category: '劳动关系', title: '试用期工资为0', description: `合同约定试用期${c.probationMonths}个月但试用期工资为0，违反《劳动合同法》第20条。` })
    })
    const daysSinceHire = Math.floor((today.getTime() - employee.hireDate.getTime()) / 86400000)
    if (daysSinceHire > 30 && employee.payslips.length === 0) risks.push({ level: '中', category: '薪酬发放', title: '无工资条记录', description: `入职已${Math.round(daysSinceHire / 30)}个月但无任何工资条记录。` })
    employee.terminations.forEach((t) => {
      if (t.status !== 'COMPLETED') risks.push({ level: '高', category: '解聘记录', title: '离职流程未完成', description: `流程当前状态为「${termStatusMap[t.status] || t.status}」，尚未完成闭环。` })
      if (t.reason === 'NEGOTIATED' && t.compensation === 0) risks.push({ level: '中', category: '解聘记录', title: '协商解除但补偿金为0', description: '解聘原因为协商解除但经济补偿金为0，面临2N赔偿风险。' })
    })

    const wsRisk = workbook.addWorksheet('风险提醒')
    wsRisk.columns = [
      { header: '序号', key: 'no', width: 6 },
      { header: '风险等级', key: 'level', width: 10 },
      { header: '类别', key: 'category', width: 12 },
      { header: '标题', key: 'title', width: 24 },
      { header: '描述', key: 'description', width: 60 },
    ]
    wsRisk.getRow(1).font = { bold: true }
    risks.forEach((r, i) => wsRisk.addRow({ no: i + 1, ...r }))

    const fullFileName = `${empName}_证据链.xlsx`
    const encodedName = encodeURIComponent(fullFileName)
    const asciiFallback = `evidence_chain_${employee.id.slice(-8)}.xlsx`
    res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
    res.setHeader('Content-Disposition', `attachment; filename="${asciiFallback}"; filename*=UTF-8''${encodedName}`)
    const buffer = await workbook.xlsx.writeBuffer()
    res.send(Buffer.from(buffer))
  } catch (err: any) {
    console.error('证据链导出失败:', err?.message || err)
    if (!res.headersSent) {
      res.status(500).json({ success: false, error: { code: 'EXPORT_FAILED', message: `导出失败：${err?.message || '服务器错误'}` } })
    } else {
      res.end()
    }
  }
})

// ========== 组织级列表查询 ==========

// 培训记录列表（全员）
router.get('/training/list', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const orgId = req.user!.orgId
    const page = parseInt(req.query.page as string) || 1
    const pageSize = parseInt(req.query.pageSize as string) || 20
    const keyword = (req.query.keyword as string) || ''
    const ackStatus = (req.query.ackStatus as string) || ''
    const where: any = { orgId }
    if (ackStatus) {
      where.ackStatus = ackStatus
    }
    if (keyword) {
      const employees = await prisma.employee.findMany({
        where: { orgId, name: { contains: keyword } },
        select: { id: true },
      })
      where.employeeId = { in: employees.map(e => e.id) }
    }
    const [rawRecords, total] = await Promise.all([
      prisma.trainingRecord.findMany({
        where,
        include: { employee: { select: { id: true, name: true, department: true, idCardNumber: true } } },
        orderBy: { trainingDate: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      prisma.trainingRecord.count({ where }),
    ])
    const records = rawRecords.map((r: any) => ({
      ...r,
      employee: r.employee ? { ...r.employee, idCardNumber: safeDecryptStr(r.employee.idCardNumber) } : r.employee,
    }))
    res.json({ success: true, data: { records, total, page, pageSize } })
  } catch (err) { next(err) }
})

// 培训记录催办（发送通知给未签收员工）
router.post('/training/remind/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const orgId = req.user!.orgId
    const record = await prisma.trainingRecord.findFirst({
      where: { id: req.params.recordId, orgId },
      include: { employee: { select: { id: true, name: true, department: true, phone: true } } },
    })
    if (!record) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '培训记录不存在' } })
    }
    if (record.ackStatus !== 'PENDING') {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '仅待签收记录可催办' } })
    }
    // 记录催办通知日志
    await prisma.notificationLog.create({
      data: {
        orgId,
        type: 'TRAINING_REMIND',
        title: `培训签收催办：${record.topic}`,
        content: `员工 ${record.employee.name}（${record.employee.department}）的培训记录「${record.topic}」尚未签收，请尽快完成签收。`,
        channel: 'SYSTEM',
        status: 'SENT',
      },
    })
    res.json({ success: true, data: { message: `已催办 ${record.employee.name} 签收「${record.topic}」` } })
  } catch (err) { next(err) }
})

// 绩效记录列表（全员）
router.get('/performance/list', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const orgId = req.user!.orgId
    const page = parseInt(req.query.page as string) || 1
    const pageSize = parseInt(req.query.pageSize as string) || 20
    const keyword = (req.query.keyword as string) || ''
    const where: any = { orgId }
    if (keyword) {
      const employees = await prisma.employee.findMany({
        where: { orgId, name: { contains: keyword } },
        select: { id: true },
      })
      where.employeeId = { in: employees.map(e => e.id) }
    }
    const [rawRecords, total] = await Promise.all([
      prisma.performanceRecord.findMany({
        where,
        include: { employee: { select: { id: true, name: true, department: true, idCardNumber: true } } },
        orderBy: { period: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      prisma.performanceRecord.count({ where }),
    ])
    const records = rawRecords.map((r: any) => ({
      ...r,
      employee: r.employee ? { ...r.employee, idCardNumber: safeDecryptStr(r.employee.idCardNumber) } : r.employee,
    }))
    res.json({ success: true, data: { records, total, page, pageSize } })
  } catch (err) { next(err) }
})

// 违纪记录列表（全员）
router.get('/disciplinary/list', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const orgId = req.user!.orgId
    const page = parseInt(req.query.page as string) || 1
    const pageSize = parseInt(req.query.pageSize as string) || 20
    const keyword = (req.query.keyword as string) || ''
    const where: any = { orgId }
    if (keyword) {
      const employees = await prisma.employee.findMany({
        where: { orgId, name: { contains: keyword } },
        select: { id: true },
      })
      where.employeeId = { in: employees.map(e => e.id) }
    }
    const [rawRecords, total] = await Promise.all([
      prisma.disciplinaryRecord.findMany({
        where,
        include: { employee: { select: { id: true, name: true, department: true, idCardNumber: true } } },
        orderBy: { violationDate: 'desc' },
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      prisma.disciplinaryRecord.count({ where }),
    ])
    const records = rawRecords.map((r: any) => ({
      ...r,
      employee: r.employee ? { ...r.employee, idCardNumber: safeDecryptStr(r.employee.idCardNumber) } : r.employee,
    }))
    res.json({ success: true, data: { records, total, page, pageSize } })
  } catch (err) { next(err) }
})

// ========== 违纪记录 CRUD ==========

router.get('/:employeeId/disciplinary', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const records = await prisma.disciplinaryRecord.findMany({
      where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
      orderBy: { violationDate: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

router.post('/:employeeId/disciplinary', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { violationDate, violationType, description, severity, action, actionDetail, deductionAmount, employeeAck, ackDate, ackMethod, witness, attachmentUrl } = req.body
    const record = await prisma.disciplinaryRecord.create({
      data: {
        orgId: req.user!.orgId,
        employeeId: req.params.employeeId,
        violationDate: new Date(violationDate),
        violationType,
        description,
        severity: severity || 'WARNING',
        action: action || 'ORAL_WARNING',
        actionDetail,
        deductionAmount: Number(deductionAmount) || 0,
        employeeAck: employeeAck || false,
        ackDate: ackDate ? new Date(ackDate) : null,
        ackMethod,
        witness,
        attachmentUrl,
        createdBy: req.user!.id,
      },
    })
    await auditLog(req, 'CREATE', 'DISCIPLINARY', record.id, { employeeId: req.params.employeeId, violationType, severity, action })
    await createEvidence({
      orgId: req.user!.orgId,
      category: 'DISCIPLINARY',
      refId: record.id,
      employeeId: req.params.employeeId,
      events: [{ action: '违纪记录创建', timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
      createdBy: req.user!.id,
    }).catch(() => {})
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

router.put('/:employeeId/disciplinary/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { violationDate, violationType, description, severity, action, actionDetail, deductionAmount, employeeAck, ackDate, ackMethod, witness, attachmentUrl } = req.body
    const record = await prisma.disciplinaryRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    const updated = await prisma.disciplinaryRecord.update({
      where: { id: req.params.recordId },
      data: {
        violationDate: violationDate ? new Date(violationDate) : undefined,
        violationType,
        description,
        severity,
        action,
        actionDetail,
        deductionAmount: deductionAmount !== undefined ? Number(deductionAmount) || 0 : undefined,
        employeeAck,
        ackDate: ackDate ? new Date(ackDate) : null,
        ackMethod,
        witness,
        attachmentUrl,
      },
    })
    res.json({ success: true, data: updated })
  } catch (err) { next(err) }
})

router.delete('/:employeeId/disciplinary/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const record = await prisma.disciplinaryRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    await prisma.disciplinaryRecord.delete({ where: { id: req.params.recordId } })
    res.json({ success: true })
  } catch (err) { next(err) }
})

// 违纪确认证明导出
router.get('/:employeeId/disciplinary/:recordId/certificate', authMiddleware, async (req: AuthRequest, res: Response, next) => {
  try {
    const record = await prisma.disciplinaryRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
      include: { employee: true },
    })
    if (!record) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    }
    const org = await prisma.organization.findUnique({ where: { id: req.user!.orgId } })
    const typeMap: Record<string, string> = { LATE: '迟到', ABSENT: '旷工', INSUBORDINATION: '不服从管理', MISCONDUCT: '违纪', VIOLATE_POLICY: '违反规章制度', OTHER: '其他' }
    const actionMap: Record<string, string> = { ORAL_WARNING: '口头警告', WRITTEN_WARNING: '书面警告', DEDUCTION: '扣款', DEMOTION: '降职', TERMINATION: '解除劳动合同' }
    const severityMap: Record<string, string> = { WARNING: '警告', SERIOUS: '严重', SEVERE: '重度' }

    let idCard = ''
    try { if (record.employee.idCardNumber) idCard = decrypt(record.employee.idCardNumber) } catch { idCard = record.employee.idCardNumber || '' }

    const content = `违纪确认证明

兹证明 ${record.employee.name}（证件号码：${idCard || '___'}）系我单位员工，于 ${record.violationDate.toISOString().slice(0, 10)} 发生以下违纪行为：

违纪类型：${typeMap[record.violationType] || record.violationType}
严重程度：${severityMap[record.severity] || record.severity}
违纪事实：${record.description}
处理结果：${actionMap[record.action] || record.action}${record.actionDetail ? `（${record.actionDetail}）` : ''}

${record.employeeAck ? `该员工已于 ${record.ackDate ? new Date(record.ackDate).toISOString().slice(0, 10) : '___'} 签字确认上述违纪事实及处理结果。${record.witness ? `见证人：${record.witness}。` : ''}` : '该员工尚未签字确认。'}

特此证明。

${org?.name || ''}
${new Date().toLocaleDateString('zh-CN')}`

    const blob = Buffer.from('\ufeff' + content, 'utf8')
    const certFileName = `${record.employee.name}_违纪确认证明.doc`
    const encodedCertName = encodeURIComponent(certFileName)
    const asciiCertFallback = `disciplinary_cert_${record.id.slice(-8)}.doc`
    res.setHeader('Content-Type', 'application/msword;charset=utf-8')
    res.setHeader('Content-Disposition', `attachment; filename="${asciiCertFallback}"; filename*=UTF-8''${encodedCertName}`)
    res.send(blob)
  } catch (err: any) {
    console.error('违纪确认证明导出失败:', err?.message || err)
    if (!res.headersSent) {
      res.status(500).json({ success: false, error: { code: 'EXPORT_FAILED', message: `导出失败：${err?.message || '服务器错误'}` } })
    }
  }
})

// ========== 考勤记录 CRUD ==========

router.get('/:employeeId/attendance', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const records = await prisma.attendanceRecord.findMany({
      where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
      orderBy: { date: 'desc' },
      take: 90,
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

router.post('/:employeeId/attendance', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { date, checkInTime, checkOutTime, status, lateMinutes, earlyMinutes, workHours, overtimeHours, remark } = req.body
    const record = await prisma.attendanceRecord.upsert({
      where: { employeeId_date: { employeeId: req.params.employeeId, date: new Date(date) } },
      create: {
        orgId: req.user!.orgId,
        employeeId: req.params.employeeId,
        date: new Date(date),
        checkInTime,
        checkOutTime,
        status: status || 'NORMAL',
        lateMinutes: lateMinutes || 0,
        earlyMinutes: earlyMinutes || 0,
        workHours: workHours || 0,
        overtimeHours: overtimeHours || 0,
        remark,
        createdBy: req.user!.id,
      },
      update: {
        checkInTime,
        checkOutTime,
        status,
        lateMinutes,
        earlyMinutes,
        workHours,
        overtimeHours,
        remark,
      },
    })
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

router.delete('/:employeeId/attendance/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const record = await prisma.attendanceRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    await prisma.attendanceRecord.delete({ where: { id: req.params.recordId } })
    res.json({ success: true })
  } catch (err) { next(err) }
})

// ========== 培训签收记录 CRUD ==========

router.get('/:employeeId/training', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const records = await prisma.trainingRecord.findMany({
      where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
      orderBy: { trainingDate: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

router.post('/:employeeId/training', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { trainingDate, topic, content, trainer, duration, ackStatus, ackDate, attachmentUrl, remark } = req.body
    const record = await prisma.trainingRecord.create({
      data: {
        orgId: req.user!.orgId,
        employeeId: req.params.employeeId,
        trainingDate: new Date(trainingDate),
        topic,
        content,
        trainer,
        duration: duration || 0,
        ackStatus: ackStatus || 'PENDING',
        ackDate: ackDate ? new Date(ackDate) : null,
        attachmentUrl,
        remark,
        createdBy: req.user!.id,
      },
    })
    await auditLog(req, 'CREATE', 'TRAINING', record.id, { employeeId: req.params.employeeId, topic, trainer, duration })
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

// 批量创建培训记录
router.post('/training/batch', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { employeeIds, trainingDate, topic, content, trainer, duration, remark } = req.body
    if (!employeeIds || !Array.isArray(employeeIds) || employeeIds.length === 0) {
      return res.json({ success: false, error: { code: 'VALIDATION_ERROR', message: '请至少选择一名员工' } })
    }
    const results = await Promise.all(employeeIds.map((empId: string) =>
      prisma.trainingRecord.create({
        data: {
          orgId: req.user!.orgId,
          employeeId: empId,
          trainingDate: new Date(trainingDate),
          topic,
          content,
          trainer,
          duration: duration || 0,
          ackStatus: 'PENDING',
          remark,
          createdBy: req.user!.id,
        },
      })
    ))
    res.json({ success: true, data: { count: results.length } })
  } catch (err) { next(err) }
})

router.put('/:employeeId/training/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { trainingDate, topic, content, trainer, duration, ackStatus, ackDate, attachmentUrl, remark } = req.body
    const record = await prisma.trainingRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    const updated = await prisma.trainingRecord.update({
      where: { id: req.params.recordId },
      data: {
        trainingDate: trainingDate ? new Date(trainingDate) : undefined,
        topic,
        content,
        trainer,
        duration,
        ackStatus,
        ackDate: ackDate ? new Date(ackDate) : null,
        attachmentUrl,
        remark,
      },
    })
    res.json({ success: true, data: updated })
  } catch (err) { next(err) }
})

router.delete('/:employeeId/training/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const record = await prisma.trainingRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    await prisma.trainingRecord.delete({ where: { id: req.params.recordId } })
    res.json({ success: true })
  } catch (err) { next(err) }
})

// ========== 绩效记录 CRUD ==========

router.get('/:employeeId/performance', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const records = await prisma.performanceRecord.findMany({
      where: { employeeId: req.params.employeeId, orgId: req.user!.orgId },
      orderBy: { period: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

router.post('/:employeeId/performance', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { period, periodType, score, grade, coefficient, result, summary, improvementPlan, employeeAck, ackDate, reviewer, templateId, dimensionScores } = req.body
    const record = await prisma.performanceRecord.upsert({
      where: { employeeId_period: { employeeId: req.params.employeeId, period } },
      create: {
        orgId: req.user!.orgId,
        employeeId: req.params.employeeId,
        period,
        periodType: periodType || 'MONTHLY',
        score: score || 0,
        grade: grade || 'B',
        coefficient: coefficient != null ? Number(coefficient) : 1.0,
        result: result || 'QUALIFIED',
        summary,
        improvementPlan,
        templateId: templateId || null,
        dimensionScores: dimensionScores || undefined,
        employeeAck: employeeAck || false,
        ackDate: ackDate ? new Date(ackDate) : null,
        reviewer,
        createdBy: req.user!.id,
      },
      update: {
        periodType,
        score,
        grade,
        coefficient: coefficient != null ? Number(coefficient) : undefined,
        result,
        summary,
        improvementPlan,
        templateId: templateId || null,
        dimensionScores: dimensionScores || undefined,
        employeeAck,
        ackDate: ackDate ? new Date(ackDate) : null,
        reviewer,
      },
    })
    await auditLog(req, 'CREATE', 'PERFORMANCE', record.id, { employeeId: req.params.employeeId, period, score, grade, reviewer })
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

router.put('/:employeeId/performance/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { period, periodType, score, grade, coefficient, result, summary, improvementPlan, employeeAck, ackDate, reviewer, templateId, dimensionScores } = req.body
    const record = await prisma.performanceRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    const updated = await prisma.performanceRecord.update({
      where: { id: req.params.recordId },
      data: {
        period,
        periodType,
        score,
        grade,
        coefficient: coefficient != null ? Number(coefficient) : undefined,
        result,
        summary,
        improvementPlan,
        templateId: templateId || null,
        dimensionScores: dimensionScores || undefined,
        employeeAck,
        ackDate: ackDate ? new Date(ackDate) : null,
        reviewer,
      },
    })
    res.json({ success: true, data: updated })
  } catch (err) { next(err) }
})

router.delete('/:employeeId/performance/:recordId', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const record = await prisma.performanceRecord.findFirst({
      where: { id: req.params.recordId, orgId: req.user!.orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '记录不存在' } })
    await prisma.performanceRecord.delete({ where: { id: req.params.recordId } })
    res.json({ success: true })
  } catch (err) { next(err) }
})

// ========== 绩效模板 CRUD ==========

// 获取模板列表
router.get('/performance/templates', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const templates = await prisma.performanceTemplate.findMany({
      where: { orgId: req.user!.orgId },
      orderBy: { createdAt: 'desc' },
    })
    res.json({ success: true, data: templates })
  } catch (err) { next(err) }
})

// 创建模板
router.post('/performance/templates', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { name, description, dimensions, gradeRules, isDefault } = req.body
    if (!name || !dimensions || !Array.isArray(dimensions)) {
      return res.json({ success: false, error: { code: 'VALIDATION_ERROR', message: '模板名称和考核维度为必填' } })
    }
    // 如果设为默认，先取消其他默认
    if (isDefault) {
      await prisma.performanceTemplate.updateMany({ where: { orgId: req.user!.orgId, isDefault: true }, data: { isDefault: false } })
    }
    const template = await prisma.performanceTemplate.create({
      data: {
        orgId: req.user!.orgId,
        name,
        description,
        dimensions,
        gradeRules: gradeRules || undefined,
        isDefault: isDefault || false,
        createdBy: req.user!.id,
      },
    })
    res.json({ success: true, data: template })
  } catch (err) { next(err) }
})

// 更新模板
router.put('/performance/templates/:id', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { name, description, dimensions, gradeRules, isDefault } = req.body
    const existing = await prisma.performanceTemplate.findFirst({ where: { id: req.params.id, orgId: req.user!.orgId } })
    if (!existing) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } })
    if (isDefault) {
      await prisma.performanceTemplate.updateMany({ where: { orgId: req.user!.orgId, isDefault: true, id: { not: req.params.id } }, data: { isDefault: false } })
    }
    const updated = await prisma.performanceTemplate.update({
      where: { id: req.params.id },
      data: { name, description, dimensions, gradeRules: gradeRules || undefined, isDefault },
    })
    res.json({ success: true, data: updated })
  } catch (err) { next(err) }
})

// 删除模板
router.delete('/performance/templates/:id', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const existing = await prisma.performanceTemplate.findFirst({ where: { id: req.params.id, orgId: req.user!.orgId } })
    if (!existing) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '模板不存在' } })
    await prisma.performanceTemplate.delete({ where: { id: req.params.id } })
    res.json({ success: true })
  } catch (err) { next(err) }
})

// ========== 调薪/调部门 API ==========

function dateToMonth(date: Date): string {
  const y = date.getFullYear()
  const m = String(date.getMonth() + 1).padStart(2, '0')
  return `${y}-${m}`
}

function prevMonth(month: string): string {
  const [y, m] = month.split('-').map(Number)
  const d = new Date(y, m - 2, 1)
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`
}

// 调薪
router.post('/:id/salary-change', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { newSalary, baseSalary, performanceSalary, effectiveMonth, reason } = req.body
    const employee = await prisma.employee.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
    })
    if (!employee) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
    }

    const oldSalary = safeDecrypt(employee.monthlySalary)
    const effMonth = effectiveMonth || dateToMonth(new Date())
    const prevEffMonth = prevMonth(effMonth)

    // 关闭之前有效记录
    await prisma.salaryChangeRecord.updateMany({
      where: { employeeId: req.params.id, endMonth: null },
      data: { endMonth: prevEffMonth },
    })

    // 创建新薪资记录
    const record = await prisma.salaryChangeRecord.create({
      data: {
        orgId: req.user!.orgId,
        employeeId: req.params.id,
        oldSalary,
        newSalary: Number(newSalary),
        effectiveDate: new Date(`${effMonth}-01`),
        effectiveMonth: effMonth,
        endMonth: null,
        changeType: 'SALARY_CHANGE',
        reason: reason || null,
        createdBy: req.user!.id,
      },
    })

    // 同步 Employee 便捷字段（含工资结构）
    // 如果同时传了 baseSalary 和 performanceSalary，自动计算 monthlySalary = base + perf
    const finalSalary = (baseSalary !== undefined && performanceSalary !== undefined)
      ? (Number(baseSalary) || 0) + (Number(performanceSalary) || 0)
      : newSalary
    const updateData: any = { monthlySalary: encrypt(String(finalSalary)) }
    if (baseSalary !== undefined) updateData.baseSalary = encrypt(String(Number(baseSalary) || 0))
    if (performanceSalary !== undefined) updateData.performanceSalary = encrypt(String(Number(performanceSalary) || 0))
    await prisma.employee.update({
      where: { id: req.params.id },
      data: updateData,
    })

    await auditLog(req, 'CREATE', 'SALARY_CHANGE', record.id, { employeeId: req.params.id, oldSalary, newSalary })
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

// 调薪历史
router.get('/:id/salary-records', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const records = await prisma.salaryChangeRecord.findMany({
      where: { employeeId: req.params.id, orgId: req.user!.orgId },
      orderBy: { effectiveDate: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

// 调动（部门+职务变动）
router.post('/:id/department-change', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { newDepartment, newPosition, departmentId, effectiveMonth, reason } = req.body
    const employee = await prisma.employee.findFirst({
      where: { id: req.params.id, orgId: req.user!.orgId },
    })
    if (!employee) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
    }

    const oldDepartment = employee.department
    const oldPosition = employee.position || null
    const effMonth = effectiveMonth || dateToMonth(new Date())
    const prevEffMonth = prevMonth(effMonth)

    // 校验 departmentId 是否属于当前组织
    let deptName = newDepartment
    if (departmentId) {
      const dept = await prisma.department.findFirst({ where: { id: departmentId, orgId: req.user!.orgId } })
      if (!dept) {
        return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', message: '目标部门不存在' } })
      }
      deptName = dept.name
    }

    // 关闭之前有效记录
    await prisma.employeeDepartmentRecord.updateMany({
      where: { employeeId: req.params.id, endMonth: null },
      data: { endMonth: prevEffMonth },
    })

    // 创建新调动记录
    const record = await prisma.employeeDepartmentRecord.create({
      data: {
        orgId: req.user!.orgId,
        employeeId: req.params.id,
        oldDepartment,
        newDepartment: deptName,
        oldPosition,
        newPosition: newPosition || null,
        effectiveMonth: effMonth,
        endMonth: null,
        changeType: 'TRANSFER',
        reason: reason || null,
        createdBy: req.user!.id,
      },
    })

    // 同步 Employee 字段（department 文本 + departmentId 关联 + position 职务）
    await prisma.employee.update({
      where: { id: req.params.id },
      data: {
        department: deptName,
        departmentId: departmentId || null,
        position: newPosition || employee.position,
      },
    })

    await auditLog(req, 'CREATE', 'DEPARTMENT_CHANGE', record.id, { employeeId: req.params.id, oldDepartment, newDepartment: deptName, oldPosition, newPosition })
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

// 调部门历史
router.get('/:id/department-records', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const records = await prisma.employeeDepartmentRecord.findMany({
      where: { employeeId: req.params.id, orgId: req.user!.orgId },
      orderBy: { effectiveMonth: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

// 30天内合同到期列表
router.get('/contracts/expiring', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const days = parseInt(req.query.days as string) || 30
    const today = new Date()
    today.setHours(0, 0, 0, 0)
    const future = new Date(today)
    future.setDate(future.getDate() + days)

    const employees = await prisma.employee.findMany({
      where: { orgId: req.user!.orgId, status: 'ACTIVE' },
      include: {
        contracts: {
          where: {
            endDate: { gte: today, lte: future },
            contractType: 'FIXED',
          },
          orderBy: { endDate: 'asc' },
          take: 1,
        },
      },
    })

    const result = employees
      .filter(e => e.contracts.length > 0)
      .map(e => {
        const contract = e.contracts[0]
        const endDate = new Date(contract.endDate!)
        const daysLeft = Math.ceil((endDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24))
        return {
          employeeId: e.id,
          employeeName: e.name,
          department: e.department,
          contractEndDate: contract.endDate,
          daysLeft,
        }
      })
      .sort((a, b) => a.daysLeft - b.daysLeft)

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

// 合同类型列表（供前端动态获取）
router.get('/contract-types', authMiddleware, (_req: AuthRequest, res) => {
  const types = [
    { value: 'FIXED', label: '劳动合同-固定期', hasEndDate: true },
    { value: 'UNFIXED', label: '劳动合同-无固定期', hasEndDate: false },
    { value: 'LABOR', label: '劳务协议', hasEndDate: true },
    { value: 'INTERNSHIP', label: '实习协议', hasEndDate: true },
    { value: 'DISPATCH', label: '劳务派遣', hasEndDate: true },
    { value: 'OUTSOURCING', label: '业务外包', hasEndDate: true },
    { value: 'PARTTIME', label: '兼职协议', hasEndDate: true },
    { value: 'UNSIGNED', label: '未签合同', hasEndDate: false },
  ]
  res.json({ success: true, data: types })
})

export default router
