import { Router, Request, Response, NextFunction } from 'express'
import bcrypt from 'bcryptjs'
import multer from 'multer'
import path from 'path'
import fs from 'fs'
import jwt from 'jsonwebtoken'
import prisma from '../lib/prisma'
import { signAccessToken, verifyAccessToken } from '../lib/jwt'
import { authMiddleware, AuthRequest } from '../middleware/auth'
import { portalLoginSchema, portalSendCodeSchema, portalVerifyCodeSchema, onboardingSchema, contractConfirmSchema, contractSendCodeSchema } from '../schemas/portal.schema'
import { setCode, getCode, deleteCode, updateCode, checkRateLimit } from '../lib/codeStore'
import { createEvidence, appendEvidence } from '../services/evidence.service'
import { encrypt, decrypt, sha256 } from '../lib/crypto'

const router = Router()

// 员工端认证中间件
function portalAuth(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ success: false, error: { code: 'UNAUTHORIZED', message: '未登录' } })
  }
  const token = authHeader.substring(7)
  try {
    const payload = verifyAccessToken(token)
    if (!payload || payload.role !== 'EMPLOYEE') {
      return res.status(401).json({ success: false, error: { code: 'UNAUTHORIZED', message: '无效的员工端 Token' } })
    }
    ;(req as any).employee = { id: payload.id, orgId: payload.orgId }
    next()
  } catch {
    return res.status(401).json({ success: false, error: { code: 'UNAUTHORIZED', message: 'Token 无效或已过期' } })
  }
}

// 密码登录
router.post('/login', async (req, res, next) => {
  try {
    const data = portalLoginSchema.parse(req.body)
    // 查找所有匹配手机号的在职员工（可能跨组织）
    const employees = await prisma.employee.findMany({
      where: { phone: data.phone, status: 'ACTIVE' },
      select: { id: true, name: true, department: true, orgId: true, passwordHash: true, idCardNumber: true, org: { select: { name: true } } },
    })
    if (employees.length === 0) {
      return res.status(400).json({ success: false, error: { code: 'AUTH_FAILED', message: '手机号或密码错误' } })
    }
    // 逐个校验密码，找到匹配的员工
    let matchedEmployee = null
    for (const emp of employees) {
      if (emp.passwordHash) {
        const valid = await bcrypt.compare(data.password, emp.passwordHash)
        if (valid) {
          matchedEmployee = emp
          break
        }
      }
    }
    if (!matchedEmployee) {
      return res.status(400).json({ success: false, error: { code: 'AUTH_FAILED', message: '手机号或密码错误' } })
    }
    const token = signAccessToken({ id: matchedEmployee.id, orgId: matchedEmployee.orgId, role: 'EMPLOYEE' })
    res.json({ success: true, data: { token, employee: { id: matchedEmployee.id, name: matchedEmployee.name, department: matchedEmployee.department, orgName: (matchedEmployee as any).org?.name || '', idCardNumber: (() => { try { return matchedEmployee.idCardNumber ? (matchedEmployee.idCardNumber.includes(':') ? decrypt(matchedEmployee.idCardNumber) : matchedEmployee.idCardNumber) : null } catch { return null } })() } } })
  } catch (err) {
    next(err)
  }
})

// 发送验证码（页面内显示）
router.post('/send-code', async (req, res, next) => {
  try {
    const data = portalSendCodeSchema.parse(req.body)
    const employee = await prisma.employee.findFirst({
      where: { phone: data.phone, status: 'ACTIVE' },
    })
    if (!employee) {
      return res.status(400).json({ success: false, error: { code: 'NOT_FOUND', message: '该手机号未在系统中登记' } })
    }
    // 频率限制：60秒内不可重复发送
    const allowed = await checkRateLimit(data.phone)
    if (!allowed) {
      return res.status(429).json({ success: false, error: { code: 'RATE_LIMIT', message: '验证码发送过于频繁，请60秒后重试' } })
    }
    const code = Math.random().toString().slice(2, 8)
    await setCode(data.phone, code)
    res.json({ success: true, data: { code, message: '验证码已生成（开发阶段直接返回，生产环境将发送短信）' } })
  } catch (err) {
    next(err)
  }
})

// 验证码登录
router.post('/verify-code', async (req, res, next) => {
  try {
    const data = portalVerifyCodeSchema.parse(req.body)
    const stored = await getCode(data.phone)
    if (!stored) {
      return res.status(400).json({ success: false, error: { code: 'CODE_EXPIRED', message: '验证码已过期，请重新获取' } })
    }
    // 错误次数限制：5次后锁定
    if (stored.failCount >= 5) {
      await deleteCode(data.phone)
      return res.status(400).json({ success: false, error: { code: 'TOO_MANY_ATTEMPTS', message: '验证码错误次数过多，请重新获取验证码' } })
    }
    if (stored.code !== data.code) {
      await updateCode(data.phone, { failCount: stored.failCount + 1 })
      return res.status(400).json({ success: false, error: { code: 'CODE_WRONG', message: `验证码错误（剩余${5 - stored.failCount - 1}次机会）` } })
    }
    await deleteCode(data.phone)
    // 查找所有匹配手机号的在职员工（可能跨组织）
    const employees = await prisma.employee.findMany({
      where: { phone: data.phone, status: 'ACTIVE' },
      select: { id: true, name: true, department: true, orgId: true, idCardNumber: true, org: { select: { name: true } } },
    })
    if (employees.length === 0) {
      return res.status(400).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
    }
    // 如果只有一个匹配，直接登录
    const employee = employees[0]
    const token = signAccessToken({ id: employee.id, orgId: employee.orgId, role: 'EMPLOYEE' })
    res.json({ success: true, data: { token, employee: { id: employee.id, name: employee.name, department: employee.department, orgName: (employee as any).org?.name || '', idCardNumber: (() => { try { return employee.idCardNumber ? (employee.idCardNumber.includes(':') ? decrypt(employee.idCardNumber) : employee.idCardNumber) : null } catch { return null } })() } } })
  } catch (err) {
    next(err)
  }
})

/**
 * 员工修改自己的密码
 * POST /portal/change-password  body: { oldPassword, newPassword }
 */
router.post('/change-password', portalAuth, async (req: any, res, next) => {
  try {
    const { oldPassword, newPassword } = req.body
    if (!oldPassword || !newPassword) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '请输入旧密码和新密码' } })
    }
    if (newPassword.length < 6) {
      return res.status(400).json({ success: false, error: { code: 'VALIDATION_ERROR', message: '新密码至少6位' } })
    }
    const employee = await prisma.employee.findFirst({
      where: { id: req.employee.id },
      select: { id: true, passwordHash: true },
    })
    if (!employee || !employee.passwordHash) {
      return res.status(400).json({ success: false, error: { code: 'AUTH_FAILED', message: '当前未设置密码，请联系管理员重置' } })
    }
    const valid = await bcrypt.compare(oldPassword, employee.passwordHash)
    if (!valid) {
      return res.status(400).json({ success: false, error: { code: 'AUTH_FAILED', message: '旧密码错误' } })
    }
    const passwordHash = await bcrypt.hash(newPassword, 10)
    await prisma.employee.update({ where: { id: employee.id }, data: { passwordHash } })
    res.json({ success: true, data: { message: '密码修改成功' } })
  } catch (err) {
    next(err)
  }
})

