import type { Request } from 'express';
import { parseUserAgent, type ParsedUA } from './ua-parser.util';

export interface RequestMeta extends ParsedUA {
  ip_address: string | null;
  user_agent: string | null;
}

/**
 * Pull the *real* client IP + parsed device info out of an Express request.
 *
 * IMPORTANT about IP behind a proxy (nginx / cloudflare / load balancer):
 *   In your NestJS bootstrap set:  app.set('trust proxy', 1);
 *   Then req.ip already respects X-Forwarded-For. We still read the header
 *   manually as a fallback so this works even if trust proxy is not set.
 */
export function getRequestMeta(req: Request): RequestMeta {
  const ua =
    (req.headers['user-agent'] as string | undefined)?.slice(0, 1024) ?? null;

  const parsed = parseUserAgent(ua);

  return {
    ip_address: getClientIp(req),
    user_agent: ua,
    ...parsed,
  };
}

export function getClientIp(req: Request): string | null {
  // 1) X-Forwarded-For: "client, proxy1, proxy2" -> take the first, left-most
  const xff = req.headers['x-forwarded-for'];
  if (xff) {
    const first = (Array.isArray(xff) ? xff[0] : xff).split(',')[0].trim();
    if (first) return normalizeIp(first);
  }

  // 2) Other common proxy headers
  const realIp = req.headers['x-real-ip'];
  if (typeof realIp === 'string' && realIp.trim()) {
    return normalizeIp(realIp.trim());
  }
  const cf = req.headers['cf-connecting-ip'];
  if (typeof cf === 'string' && cf.trim()) {
    return normalizeIp(cf.trim());
  }

  // 3) Express-resolved IP (respects trust proxy if configured)
  if (req.ip) return normalizeIp(req.ip);

  // 4) Raw socket
  const sock = (req.socket && req.socket.remoteAddress) || null;
  return sock ? normalizeIp(sock) : null;
}

/** Turns IPv4-mapped IPv6 (::ffff:1.2.3.4) back into plain IPv4, and caps length. */
function normalizeIp(ip: string): string {
  const cleaned = ip.replace(/^::ffff:/i, '');
  return cleaned.slice(0, 45);
}
