import { isPrivateOrBlockedHost } from './ssrfGuard.js';

/**
 * Zentrale Validierung für nach außen geleitete URLs (Portal-Links,
 * Anbieter-Portale, Mail-Footer). Konsolidiert die Schema-/Host-Checks,
 * die bisher pro Feld einzeln (und uneinheitlich) verstreut waren:
 * - `appSetting.portalLoginUrl` hatte einen vollen Check
 * - `provider.portalUrl` hatte gar keinen → Stored XSS via
 *   `javascript:alert(...)` (Pentest 46.1 HIGH)
 * - andere Felder strippten nur `<script>`-Tags
 *
 * Regelwerk:
 * - Leer/null → OK (Feld ist optional, keine Validierung)
 * - Schema MUSS http oder https sein (keine `javascript:`,
 *   `data:`, `file:`, `vbscript:` …)
 * - Host muss vorhanden sein
 * - Bei `allowPrivateHosts=false` (Default): Private/Loopback-IPs
 *   und Cloud-Metadata-Adressen sind gesperrt, weil die URL für
 *   Endkunden gedacht ist und 10.x/192.168.x für die ohnehin
 *   nicht erreichbar wären
 * - Trailing-Slash wird gestrippt (Komfort beim Speichern)
 */
export function validateHttpUrl(
  rawValue: string,
  opts: { fieldLabel?: string; allowPrivateHosts?: boolean } = {},
): { ok: true; value: string } | { ok: false; error: string } {
  const label = opts.fieldLabel ?? 'URL';
  const trimmed = rawValue.trim().replace(/\/+$/, '');
  if (trimmed === '') return { ok: true, value: '' };

  let parsed: URL;
  try {
    parsed = new URL(trimmed);
  } catch {
    return { ok: false, error: `${label} muss eine absolute http(s)-URL sein.` };
  }
  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
    return { ok: false, error: `${label}: unzulässiges Schema '${parsed.protocol}'. Nur http(s) erlaubt.` };
  }
  if (!parsed.hostname) {
    return { ok: false, error: `${label}: Host fehlt.` };
  }

  if (!opts.allowPrivateHosts) {
    // Node's URL-Parser lässt eckige Klammern im hostname für IPv6
    // (`http://[::1]` → hostname `"[::1]"`). Klammern strippen, sonst
    // matcht der Loopback-Pattern `^::1$` nicht.
    const hostForCheck = parsed.hostname.replace(/^\[|\]$/g, '');
    if (isPrivateOrBlockedHost(hostForCheck)) {
      return {
        ok: false,
        error: `${label}: Host '${hostForCheck}' ist gesperrt (interne, private oder Loopback-Adresse). Bitte öffentlich erreichbare Domain verwenden.`,
      };
    }
  }

  return { ok: true, value: trimmed };
}
