const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;

function sanitizeHeaderValue(value: string, maxLength: number) {
  return value.replace(/[\u0000-\u001f\u007f]/g, '').trim().slice(0, maxLength) || 'unknown';
}

export function getClientAddress(headers: Headers) {
  const forwarded = headers.get('x-forwarded-for');
  if (forwarded) {
    const addresses = forwarded
      .split(',')
      .map((value) => value.trim())
      .filter(Boolean);

    if (addresses.length > 0) {
      // Use the nearest address. This avoids trusting a spoofed left-most value
      // when Next or a reverse proxy appends the real peer address.
      return sanitizeHeaderValue(addresses[addresses.length - 1], 128);
    }
  }

  const realIp = headers.get('x-real-ip');
  if (realIp) return sanitizeHeaderValue(realIp, 128);

  return 'unknown';
}

export function getUserAgent(headers: Headers) {
  return sanitizeHeaderValue(headers.get('user-agent') || 'unknown', 256);
}

export function normalizeVisitPath(value: unknown) {
  if (typeof value !== 'string') return null;

  const input = value.trim();
  if (
    input.length === 0 ||
    input.length > 2048 ||
    !input.startsWith('/') ||
    input.startsWith('//') ||
    CONTROL_CHARS.test(input)
  ) {
    return null;
  }

  try {
    const parsed = new URL(input, 'https://site.local');
    if (parsed.origin !== 'https://site.local') return null;
    return `${parsed.pathname}${parsed.search}`;
  } catch {
    return null;
  }
}

export function isSameOriginRequest(request: Request) {
  const host = request.headers.get('host');
  if (!host) return false;
  const requestHost = host.toLowerCase();

  const origin = request.headers.get('origin');
  if (origin) {
    try {
      return new URL(origin).host.toLowerCase() === requestHost;
    } catch {
      return false;
    }
  }

  const referer = request.headers.get('referer');
  if (referer) {
    try {
      return new URL(referer).host.toLowerCase() === requestHost;
    } catch {
      return false;
    }
  }

  const fetchSite = request.headers.get('sec-fetch-site');
  return fetchSite === 'same-origin' || fetchSite === 'none';
}