// 工资条
router.get('/payslip', portalAuth, async (req: any, res, next) => {
  try {
    const month = req.query.month as string || new Date().toISOString().slice(0, 7)
    const payslip = await prisma.payslip.findFirst({
      where: { employeeId: req.employee.id, orgId: req.employee.orgId, month, publishStatus: 'PUBLISHED' },
    })
    if (!payslip) {
      return res.json({ success: true, data: null })
    }
    // 记录查看时间
    if (!payslip.viewedAt) {
      await prisma.payslip.update({ where: { id: payslip.id }, data: { viewedAt: new Date() } })
    }
    res.json({ success: true, data: { ...payslip, viewedAt: payslip.viewedAt || new Date() } })
  } catch (err) {
    next(err)
  }
})

// 工资条历史（最近6个月）
router.get('/payslip/history', portalAuth, async (req: any, res, next) => {
  try {
    const payslips = await prisma.payslip.findMany({
      where: { employeeId: req.employee.id, orgId: req.employee.orgId, publishStatus: 'PUBLISHED' },
      orderBy: { month: 'desc' },
      take: 6,
    })
    res.json({ success: true, data: payslips })
  } catch (err) {
    next(err)
  }
})

// 工资条确认已阅
router.post('/payslip/:id/confirm', portalAuth, async (req: any, res, next) => {
  try {
    const payslip = await prisma.payslip.findFirst({
      where: { id: req.params.id, orgId: req.employee.orgId, employeeId: req.employee.id },
      include: { employee: true },
    })
    if (!payslip) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '工资条不存在' } })
    }
    await prisma.payslip.update({
      where: { id: req.params.id },
      data: { confirmedAt: new Date(), confirmedIp: req.ip },
    })
    // 通知 HR
    await prisma.notificationLog.create({
      data: {
        orgId: req.employee.orgId,
        title: '工资条确认通知',
        content: `员工 ${payslip.employee.name} 已确认 ${payslip.month} 月工资条（IP: ${req.ip}）`,
        type: 'PAYSLIP_CONFIRM',
        channel: 'IN_APP',
      },
    })
    await createEvidence({
      orgId: req.employee.orgId,
      category: 'PAYSLIP_CONFIRM',
      refId: payslip.id,
      employeeId: req.employee.id,
      events: [{ action: '工资条确认', timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
      createdBy: req.employee.id,
    }).catch(() => {})

    // 如果开启了工资条电子签，创建电子签记录
    const org = await prisma.organization.findUnique({ where: { id: req.employee.orgId }, select: { esignPayslipEnabled: true } })
    if (org?.esignPayslipEnabled) {
      await prisma.eSignRecord.create({
        data: {
          orgId: req.employee.orgId,
          employeeId: req.employee.id,
          scene: 'PAYSLIP',
          documentTitle: `工资条确认：${payslip.month}`,
          status: 'PENDING',
          initiatedBy: req.employee.id,
          createdBy: req.employee.id,
          remark: '工资条确认时自动发起',
          expiredAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        },
      })
    }

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

// 我的合同
router.get('/contract', portalAuth, async (req: any, res, next) => {
  try {
    const contract = await prisma.laborContract.findFirst({
      where: { employeeId: req.employee.id, orgId: req.employee.orgId },
      orderBy: { createdAt: 'desc' },
    })
    if (!contract) {
      return res.json({ success: true, data: null })
    }
    // 补充员工工资结构（合同中 baseSalary/performanceSalary 为 0 时作为 fallback）
    const employee = await prisma.employee.findFirst({
      where: { id: req.employee.id },
      select: { monthlySalary: true, baseSalary: true, performanceSalary: true },
    })
    const empBase = employee?.baseSalary ? Number(decrypt(employee.baseSalary)) || 0 : 0
    const empPerf = employee?.performanceSalary ? Number(decrypt(employee.performanceSalary)) || 0 : 0
    const empMonthly = employee?.monthlySalary ? Number(decrypt(employee.monthlySalary)) || 0 : 0
    res.json({
      success: true,
      data: {
        ...contract,
        // 合同中工资为 0 时用员工档案的工资补齐
        baseSalary: contract.baseSalary || empBase,
        performanceSalary: contract.performanceSalary || empPerf,
        monthlySalary: empMonthly,
      },
    })
  } catch (err) {
    next(err)
  }
})

// 入职填报提交
router.post('/onboarding', async (req, res, next) => {
  try {
    const data = onboardingSchema.parse(req.body)
    const link = await prisma.onboardingLink.findFirst({
      where: { token: data.token, status: 'PENDING', expiresAt: { gt: new Date() } },
    })
    if (!link) {
      return res.status(400).json({ success: false, error: { code: 'LINK_INVALID', message: '链接无效或已过期' } })
    }
    await prisma.onboardingLink.update({
      where: { id: link.id },
      data: {
        employeeName: data.name,
        phone: data.phone,
        formData: {
          name: data.name,
          phone: data.phone,
          idCard: data.idCard,
          emergencyContact: data.emergencyContact,
          emergencyPhone: data.emergencyPhone,
          address: data.address,
          bankCard: data.bankCard,
          bankName: data.bankName,
        },
        status: 'APPROVED',
        usedAt: new Date(),
      },
    })
    res.json({ success: true, data: { message: '信息提交成功，HR 将审核您的信息' } })
  } catch (err) {
    next(err)
  }
})

// 合同签署验证码发送
router.post('/contract-confirm/send-code', async (req, res, next) => {
  try {
    const data = contractSendCodeSchema.parse(req.body)
    const link = await prisma.contractConfirmLink.findFirst({
      where: { token: data.token, status: 'UNCONFIRMED', expiresAt: { gt: new Date() } },
      include: { contract: { include: { employee: true } } },
    })
    if (!link) {
      return res.status(400).json({ success: false, error: { code: 'LINK_INVALID', message: '链接无效或已过期' } })
    }
    const phone = link.contract.employee.phone
    if (!phone) {
      return res.status(400).json({ success: false, error: { code: 'NO_PHONE', message: '员工手机号未登记，无法发送验证码' } })
    }
    const code = Math.random().toString().slice(2, 8)
    await setCode(`contract-${data.token}`, code)
    res.json({ success: true, data: { code, message: '验证码已生成（开发阶段直接返回，生产环境将发送短信）' } })
  } catch (err) {
    next(err)
  }
})

// 合同签署确认
router.post('/contract-confirm', async (req, res, next) => {
  try {
    const data = contractConfirmSchema.parse(req.body)
    const link = await prisma.contractConfirmLink.findFirst({
      where: { token: data.token, status: 'UNCONFIRMED', expiresAt: { gt: new Date() } },
      include: { contract: { include: { employee: true } } },
    })
    if (!link) {
      return res.status(400).json({ success: false, error: { code: 'LINK_INVALID', message: '链接无效或已过期' } })
    }
    // 验证码校验
    const stored = await getCode(`contract-${data.token}`)
    if (!stored) {
      return res.status(400).json({ success: false, error: { code: 'CODE_EXPIRED', message: '验证码已过期，请重新获取' } })
    }
    if (stored.failCount >= 5) {
      await deleteCode(`contract-${data.token}`)
      return res.status(400).json({ success: false, error: { code: 'TOO_MANY_ATTEMPTS', message: '验证码错误次数过多，请重新获取验证码' } })
    }
    if (stored.code !== data.verifyCode) {
      await updateCode(`contract-${data.token}`, { failCount: stored.failCount + 1 })
      return res.status(400).json({ success: false, error: { code: 'CODE_WRONG', message: `验证码错误（剩余${5 - stored.failCount - 1}次机会）` } })
    }
    await deleteCode(`contract-${data.token}`)

    const userAgent = req.headers['user-agent'] || ''
    const signEvidence = JSON.stringify({
      ip: req.ip,
      userAgent,
      timestamp: new Date().toISOString(),
    })
    await prisma.contractConfirmLink.update({
      where: { id: link.id },
      data: { status: 'CONFIRMED', confirmedAt: new Date(), confirmedIp: req.ip },
    })
    await prisma.laborContract.update({
      where: { id: link.contractId },
      data: { attachmentName: `confirmed:${new Date().toISOString()}|evidence:${signEvidence}` },
    })
    await createEvidence({
      orgId: link.contract.orgId,
      category: 'CONTRACT_SIGN',
      refId: link.contractId,
      employeeId: link.contract.employeeId,
      events: [{ action: '合同签署确认', timestamp: new Date().toISOString(), ip: req.ip, userAgent, smsCode: data.verifyCode }],
      createdBy: link.contract.employeeId,
    }).catch(() => {})
    res.json({ success: true, data: { message: '合同签署确认成功' } })
  } catch (err) {
    next(err)
  }
})

// 获取入职填报信息（通过 token）
router.get('/onboarding/:token', async (req, res, next) => {
  try {
    const link = await prisma.onboardingLink.findFirst({
      where: { token: req.params.token, status: 'PENDING', expiresAt: { gt: new Date() } },
      include: { org: { select: { name: true } } },
    })
    if (!link) {
      return res.status(400).json({ success: false, error: { code: 'LINK_INVALID', message: '链接无效或已过期' } })
    }
    res.json({ success: true, data: { orgName: link.org.name } })
  } catch (err) {
    next(err)
  }
})

// 撤回入职链接（HR 端调用，需要认证）
router.post('/onboarding/:id/revoke', portalAuth, async (req: any, res, next) => {
  try {
    const link = await prisma.onboardingLink.findFirst({
      where: { id: req.params.id, orgId: req.employee.orgId },
    })
    if (!link) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '链接不存在' } })
    }
    if (link.status !== 'PENDING') {
      return res.status(400).json({ success: false, error: { code: 'INVALID_STATUS', message: '仅待填报状态的链接可撤回' } })
    }
    await prisma.onboardingLink.update({
      where: { id: link.id },
      data: { status: 'CANCELLED' },
    })
    res.json({ success: true, data: { message: '入职链接已撤回' } })
  } catch (err) {
    next(err)
  }
})

