import crypto from 'crypto'

const cache = globalThis.__HTC_HIBP_CACHE__ || new Map()
globalThis.__HTC_HIBP_CACHE__ = cache

function nowMs() {
  return Date.now()
}

function sha1UpperHex(input) {
  return crypto.createHash('sha1').update(String(input), 'utf8').digest('hex').toUpperCase()
}

function parseRangeResponse(text) {
  // Format: "SUFFIX:COUNT" per line
  const map = new Map()
  for (const line of String(text || '').split('\n')) {
    const trimmed = line.trim()
    if (!trimmed) continue
    const [suffix, count] = trimmed.split(':')
    if (suffix && count) map.set(suffix.trim().toUpperCase(), Number(count.trim()) || 0)
  }
  return map
}

async function fetchWithTimeout(url, { timeoutMs = 4000, headers = {} } = {}) {
  const ctrl = new AbortController()
  const t = setTimeout(() => ctrl.abort(), timeoutMs)
  try {
    return await fetch(url, { headers, signal: ctrl.signal })
  } finally {
    clearTimeout(t)
  }
}

/**
 * Prüft Passwort gegen HIBP Pwned Passwords (k-Anonymity).
 * Gibt zurück: { pwned: boolean, count: number }
 */
export async function checkPasswordPwned(password) {
  const enabled = (process.env.HIBP_ENABLED || '').toLowerCase() === 'true'
  if (!enabled) return { pwned: false, count: 0 }

  const hash = sha1UpperHex(password)
  const prefix = hash.slice(0, 5)
  const suffix = hash.slice(5)

  // Cache pro Prefix (TTL)
  const ttlMs = Number(process.env.HIBP_CACHE_TTL_MS || 6 * 60 * 60 * 1000) // 6h
  const cached = cache.get(prefix)
  const now = nowMs()
  if (cached && cached.expiresAt > now && cached.map) {
    const count = cached.map.get(suffix) || 0
    return { pwned: count > 0, count }
  }

  const ua = process.env.HIBP_USER_AGENT || 'harheimertc'
  const url = `https://api.pwnedpasswords.com/range/${prefix}`
  const res = await fetchWithTimeout(url, {
    timeoutMs: Number(process.env.HIBP_TIMEOUT_MS || 4000),
    headers: {
      'User-Agent': ua,
      // HIBP empfiehlt optional diesen Header für Padding; wir schalten ihn per default ein.
      'Add-Padding': 'true'
    }
  })

  if (!res.ok) {
    const failClosed = (process.env.HIBP_FAIL_CLOSED || '').toLowerCase() === 'true'
    if (failClosed) {
      throw createError({ statusCode: 503, statusMessage: 'Passwortprüfung derzeit nicht verfügbar. Bitte später erneut versuchen.' })
    }
    // fail-open
    return { pwned: false, count: 0 }
  }

  const text = await res.text()
  const map = parseRangeResponse(text)
  cache.set(prefix, { expiresAt: now + ttlMs, map })

  const count = map.get(suffix) || 0
  return { pwned: count > 0, count }
}

export async function assertPasswordNotPwned(password) {
  const { pwned } = await checkPasswordPwned(password)
  if (pwned) {
    throw createError({
      statusCode: 400,
      message: 'Dieses Passwort wurde in bekannten Datenleaks gefunden. Bitte wählen Sie ein anderes Passwort.'
    })
  }
}


