import type { Request } from 'express';

/**
 * 从 HTTP 请求中提取客户端 IP。
 *
 * 优先级：
 *  1. `X-Forwarded-For` 列表的**首项**（最靠近客户端的代理写入；逗号分隔字符串
 *     `"1.2.3.4, 5.6.7.8"` 取 `1.2.3.4`，避免把整串塞 audit）
 *  2. `X-Real-IP`（单值，常见 nginx 反代写入）
 *  3. `socket.remoteAddress`（直接 TCP 连接对端）
 *  4. `req.ip`（Express 在 trust proxy 启用时解析的结果）
 *  5. `'unknown'` 兜底
 *
 * 说明：所有审计 / 限流 / SSO 等需要客户端 IP 的场景都应该走本 helper，
 * 避免每处自写逻辑导致格式不一致（XFF 列表整串落库 / 取错项等）。
 *
 * ⚠️ **安全约束**：X-Forwarded-For / X-Real-IP 是 HTTP 头，**客户端可任意伪造**。
 * 本 helper 无条件信任首项的前提是「生产链路始终位于可信反代后方」（Caddy /
 * Nginx / Cloudflare 等会覆盖/追加 XFF）。
 *
 * 如果未来某个服务被部署成**直接暴露**（容器端口直 expose，无反代）→ 攻击者可
 * 在 SSO audit / IP 限流 / 安全日志里污染任意 IP。新部署架构变更时必须 review
 * 本 helper 的所有调用方，决策是改造为「按部署配置切换信任策略」还是限制部署
 * 拓扑必须经反代。
 */
export function getClientIp(req: Request): string {
  const forwarded = req.headers['x-forwarded-for'];
  if (typeof forwarded === 'string' && forwarded.length > 0) {
    const first = forwarded.split(',')[0];
    if (first) {
      const trimmed = first.trim();
      if (trimmed) return trimmed;
    }
  }
  const realIp = req.headers['x-real-ip'];
  if (typeof realIp === 'string' && realIp.length > 0) return realIp;
  return req.socket?.remoteAddress || req.ip || 'unknown';
}