// 获取合同确认信息（通过 token）
router.get('/contract-confirm/:token', async (req, res, next) => {
  try {
    const link = await prisma.contractConfirmLink.findFirst({
      where: { token: req.params.token, status: 'UNCONFIRMED', expiresAt: { gt: new Date() } },
      include: {
        contract: {
          include: {
            employee: { select: { name: true, idCardNumber: true, org: { select: { name: true } } } },
          },
        },
      },
    })
    if (!link) {
      return res.status(400).json({ success: false, error: { code: 'LINK_INVALID', message: '链接无效或已过期' } })
    }
    res.json({
      success: true,
      data: {
        orgName: link.contract.employee.org.name,
        employeeName: link.contract.employee.name,
        employeeIdCardNumber: (() => { try { const v = link.contract.employee.idCardNumber; return v ? (v.includes(':') ? decrypt(v) : v) : null } catch { return null } })(),
        contract: link.contract,
      },
    })
  } catch (err) {
    next(err)
  }
})

// 重发合同确认链接（HR 端调用，需要认证）
router.post('/contract-confirm/:id/resend', portalAuth, async (req: any, res, next) => {
  try {
    const link = await prisma.contractConfirmLink.findFirst({
      where: { id: req.params.id, orgId: req.employee.orgId },
      include: { contract: { include: { employee: true } } },
    })
    if (!link) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '链接不存在' } })
    }
    if (link.status === 'CONFIRMED') {
      return res.status(400).json({ success: false, error: { code: 'ALREADY_CONFIRMED', message: '合同已确认，无需重发' } })
    }
    // 生成新 token 并延长过期时间
    const crypto = await import('crypto')
    const newToken = crypto.randomUUID()
    await prisma.contractConfirmLink.update({
      where: { id: link.id },
      data: {
        token: newToken,
        expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        status: 'UNCONFIRMED',
      },
    })
    res.json({ success: true, data: { token: newToken, message: '确认链接已重发，有效期7天' } })
  } catch (err) {
    next(err)
  }
})

// 入职文件上传
const uploadDir = path.join(process.cwd(), 'uploads', 'onboarding')
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true })

const onboardingUpload = multer({
  storage: multer.diskStorage({
    destination: uploadDir,
    filename: (_req, file, cb) => {
      const ext = path.extname(file.originalname)
      cb(null, `${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`)
    },
  }),
  limits: { fileSize: 10 * 1024 * 1024 },
  fileFilter: (_req, file, cb) => {
    const allowed = ['.jpg', '.jpeg', '.png', '.pdf', '.bmp']
    const ext = path.extname(file.originalname).toLowerCase()
    if (allowed.includes(ext)) cb(null, true)
    else cb(new Error('仅支持 JPG/PNG/PDF/BMP 格式'))
  },
})

router.post('/onboarding/:token/upload', onboardingUpload.single('file'), async (req, res, next) => {
  try {
    if (!req.file) {
      return res.status(400).json({ success: false, error: { code: 'NO_FILE', message: '请选择文件' } })
    }
    const link = await prisma.onboardingLink.findFirst({
      where: { token: req.params.token, status: 'PENDING', expiresAt: { gt: new Date() } },
    })
    if (!link) {
      fs.unlinkSync(req.file.path)
      return res.status(400).json({ success: false, error: { code: 'LINK_INVALID', message: '链接无效或已过期' } })
    }
    const fileType = (req.body.fileType as string) || 'OTHER'
    const fileUrl = `/uploads/onboarding/${req.file.filename}`
    res.json({ success: true, data: { fileName: req.file.originalname, fileUrl, fileType, fileSize: req.file.size } })
  } catch (err) {
    next(err)
  }
})

// ========== 员工端：规章制度公示 ==========

/** 获取已公示制度列表（含当前员工阅读状态） */
router.get('/policies', portalAuth, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { orgId, id: employeeId } = (req as any).employee
    const policies = await prisma.policyDocument.findMany({
      where: { orgId, status: 'PUBLISHED' },
      orderBy: { publishedAt: 'desc' },
      select: {
        id: true,
        title: true,
        type: true,
        publishedAt: true,
        readRecords: {
          where: { employeeId },
          select: { id: true, readAt: true },
        },
      },
    })
    const result = policies.map(p => ({
      id: p.id,
      title: p.title,
      type: p.type,
      publishedAt: p.publishedAt?.toISOString() || null,
      hasRead: p.readRecords.length > 0,
      readAt: p.readRecords[0]?.readAt?.toISOString() || null,
    }))
    res.json({ success: true, data: result })
  } catch (err) {
    next(err)
  }
})

/** 获取制度详情（已公示） */
router.get('/policies/:id', portalAuth, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { orgId, id: employeeId } = (req as any).employee
    const policy = await prisma.policyDocument.findFirst({
      where: { id: req.params.id, orgId, status: 'PUBLISHED' },
      select: {
        id: true,
        title: true,
        content: true,
        type: true,
        publishedAt: true,
        readRecords: {
          where: { employeeId },
          select: { id: true, readAt: true },
        },
      },
    })
    if (!policy) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '制度不存在或未公示' } })
    }
    res.json({
      success: true,
      data: {
        id: policy.id,
        title: policy.title,
        content: policy.content,
        type: policy.type,
        publishedAt: policy.publishedAt?.toISOString() || null,
        hasRead: policy.readRecords.length > 0,
        readAt: policy.readRecords[0]?.readAt?.toISOString() || null,
      },
    })
  } catch (err) {
    next(err)
  }
})

