// ==================== PASSWORD GENERATOR ====================
// Generiert sichere, zufällige Passwörter

import { randomBytes } from 'crypto';

// Zeichensätze für Passwort-Generierung
const LOWERCASE = 'abcdefghijklmnopqrstuvwxyz';
const UPPERCASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const NUMBERS = '0123456789';
const SPECIAL = '!@#$%^&*()_+-=[]{}|;:,.<>?';

// Standard-Passwortlänge
const DEFAULT_LENGTH = 16;

export interface PasswordOptions {
  length?: number;
  includeLowercase?: boolean;
  includeUppercase?: boolean;
  includeNumbers?: boolean;
  includeSpecial?: boolean;
}

/**
 * Generiert ein kryptografisch sicheres Passwort
 */
export function generateSecurePassword(options: PasswordOptions = {}): string {
  const {
    length = DEFAULT_LENGTH,
    includeLowercase = true,
    includeUppercase = true,
    includeNumbers = true,
    includeSpecial = true,
  } = options;

  // Zeichensatz zusammenstellen
  let charset = '';
  const requiredChars: string[] = [];

  if (includeLowercase) {
    charset += LOWERCASE;
    requiredChars.push(getRandomChar(LOWERCASE));
  }
  if (includeUppercase) {
    charset += UPPERCASE;
    requiredChars.push(getRandomChar(UPPERCASE));
  }
  if (includeNumbers) {
    charset += NUMBERS;
    requiredChars.push(getRandomChar(NUMBERS));
  }
  if (includeSpecial) {
    charset += SPECIAL;
    requiredChars.push(getRandomChar(SPECIAL));
  }

  if (charset.length === 0) {
    throw new Error('Mindestens ein Zeichensatz muss aktiviert sein');
  }

  // Restliche Zeichen auffüllen
  const remainingLength = Math.max(0, length - requiredChars.length);
  const randomChars: string[] = [];

  for (let i = 0; i < remainingLength; i++) {
    randomChars.push(getRandomChar(charset));
  }

  // Alle Zeichen mischen (Fisher-Yates Shuffle)
  const allChars = [...requiredChars, ...randomChars];
  for (let i = allChars.length - 1; i > 0; i--) {
    const j = getRandomInt(i + 1);
    [allChars[i], allChars[j]] = [allChars[j], allChars[i]];
  }

  return allChars.join('');
}

/**
 * Generiert ein einfaches Passwort ohne Sonderzeichen (für APIs die das nicht mögen)
 */
export function generateSimplePassword(length = 12): string {
  return generateSecurePassword({
    length,
    includeLowercase: true,
    includeUppercase: true,
    includeNumbers: true,
    includeSpecial: false,
  });
}

// ==================== PASSWORD COMPLEXITY VALIDATION ====================

/**
 * Mindestanforderungen für vom User vergebene Passwörter.
 * Generator-Output (generateSecurePassword) erfüllt diese standardmäßig.
 */
export interface PasswordComplexityResult {
  ok: boolean;
  errors: string[];
}

// Mindestlängen nach Kontext (Pentest Runde 13 / 2026-05-18):
// Endkunden tippen ihr Portal-Passwort auch auf dem Handy ein – 12 ist hier
// der Endkunden-Floor. Mitarbeiter/Admin nutzen Passwort-Manager → 25
// Zeichen entsprechen der aktuellen BSI-Empfehlung für lange Passphrasen
// mit Komplexität.
export const PORTAL_MIN_PASSWORD_LENGTH = 12;
export const STAFF_MIN_PASSWORD_LENGTH = 25;

export function validatePasswordComplexity(
  pw: unknown,
  opts: { minLength?: number } = {},
): PasswordComplexityResult {
  const minLength = opts.minLength ?? PORTAL_MIN_PASSWORD_LENGTH;
  const errors: string[] = [];
  if (typeof pw !== 'string') {
    return { ok: false, errors: ['Passwort fehlt oder ist kein Text'] };
  }
  if (pw.length < minLength) errors.push(`mindestens ${minLength} Zeichen`);
  if (!/[a-z]/.test(pw)) errors.push('mindestens einen Kleinbuchstaben');
  if (!/[A-Z]/.test(pw)) errors.push('mindestens einen Großbuchstaben');
  if (!/[0-9]/.test(pw)) errors.push('mindestens eine Ziffer');
  // Sonderzeichen-Set bewusst breit – auch Leerzeichen + Unicode-Punktuation
  // zulassen, damit gängige Passwort-Manager-Outputs nicht abgelehnt werden.
  if (!/[^A-Za-z0-9]/.test(pw)) errors.push('mindestens ein Sonderzeichen');
  return { ok: errors.length === 0, errors };
}

/**
 * Wirft mit sprechender Fehlermeldung, wenn das Passwort die Komplexität
 * nicht erfüllt. Für Aufruf direkt im Controller, der die Exception fängt.
 */
export function assertPasswordComplexity(pw: unknown, opts: { minLength?: number } = {}): void {
  const r = validatePasswordComplexity(pw, opts);
  if (!r.ok) {
    throw new Error('Passwort erfüllt Mindestanforderungen nicht: ' + r.errors.join(', '));
  }
}

// Kryptografisch sichere Zufallszahl
function getRandomInt(max: number): number {
  const bytes = randomBytes(4);
  const value = bytes.readUInt32BE(0);
  return value % max;
}

// Zufälliges Zeichen aus einem Zeichensatz
function getRandomChar(charset: string): string {
  return charset[getRandomInt(charset.length)];
}