/** 提交阅读确认（签收） */
router.post('/policies/:id/read', portalAuth, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const { orgId, id: employeeId } = (req as any).employee
    const policy = await prisma.policyDocument.findFirst({
      where: { id: req.params.id, orgId, status: 'PUBLISHED' },
      select: { id: true },
    })
    if (!policy) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '制度不存在或未公示' } })
    }

    // 幂等：已存在则返回已有记录
    const existing = await prisma.policyReadRecord.findUnique({
      where: { policyId_employeeId: { policyId: req.params.id, employeeId } },
    })
    if (existing) {
      return res.json({ success: true, data: { readAt: existing.readAt.toISOString() } })
    }

    const record = await prisma.policyReadRecord.create({
      data: {
        policyId: req.params.id,
        orgId,
        employeeId,
        ip: req.ip || req.socket.remoteAddress,
        userAgent: req.headers['user-agent'] || null,
      },
    })

    // 如果开启了制度电子签，创建电子签记录
    const org = await prisma.organization.findUnique({ where: { id: orgId }, select: { esignPolicyEnabled: true } })
    if (org?.esignPolicyEnabled) {
      const policyDoc = await prisma.policyDocument.findUnique({ where: { id: req.params.id }, select: { title: true, content: true } })
      await prisma.eSignRecord.create({
        data: {
          orgId,
          employeeId,
          scene: 'POLICY',
          documentTitle: `制度签收：${policyDoc?.title || '未知'}`,
          documentContent: policyDoc?.content || null,
          status: 'PENDING',
          initiatedBy: employeeId,
          createdBy: employeeId,
          remark: '制度阅读确认后自动发起',
          expiredAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
        },
      })
    }

    res.json({ success: true, data: { readAt: record.readAt.toISOString() } })
  } catch (err) {
    next(err)
  }
})

// ========== 一次性自动登录 ==========

const AUTO_LOGIN_SECRET = process.env.JWT_SECRET || 'dev-secret'

/**
 * 管理端生成员工一次性自动登录 token（10 分钟有效）
 * POST /portal/auto-login-token  body: { employeeId }
 */
router.post('/auto-login-token', authMiddleware, async (req: AuthRequest, res, next) => {
  try {
    const { employeeId } = req.body
    if (!employeeId) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '缺少 employeeId' } })
    }
    const employee = await prisma.employee.findFirst({
      where: { id: employeeId, orgId: req.user!.orgId, status: 'ACTIVE' },
      select: { id: true, name: true, phone: true, orgId: true },
    })
    if (!employee) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在或已离职' } })
    }
    const token = jwt.sign(
      { id: employee.id, orgId: employee.orgId, role: 'EMPLOYEE_AUTO', name: employee.name },
      AUTO_LOGIN_SECRET,
      { expiresIn: '10m' }
    )
    res.json({ success: true, data: { token, employeeName: employee.name, phone: employee.phone } })
  } catch (err) {
    next(err)
  }
})

/**
 * 员工端自动登录（消费一次性 token）
 * GET /portal/auto-login?token=xxx
 */
router.get('/auto-login', async (req, res, next) => {
  try {
    const { token } = req.query
    if (!token || typeof token !== 'string') {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '缺少 token' } })
    }
    let payload: any
    try {
      payload = jwt.verify(token, AUTO_LOGIN_SECRET)
    } catch {
      return res.status(401).json({ success: false, error: { code: 'TOKEN_EXPIRED', message: '链接已过期，请重新扫码' } })
    }
    if (payload.role !== 'EMPLOYEE_AUTO') {
      return res.status(401).json({ success: false, error: { code: 'TOKEN_INVALID', message: '无效的登录链接' } })
    }
    const employee = await prisma.employee.findFirst({
      where: { id: payload.id, orgId: payload.orgId, status: 'ACTIVE' },
      select: { id: true, name: true, department: true, orgId: true, idCardNumber: true, org: { select: { name: true } } },
    })
    if (!employee) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在或已离职' } })
    }
    const accessToken = signAccessToken({ id: employee.id, orgId: employee.orgId, role: 'EMPLOYEE' })
    res.json({ success: true, data: { token: accessToken, employee: { id: employee.id, name: employee.name, department: employee.department, orgName: (employee as any).org?.name || '', idCardNumber: (() => { try { return employee.idCardNumber ? (employee.idCardNumber.includes(':') ? decrypt(employee.idCardNumber) : employee.idCardNumber) : null } catch { return null } })() } } })
  } catch (err) {
    next(err)
  }
})

// 员工端查看自己的月度考勤
router.get('/attendance', portalAuth, async (req: any, res, next) => {
  try {
    const month = req.query.month as string || new Date().toISOString().slice(0, 7)
    // 检查该月份是否已发布
    const publish = await (prisma as any).attendancePublish.findFirst({
      where: { orgId: req.employee.orgId, month, status: 'PUBLISHED' },
    })
    if (!publish) {
      return res.json({ success: true, data: { published: false, records: [] } })
    }
    // 查询该月考勤记录
    const startDate = new Date(`${month}-01`)
    const endDate = new Date(startDate)
    endDate.setMonth(endDate.getMonth() + 1)
    const records = await prisma.attendanceRecord.findMany({
      where: {
        employeeId: req.employee.id,
        orgId: req.employee.orgId,
        date: { gte: startDate, lt: endDate },
      },
      orderBy: { date: 'asc' },
    })

    // 查询节假日配置，判断每日日期类型和加班费率
    const holidays = await prisma.holidayConfig.findMany({
      where: { orgId: req.employee.orgId, date: { gte: startDate, lt: endDate } },
    })
    const holidayMap = new Map<string, string>() // dateStr -> type
    for (const h of holidays) {
      holidayMap.set(h.date.toISOString().slice(0, 10), h.type)
    }

    // 获取加班费配置
    const otConfig = await prisma.overtimeConfig.findUnique({ where: { orgId: req.employee.orgId } })
    const rates = {
      weekday: otConfig?.weekdayRate ?? 1.5,
      weekend: otConfig?.weekendRate ?? 2.0,
      holiday: otConfig?.holidayRate ?? 3.0,
    }

    // 为每条记录附加日期类型和加班费率
    const enrichedRecords = records.map(r => {
      const dateStr = r.date.toISOString().slice(0, 10)
      const day = new Date(dateStr + 'T00:00:00')
      const dayOfWeek = day.getDay()
      const holidayType = holidayMap.get(dateStr)

      let dateType: 'weekday' | 'weekend' | 'holiday' = 'weekday'
      let overtimeRate = rates.weekday

      if (holidayType === 'HOLIDAY') {
        dateType = 'holiday'
        overtimeRate = rates.holiday
      } else if (holidayType === 'WORKDAY') {
        dateType = 'weekday'
        overtimeRate = rates.weekday
      } else if (dayOfWeek === 0 || dayOfWeek === 6) {
        dateType = 'weekend'
        overtimeRate = rates.weekend
      }

      return {
        ...r,
        dateType,
        overtimeRate,
        hasOvertime: (r.overtimeHours || 0) > 0,
      }
    })

    res.json({ success: true, data: { published: true, records: enrichedRecords, title: publish.title, overtimeRates: rates } })
  } catch (err) {
    next(err)
  }
})

// ========== 员工端：首页概览 ==========
router.get('/home/overview', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const employee = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
    if (!employee) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })

    let employeeIdCardNumber: string | null = null
    try { if (employee.idCardNumber) employeeIdCardNumber = employee.idCardNumber.includes(':') ? decrypt(employee.idCardNumber) : employee.idCardNumber } catch {}

    // 最新工资条
    const latestPayslip = await prisma.payslip.findFirst({
      where: { employeeId, orgId, publishStatus: 'PUBLISHED' },
      orderBy: { month: 'desc' },
      select: { month: true, totalPay: true, netPay: true },
    })

    // 合同信息
    const contract = await prisma.laborContract.findFirst({
      where: { employeeId, orgId },
      orderBy: { createdAt: 'desc' },
      select: { contractType: true, startDate: true, endDate: true },
    })
    const typeLabels: Record<string, string> = { FIXED: '劳动合同-固定期', UNFIXED: '劳动合同-无固定期', LABOR: '劳务协议', INTERNSHIP: '实习协议', DISPATCH: '劳务派遣', OUTSOURCING: '业务外包', PARTTIME: '兼职协议', UNSIGNED: '未签合同' }
    let daysToExpire: number | null = null
    if (contract?.endDate) {
      const diff = Math.ceil((new Date(contract.endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24))
      daysToExpire = diff
    }

    // 本月考勤概览
    const month = new Date().toISOString().slice(0, 7)
    const startDate = new Date(`${month}-01`)
    const endDate = new Date(startDate)
    endDate.setMonth(endDate.getMonth() + 1)
    const attendanceRecords = await prisma.attendanceRecord.findMany({
      where: { employeeId, orgId, date: { gte: startDate, lt: endDate } },
    })
    const attendanceSummary = {
      normalDays: attendanceRecords.filter((r: any) => r.status === 'NORMAL').length,
      lateCount: attendanceRecords.filter((r: any) => r.status === 'LATE').length,
      leaveDays: attendanceRecords.filter((r: any) => r.status === 'LEAVE').length,
      absentDays: attendanceRecords.filter((r: any) => r.status === 'ABSENT').length,
    }

    // 待办事项
    const pendingTasks: any[] = []
    if (contract && daysToExpire !== null && daysToExpire < 30 && daysToExpire >= 0) {
      pendingTasks.push({ severity: 'high', message: `合同将在 ${daysToExpire} 天后到期，请联系HR确认续签事宜` })
    }
    if (contract && daysToExpire !== null && daysToExpire < 0) {
      pendingTasks.push({ severity: 'high', message: '合同已到期，请尽快联系HR办理续签或离职手续' })
    }
    // 查找未阅读的制度：取所有制度ID，排除已阅读的
    const allPolicies = await prisma.policyDocument.findMany({ where: { orgId }, select: { id: true } })
    const readRecords = await prisma.policyReadRecord.findMany({
      where: { employeeId, orgId },
      select: { policyId: true },
    })
    const readPolicyIds = new Set(readRecords.map(r => r.policyId))
    const unreadPolicies = allPolicies.filter(p => !readPolicyIds.has(p.id))
    if (unreadPolicies.length > 0) {
      pendingTasks.push({ severity: 'medium', message: `您有 ${unreadPolicies.length} 份制度待阅读确认` })
    }
    // 待签署文件
    const pendingEsign = await prisma.eSignRecord.findMany({
      where: { employeeId, orgId, status: 'PENDING' },
      select: { id: true, documentTitle: true },
    })
    if (pendingEsign.length > 0) {
      pendingTasks.push({ severity: 'high', message: `您有 ${pendingEsign.length} 份文件待签署（${pendingEsign.map(e => e.documentTitle).join('、')}）` })
    }

    res.json({
      success: true,
      data: {
        employeeName: employee.name,
        employeeIdCardNumber,
        latestPayslip,
        contract: contract ? {
          typeLabel: typeLabels[contract.contractType] || contract.contractType,
          startDate: contract.startDate?.toISOString().slice(0, 10),
          endDate: contract.endDate?.toISOString().slice(0, 10),
          daysToExpire,
        } : null,
        attendance: attendanceSummary,
        pendingTasks,
        announcements: [],
      },
    })
  } catch (err) { next(err) }
})

// ========== 员工端：入职进度 ==========
router.get('/onboarding/progress', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const employee = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
    if (!employee) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })

    const contract = await prisma.laborContract.findFirst({ where: { employeeId, orgId } })
    const files = await prisma.employeeAttachment.findMany({ where: { employeeId, orgId } })

    const steps: any = {
      profile: {
        completed: !!(employee.name && employee.idCardNumber && employee.phone),
        description: employee.name ? '基本信息已填写' : '请完善基本信息',
        completedAt: employee.hireDate,
      },
      documents: {
        completed: files.length > 0,
        description: files.length > 0 ? `已上传 ${files.length} 份材料` : '请上传入职材料',
      },
      contract: {
        completed: !!contract,
        description: contract ? '合同已签署' : '等待合同签署',
        completedAt: contract?.createdAt,
      },
      bankcard: {
        completed: !!(employee as any).bankCard,
        description: (employee as any).bankCard ? '银行卡已登记' : '请登记银行卡信息',
      },
      complete: {
        completed: employee.status === 'ACTIVE',
        description: employee.status === 'ACTIVE' ? '入职流程已完成' : '入职流程进行中',
      },
    }

    const completedCount = Object.values(steps).filter((s: any) => s.completed).length
    const completionRate = Math.round((completedCount / 5) * 100)
    const currentStepIndex = Object.values(steps).findIndex((s: any) => !s.completed)

    const pendingItems: any[] = []
    Object.entries(steps).forEach(([key, s]: any) => {
      if (!s.completed) pendingItems.push({ message: s.description })
    })

    res.json({ success: true, data: { steps, completionRate, currentStepIndex, pendingItems } })
  } catch (err) { next(err) }
})

// ========== 员工端：个人资料查看与编辑 ==========
// 查看个人资料
router.get('/profile', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const emp = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
    if (!emp) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })

    // 解密敏感字段
    let idCardNo = ''
    let bankAccount = ''
    try { if (emp.idCardNumber) idCardNo = decrypt(emp.idCardNumber) } catch {}
    try { if (emp.bankAccount) bankAccount = decrypt(emp.bankAccount) } catch {}

    // 从身份证号解析性别和出生日期（数据库字段为空时）
    let gender = emp.gender || ''
    let birthDate = emp.birthDate ? emp.birthDate.toISOString().slice(0, 10) : ''
    if (idCardNo && idCardNo.length === 18) {
      // 第17位：奇数=男，偶数=女
      if (!gender) {
        const genderDigit = parseInt(idCardNo[16], 10)
        gender = genderDigit % 2 === 1 ? '男' : '女'
      }
      // 第7-14位：出生日期 YYYYMMDD
      if (!birthDate) {
        const y = idCardNo.slice(6, 10)
        const m = idCardNo.slice(10, 12)
        const d = idCardNo.slice(12, 14)
        birthDate = `${y}-${m}-${d}`
      }
    }

    // 查询最新月份的专项附加扣除明细
    const latestDeductionRecord = await (prisma as any).specialDeductionRecord.findFirst({
      where: { employeeId, orgId },
      orderBy: { month: 'desc' },
    })
    const latestDeduction = latestDeductionRecord ? {
      month: latestDeductionRecord.month,
      children: latestDeductionRecord.children || 0,
      elderly: latestDeductionRecord.elderly || 0,
      housing: latestDeductionRecord.housing || 0,
      education: latestDeductionRecord.education || 0,
      infant: latestDeductionRecord.infant || 0,
      remark: latestDeductionRecord.remark || '',
    } : null

    res.json({
      success: true,
      data: {
        name: emp.name,
        gender,
        phone: emp.phone || '',
        idCardNo,
        idType: emp.idType || 'ID_CARD',
        department: emp.department,
        position: emp.position || '',
        hireDate: emp.hireDate ? emp.hireDate.toISOString().slice(0, 10) : '',
        emergencyContact: emp.emergencyContact || '',
        emergencyPhone: emp.emergencyPhone || '',
        address: emp.address || '',
        bankAccount,
        bankName: emp.bankName || '',
        education: emp.education || '',
        city: emp.city || '',
        birthDate,
        employeeNo: emp.employeeNo || '',
        femaleWorkerType: emp.femaleWorkerType || '',
        specialDeduction: emp.specialDeduction ?? 0,
        specialDeductionDetail: latestDeduction,
      },
    })
  } catch (err) { next(err) }
})

// 更新个人资料（员工可编辑字段：紧急联系人、地址、银行卡、学历等）
router.put('/profile', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const emp = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
    if (!emp) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })

    const {
      emergencyContact, emergencyPhone, address,
      bankAccount, bankName, education, city,
      specialDeductionDetail,
    } = req.body

    // 构建更新数据（仅允许员工自行编辑的字段）
    const updateData: any = {}
    if (emergencyContact !== undefined) updateData.emergencyContact = emergencyContact || null
    if (emergencyPhone !== undefined) updateData.emergencyPhone = emergencyPhone || null
    if (address !== undefined) updateData.address = address || null
    if (bankName !== undefined) updateData.bankName = bankName || null
    if (education !== undefined) updateData.education = education || null
    if (city !== undefined) updateData.city = city || null
    // 银行卡号需要加密
    if (bankAccount !== undefined && bankAccount) {
      updateData.bankAccount = encrypt(bankAccount)
    }

    // 专项附加扣除明细保存到 SpecialDeductionRecord 表（当前月份）
    if (specialDeductionDetail && typeof specialDeductionDetail === 'object') {
      const now = new Date()
      const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`
      const { children, elderly, housing, education: dedEducation, infant, remark } = specialDeductionDetail
      const total = (Number(children) || 0) + (Number(elderly) || 0) + (Number(housing) || 0) + (Number(dedEducation) || 0) + (Number(infant) || 0)
      await (prisma as any).specialDeductionRecord.upsert({
        where: { employeeId_month: { employeeId, month: currentMonth } },
        create: {
          orgId, employeeId, month: currentMonth,
          amount: total,
          children: Number(children) || 0,
          elderly: Number(elderly) || 0,
          housing: Number(housing) || 0,
          education: Number(dedEducation) || 0,
          infant: Number(infant) || 0,
          remark: remark || null,
          createdBy: employeeId,
        },
        update: {
          amount: total,
          children: Number(children) || 0,
          elderly: Number(elderly) || 0,
          housing: Number(housing) || 0,
          education: Number(dedEducation) || 0,
          infant: Number(infant) || 0,
          remark: remark || null,
        },
      })
      // 同步便捷字段
      updateData.specialDeduction = total
    }

    await prisma.employee.update({ where: { id: employeeId }, data: updateData })

    // 记录证据链
    await createEvidence({
      orgId,
      category: 'PROFILE_UPDATE',
      refId: employeeId,
      employeeId,
      events: [{
        action: '员工更新个人资料',
        timestamp: new Date().toISOString(),
        ip: req.ip,
        userAgent: req.headers['user-agent'],
        location: `更新字段：${Object.keys(updateData).join(', ')}`,
      }],
      createdBy: employeeId,
    }).catch(() => {})

    res.json({ success: true, data: { message: '资料更新成功' } })
  } catch (err) { next(err) }
})

// ========== 员工端：附件管理 ==========
// 查看自己的附件列表
router.get('/attachments', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const attachments = await prisma.employeeAttachment.findMany({
      where: { employeeId, orgId },
      orderBy: { createdAt: 'desc' },
    })
    res.json({ success: true, data: attachments })
  } catch (err) { next(err) }
})

// 上传附件（文件URL由前端 FileReader 转为 base64 后传入）
router.post('/attachments', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const { fileName, fileType, fileUrl, fileSize } = req.body
    if (!fileName || !fileUrl) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '文件名和文件内容不能为空' } })
    }
    const attachment = await prisma.employeeAttachment.create({
      data: { orgId, employeeId, fileName, fileType: fileType || 'OTHER', fileUrl, fileSize: fileSize || 0, uploadedBy: req.employee.id },
    })
    res.json({ success: true, data: attachment })
  } catch (err) { next(err) }
})

// 删除自己的附件
router.delete('/attachments/:id', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const attachment = await prisma.employeeAttachment.findFirst({
      where: { id: req.params.id, employeeId, orgId },
    })
    if (!attachment) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '附件不存在' } })
    }
    await prisma.employeeAttachment.delete({ where: { id: attachment.id } })
    res.json({ success: true })
  } catch (err) { next(err) }
})
// 提交离职申请
router.post('/resignation/submit', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const { reason, expectedDate, remark, attachments } = req.body
    if (!reason || !expectedDate) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '请填写离职原因和预计离职日期' } })
    }
    const employee = await prisma.employee.findFirst({ where: { id: employeeId, orgId } })
    if (!employee) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '员工不存在' } })
    if (employee.status === 'RESIGNED' || employee.status === 'TERMINATED') {
      return res.status(400).json({ success: false, error: { code: 'ALREADY_RESIGNED', message: '您已离职，无法重复申请' } })
    }
    // 检查是否已有待审批的离职申请
    const existing = await (prisma as any).terminationRecord.findFirst({
      where: { employeeId, orgId, status: { in: ['DRAFT', 'PENDING_APPROVAL'] } },
    })
    if (existing) {
      return res.status(400).json({ success: false, error: { code: 'DUPLICATE', message: '您已有一个待处理的离职申请' } })
    }
    const remarkText = `员工自主申请：${reason}${remark ? '；备注：' + remark : ''}${attachments && attachments.length > 0 ? `；附件：${attachments.length}张辞职信照片` : ''}`
    const record = await (prisma as any).terminationRecord.create({
      data: {
        employeeId, orgId,
        type: 'RESIGNATION',
        reason: 'RESIGNATION',
        resignationReason: reason,
        terminationDate: new Date(expectedDate),
        status: 'PENDING_APPROVAL',
        checklist: attachments && attachments.length > 0 ? attachments : [],
        remark: remarkText,
        createdBy: employeeId,
      },
    })
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

// 查询自己的离职申请状态
router.get('/resignation/status', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const records = await (prisma as any).terminationRecord.findMany({
      where: { employeeId, orgId },
      orderBy: { createdAt: 'desc' },
      take: 5,
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

// 撤回离职申请（仅 DRAFT/PENDING_APPROVAL 可撤回）
router.post('/resignation/:id/withdraw', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await (prisma as any).terminationRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '离职申请不存在' } })
    if (record.status !== 'DRAFT' && record.status !== 'PENDING_APPROVAL') {
      return res.status(400).json({ success: false, error: { code: 'INVALID_STATUS', message: '当前状态无法撤回' } })
    }
    await (prisma as any).terminationRecord.update({
      where: { id: record.id },
      data: { status: 'CANCELLED' },
    })
    res.json({ success: true, data: { id: record.id, status: 'CANCELLED' } })
  } catch (err) { next(err) }
})

// 下载离职证明（仅已完成的离职记录）
router.get('/resignation/:id/certificate', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await (prisma as any).terminationRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId },
      include: { employee: true },
    })
    if (!record) return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '离职记录不存在' } })
    if (record.status !== 'COMPLETED') {
      return res.status(400).json({ success: false, error: { code: 'INVALID_STATUS', message: '离职流程未完成，无法下载证明' } })
    }

    const org = await prisma.organization.findFirst({ where: { id: orgId } })
    const orgName = org?.name || ''

    // 查找企业自定义的离职证明模板
    const tpl = await (prisma as any).enterpriseTemplate.findFirst({
      where: { orgId, category: 'LEAVING_CERT' },
    })

    const reasonLabel: Record<string, string> = {
      RESIGNATION: '个人辞职', EXPIRY: '合同到期', DISMISSAL: '违纪辞退',
      NEGOTIATED: '协商解除', RETIREMENT: '退休', DEATH: '死亡',
    }
    const reason = reasonLabel[record.reason] || record.reason || ''

    const variables: Record<string, string> = {
      employeeName: record.employee?.name || '',
      idCardNumber: record.employee?.idCardNumber || '',
      department: record.employee?.department || '',
      position: record.employee?.position || '',
      hireDate: record.employee?.hireDate ? new Date(record.employee.hireDate).toISOString().slice(0, 10) : '',
      leaveDate: record.terminationDate ? new Date(record.terminationDate).toISOString().slice(0, 10) : '',
      reason,
      companyName: orgName,
      compensation: String(record.compensation || 0),
      socialInsEndMonth: record.socialInsEndMonth || '',
      housingFundEndMonth: record.housingFundEndMonth || '',
    }

    let content: string
    if (tpl) {
      content = tpl.content
      for (const [key, value] of Object.entries(variables)) {
        content = content.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value)
      }
    } else {
      content = `<h1>解除/终止劳动合同证明书</h1>
<p>兹证明 ${variables.employeeName}（证件号码：${variables.idCardNumber}），原系我单位 ${variables.department} 部门员工，于 ${variables.leaveDate} 因 ${reason} 原因，正式解除/终止劳动合同。</p>
<p>经济补偿金已结清：¥${variables.compensation}。社保截止月份：${variables.socialInsEndMonth || '—'}，公积金截止月份：${variables.housingFundEndMonth || '—'}。</p>
<p>特此证明。</p>
<div class="sign">公司（盖章）<br/>${new Date().toISOString().slice(0, 10)}</div>`
    }

    const htmlContent = `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40">
<head><meta charset="utf-8"><title>离职证明</title>
<!--[if gte mso 9]><xml>
<w:WordDocument><w:View>Print</w:View><w:Zoom>100</w:Zoom><w:DoNotOptimizeForBrowser/></w:WordDocument>
</xml><![endif]-->
<style>
@page { size: A4; margin: 2.54cm 3.17cm 2.54cm 3.17cm; }
body { font-family: SimSun, serif; font-size: 14pt; line-height: 2; text-align: justify; }
h1 { font-size: 22pt; font-weight: bold; text-align: center; margin: 30pt 0 20pt 0; font-family: SimHei, sans-serif; }
p { text-indent: 2em; margin: 0 0 10pt 0; }
.sign { text-align: right; margin-top: 30pt; margin-right: 20pt; text-indent: 0; }
</style></head>
<body>${content}</body></html>`

    const encoded = encodeURIComponent(`离职证明-${variables.employeeName}.doc`)
    res.setHeader('Content-Type', 'application/msword; charset=utf-8')
    res.setHeader('Content-Disposition', `attachment; filename="${encoded}"; filename*=UTF-8''${encoded}`)
    res.send(htmlContent)
  } catch (err) { next(err) }
})

// ========== 员工端：休假申请 ==========
// 查看自己的休假申请列表
router.get('/leaves', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const list = await prisma.leaveRequest.findMany({
      where: { employeeId, orgId },
      orderBy: { createdAt: 'desc' },
    })
    res.json({ success: true, data: list })
  } catch (err) { next(err) }
})

// 提交休假申请
router.post('/leaves', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const { leaveType, startDate, endDate, days, reason } = req.body

    if (!leaveType || !startDate || !endDate) {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '缺少必填字段' } })
    }

    const record = await prisma.leaveRequest.create({
      data: {
        orgId,
        employeeId,
        leaveType,
        startDate: new Date(startDate),
        endDate: new Date(endDate),
        days: days || 1,
        reason: reason || null,
        createdBy: employeeId,
      },
    })
    res.json({ success: true, data: record })
  } catch (err) { next(err) }
})

// 撤回休假申请（仅待审批可撤回）
router.post('/leaves/:id/cancel', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await prisma.leaveRequest.findFirst({
      where: { id: req.params.id, employeeId, orgId },
    })
    if (!record) {
      return res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: '申请不存在' } })
    }
    if (record.status !== 'PENDING') {
      return res.status(400).json({ success: false, error: { code: 'BAD_REQUEST', message: '已处理的申请不可撤回' } })
    }
    const updated = await prisma.leaveRequest.update({
      where: { id: record.id },
      data: { status: 'CANCELLED' },
    })
    res.json({ success: true, data: updated })
  } catch (err) { next(err) }
})

// ========== 员工端：电子签署 ==========
// 查看自己的签署记录列表
// ========== 员工端：电子签署（验证码确认 + 证据链） ==========

/**
 * 我的电子签署列表
 * - 自动将过期的 PENDING 记录标记为 EXPIRED
 */
router.get('/esign', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const records = await prisma.eSignRecord.findMany({
      where: { employeeId, orgId },
      orderBy: { createdAt: 'desc' },
    })

    // 自动处理过期：PENDING 且已超过 expiredAt 的记录标记为 EXPIRED
    const now = new Date()
    const expiredIds = records
      .filter(r => r.status === 'PENDING' && r.expiredAt && r.expiredAt < now)
      .map(r => r.id)
    if (expiredIds.length > 0) {
      await prisma.eSignRecord.updateMany({
        where: { id: { in: expiredIds } },
        data: { status: 'EXPIRED' },
      })
      expiredIds.forEach(id => {
        const r = records.find(rec => rec.id === id)
        if (r) r.status = 'EXPIRED'
      })
    }

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

/**
 * 查看签署详情（含文件内容）
 */
router.get('/esign/:id', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await prisma.eSignRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId },
    })
    if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在' } })

    // 检查过期
    if (record.status === 'PENDING' && record.expiredAt && record.expiredAt < new Date()) {
      const updated = await prisma.eSignRecord.update({
        where: { id: record.id },
        data: { status: 'EXPIRED' },
      })
      return res.json({ success: true, data: updated })
    }

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

/**
 * 发送签署验证码
 * - 通过手机号发送6位验证码
 * - 验证码5分钟有效，最多5次错误
 */
router.post('/esign/:id/send-code', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await prisma.eSignRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId, status: 'PENDING' },
      include: { employee: { select: { phone: true, name: true } } },
    })
    if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已处理' } })
    if (!record.employee.phone) {
      return res.status(400).json({ success: false, error: { code: 'NO_PHONE', message: '未登记手机号，无法发送验证码' } })
    }

    // 限流：同一记录60秒内只能发一次
    const rateLimitKey = `esign-rate-${record.id}`
    const allowed = await checkRateLimit(rateLimitKey, 60 * 1000)
    if (!allowed) {
      return res.status(429).json({ success: false, error: { code: 'RATE_LIMIT', message: '请求过于频繁，请60秒后重试' } })
    }

    const code = Math.random().toString().slice(2, 8)
    await setCode(`esign-${record.id}`, code)

    // 追加证据链 — 验证码发送事件
    const evidence = await prisma.evidenceChain.findFirst({
      where: { orgId, refId: record.id, category: 'CONTRACT_SIGN' },
    })
    if (evidence) {
      await appendEvidence(orgId, evidence.id, {
        action: '发送签署验证码',
        timestamp: new Date().toISOString(),
        ip: req.ip,
        userAgent: req.headers['user-agent'] as string,
        location: `手机号：${record.employee.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')}`,
      }).catch(() => {})
    }

    res.json({
      success: true,
      data: {
        code, // 开发阶段直接返回，生产环境通过短信发送
        message: '验证码已生成（开发阶段直接返回，生产环境将发送短信）',
        phone: record.employee.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'),
      },
    })
  } catch (err) { next(err) }
})

/**
 * 员工签署操作（验证码确认 + 证据链 + 回写合同）
 * - 校验验证码
 * - 更新签署状态为 COMPLETED
 * - 追加证据链（签署完成事件，含IP/UA/验证码/时间戳）
 * - 回写合同 signMethod 和 electronicContractUrl
 */
router.post('/esign/:id/sign', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const { verifyCode } = req.body || {}

    const record = await prisma.eSignRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId, status: 'PENDING' },
      include: { employee: { select: { name: true, phone: true } } },
    })
    if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已处理' } })

    // 检查过期
    if (record.expiredAt && record.expiredAt < new Date()) {
      await prisma.eSignRecord.update({ where: { id: record.id }, data: { status: 'EXPIRED' } })
      return res.status(400).json({ success: false, error: { code: 'EXPIRED', message: '签署链接已过期' } })
    }

    // 校验验证码
    if (!verifyCode) {
      return res.status(400).json({ success: false, error: { code: 'NO_CODE', message: '请输入验证码' } })
    }
    const stored = await getCode(`esign-${record.id}`)
    if (!stored) {
      return res.status(400).json({ success: false, error: { code: 'CODE_EXPIRED', message: '验证码已过期，请重新获取' } })
    }
    if (stored.failCount >= 5) {
      await deleteCode(`esign-${record.id}`)
      return res.status(400).json({ success: false, error: { code: 'TOO_MANY_ATTEMPTS', message: '验证码错误次数过多，请重新获取验证码' } })
    }
    if (stored.code !== verifyCode) {
      await updateCode(`esign-${record.id}`, { failCount: stored.failCount + 1 })
      return res.status(400).json({ success: false, error: { code: 'CODE_WRONG', message: `验证码错误（剩余${5 - stored.failCount - 1}次机会）` } })
    }
    await deleteCode(`esign-${record.id}`)

    // 签署完成
    const userAgent = req.headers['user-agent'] || ''
    const signEvidence = JSON.stringify({
      ip: req.ip,
      userAgent,
      timestamp: new Date().toISOString(),
      verifyCode: true,
      employeeName: record.employee.name,
      phone: record.employee.phone,
    })

    const updated = await prisma.eSignRecord.update({
      where: { id: record.id },
      data: {
        status: 'COMPLETED',
        completedAt: new Date(),
        // 保存签署证据到 callbackData（作为签署凭证）
        callbackData: { signEvidence, signedAt: new Date().toISOString() } as any,
      },
    })

    // 回写合同
    if (record.contractId) {
      await prisma.laborContract.update({
        where: { id: record.contractId },
        data: {
          signMethod: 'ELECTRONIC',
          // 签署证据写入 attachmentName
          attachmentName: `e-sign:${new Date().toISOString()}|evidence:${signEvidence}`,
        },
      })
    }

    // 追加证据链 — 签署完成事件
    const evidence = await prisma.evidenceChain.findFirst({
      where: { orgId, refId: record.id, category: 'CONTRACT_SIGN' },
    })
    if (evidence) {
      await appendEvidence(orgId, evidence.id, {
        action: `员工签署完成：${record.documentTitle}`,
        timestamp: new Date().toISOString(),
        ip: req.ip,
        userAgent,
        smsCode: verifyCode,
        location: `签署人：${record.employee.name}，验证码已验证`,
      }).catch(() => {})
    } else {
      // 证据链不存在时创建新的
      await createEvidence({
        orgId,
        category: 'CONTRACT_SIGN',
        refId: record.id,
        employeeId,
        events: [{
          action: `员工签署完成：${record.documentTitle}`,
          timestamp: new Date().toISOString(),
          ip: req.ip,
          userAgent,
          smsCode: verifyCode,
          location: `签署人：${record.employee.name}，验证码已验证`,
        }],
        createdBy: employeeId,
      }).catch(() => {})
    }

    res.json({ success: true, data: updated, message: '签署成功' })
  } catch (err) { next(err) }
})

// ========== 员工端：培训签收 ==========
// 查看自己的培训记录
router.get('/training', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const records = await prisma.trainingRecord.findMany({
      where: { employeeId, orgId },
      orderBy: { trainingDate: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

// 培训签收
router.post('/training/:id/sign', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await prisma.trainingRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId, ackStatus: 'PENDING' },
    })
    if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已签收' } })

    const updated = await prisma.trainingRecord.update({
      where: { id: record.id },
      data: { ackStatus: 'SIGNED', ackDate: new Date() },
    })

    await createEvidence({
      orgId,
      category: 'TRAINING',
      refId: record.id,
      employeeId,
      events: [{ action: `培训签收：${record.topic}`, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
      createdBy: employeeId,
    }).catch(() => {})

    res.json({ success: true, data: updated, message: '签收成功' })
  } catch (err) { next(err) }
})

// 培训拒绝签收
router.post('/training/:id/refuse', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await prisma.trainingRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId, ackStatus: 'PENDING' },
    })
    if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已处理' } })

    const updated = await prisma.trainingRecord.update({
      where: { id: record.id },
      data: { ackStatus: 'REFUSED', ackDate: new Date() },
    })

    res.json({ success: true, data: updated, message: '已拒绝签收' })
  } catch (err) { next(err) }
})

// ========== 员工端：绩效签字 ==========
// 查看自己的绩效记录
router.get('/performance', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const records = await prisma.performanceRecord.findMany({
      where: { employeeId, orgId },
      orderBy: { period: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

// 绩效签字确认
router.post('/performance/:id/sign', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await prisma.performanceRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId, employeeAck: false },
    })
    if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已签字' } })

    const updated = await prisma.performanceRecord.update({
      where: { id: record.id },
      data: { employeeAck: true, ackDate: new Date() },
    })

    await createEvidence({
      orgId,
      category: 'PERFORMANCE',
      refId: record.id,
      employeeId,
      events: [{ action: `绩效签字确认：${record.period}`, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
      createdBy: employeeId,
    }).catch(() => {})

    res.json({ success: true, data: updated, message: '签字成功' })
  } catch (err) { next(err) }
})

// ========== 员工端：违纪签字 ==========
// 查看自己的违纪记录
router.get('/disciplinary', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const records = await prisma.disciplinaryRecord.findMany({
      where: { employeeId, orgId },
      orderBy: { violationDate: 'desc' },
    })
    res.json({ success: true, data: records })
  } catch (err) { next(err) }
})

// 违纪签字确认
router.post('/disciplinary/:id/sign', portalAuth, async (req: any, res, next) => {
  try {
    const { id: employeeId, orgId } = req.employee
    const record = await prisma.disciplinaryRecord.findFirst({
      where: { id: req.params.id, employeeId, orgId, employeeAck: false },
    })
    if (!record) return res.status(404).json({ success: false, error: { message: '记录不存在或已签字' } })

    const updated = await prisma.disciplinaryRecord.update({
      where: { id: record.id },
      data: { employeeAck: true, ackDate: new Date(), ackMethod: 'SIGN' },
    })

    await createEvidence({
      orgId,
      category: 'DISCIPLINARY',
      refId: record.id,
      employeeId,
      events: [{ action: `违纪签字确认：${record.violationType}`, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'] }],
      createdBy: employeeId,
    }).catch(() => {})

    res.json({ success: true, data: updated, message: '签字成功' })
  } catch (err) { next(err) }
})

export default router
